Simple Django Tip #4

Photo by Anton on Unsplash

Simple Django Tip #4

Points to remember while designing a model

ยท

4 min read

So far my tips were more about the environment and setting up the stage in the right way.

Let's look at a few key points to remember while defining a model in models.py.

I plan to write a couple of posts regarding model design, rather than one single long post.

Models in Django

A model in the Django ORM world is the equivalent of a table in the database. That's the beauty of ORMs. We can define the complete table structure in the application itself, create, modify, query and do every single thing that we do using SQLs but without them.

๐Ÿ’ก
A word of caution though, Do give a lot of thought while defining a model, the relationships, the validations, and the audit columns it may need upfront. It gets quite tricky to keep applying changes to the model once data starts to load.

Let's jump in straight ๐Ÿ˜€. To not repeat myself I'd urge you to refer to my previous post to perform the usual steps of creating a Django project and other steps.

I'm a big fan of popsicles and soft cones ๐Ÿฆ๐Ÿ˜‹. Just to give the post a bit of flavor, I decided to take an example of defining a few models for a hypothetical website that sells and delivers ice cream.

A few models for our ice cream website would be:

  • Customer

  • Icecream

  • Order

Now for a few tips to keep in mind while we define a model. We shall apply them in the end to these models to see what it looks like.

Tips to remember while defining a model

Use descriptive field names

This may seem like an age-old tip. But it is forgotten many a time ๐Ÿ˜‚. Do not worry about the name of a field being long as long as it is self-explanatory.

# WRONG
class Customer(models.Model):
    fname = models.CharField(...)
    lname = models.CharField(...)

# RIGHT
class Customer(models.Model):
    first_name = models.CharField(...)
    last_name = models.CharField(...)

Tables being related is extremely common in any database. In Django, while we use ForeignKey as field type, make sure to give an understandable value for related_name.

If not followed from the beginning, it becomes unnecessarily complicated when the number of models starts to grow. I generally use the approach of noun + verb combination. Feel free to use whatever suits your naming standards but make sure it's descriptive. Let's see an example.

# WRONG
class Order(models.Model):
   ordered_by = models.ForeignKey(
        Customer, on_delete=models.CASCADE, related_name="ordered_by")

# RIGHT
class Order(models.Model):
   ordered_by = models.ForeignKey(
        Customer, on_delete=models.CASCADE, related_name="customer_who_ordered")

Use choices whenever there is a list of values

When a field in a model is designed to have a limited set of choices (think of a dropdown as its corresponding widget in the UI), it's always a good idea to define choices for the field.

It makes the code more readable and easy to maintain when there is a need to modify the values for some business reason. Validation of the field is also quite straightforward when defined as a choice field.

Where to place the choices field is debatable ๐Ÿ˜€.

If there are a lot of such list values in your business scenario or if a particular type of value will appear in multiple tables/classes (example: gender field) then a probable case may be to have a separate choices.py so that a particular choice field can be reused.

Otherwise, it's a good idea to just define it as the first thing in your class.

# WRONG
class Icecream(models.Model):
    flavor = models.CharField("Choose your flavor", max_length=500, \
             blank=False, null=False)


# RIGHT
class Icecream(models.Model):
    flavor_choices = (
        ("butterscotch", "Butterscotch"),
        ("carmel", "Carmel"),
        ("pista", "Pista"),
        ("strawberry", "Strawberry"),
        ("vanilla", "Vanilla")
    )
    flavor = models.CharField("Choose your flavor", choices=flavor_choices,
          default="vanilla")

Use indexes for frequently queried fields

There are a few fields that will be used in most of the queries, especially the unique fields in a table. For such cases, it helps by defining them as indexes as part of Meta definition.

๐Ÿ’ก
One should be judicious though while defining indexes on fields as it may slow down write operations.

Let's assume in our ice cream shop website, email is the unique identifier for a customer. Hence all queries that fetch customer details, dashboard-related ones especially will use email as part of the where clause. In such a case, we should define the index on the email column.

# WRONG
class Customer(models.Model):
    first_name = models.CharField("First name", ...)
    last_name = models.CharField("Last name", ...)
    email = models.EmailField("Email address", ...)

# RIGHT
class Customer(models.Model):
    first_name = models.CharField("First name", ...)
    last_name = models.CharField("Last name", ...)
    email = models.EmailField("Email address", ...)
    class Meta:
        indexes = [
            models.Index(fields=["email"]),
        ]
๐Ÿ’ก
db_index will be deprecated in the future. Hence the above definition is considered good practice

Will be continued

As I mentioned at the beginning of the post, I shall write a couple of posts in the immediate future concerning models as it is the backbone of any Django application.

Hope you found them useful!

ย