a blog about Django & Web Development

4 Types of Model Methods Django Beginners Should Know About

Supercharge your model classes by adding methods.
Django Models

Django models are not just for defining what columns will you into your database table. As well as setting fields as attributes of the class, you can also define methods.

This post will go through 4 kinds of methods you can use on your Django models and when to use them. These are:

  1. Properties
  2. Overriding parent methods
  3. Custom methods
  4. Dunder methods

If you’re new to Django and want to build a better understanding of how models work, I recommend reading my beginner’s guide to Django models first.

Adding methods to your models allow you to add custom functionality to your model and keep your code DRY (Don’t Repeat Yourself). All the examples in this post are for a model called Post , which is part of a blogging application.

You can find the full code for the post in this GitHub repository.

1. Properties

Model properties are similar to fields except they are not stored in the database. They are used when the value can be calculated from other fields.

For example, you are building a blog and you want to add an excerpt for each post. Let’s say in this case, you just want the first 150 characters of the post body.

A property is the best option here because the excerpt is calculated from the post body. Storing the excerpt in the database would be very inefficient because it would mean storing data twice.

The code defined in properties is ad-hoc. This means the code in def excerpt(self) is only run when post.excerpt is called.

Properties are defined by adding the @property decorator above the method. Adding the decorator means you can access the property just like any other field on the model like this” post.excerpt. You can leave out the decorator and still get the same result but you would have to add parentheses to access the excerpt like so: post.excerpt().

@property
def excerpt(self):
    character_limit = 150
    if len(self.body) < character_limit:
        excerpt = self.body
    else:
        excerpt = self.body[:character_limit] + "..."

    return excerpt

What is self?

Self refers to a single instance of a class. If the class is called Post, then self refers to an individual post.

This is an example of object-orientated programming and is not specific to Django models- it is very common style of coding amongst Python programmers.

If you see a method belonging to a class where the first argument is self, it means the method is designed to be applied to a single instance of the class.

post = Post(title="abc", body="abc")
result = post.do_something()

If the only argument is self, you don’t need to provide any arguments when calling the function. The value of self will be the object the method was called from.

2. Override parent methods

Models work by inheriting from django.db.models.Model. This base class contains a number of methods that provide the functionality for Django to detect models and propagate their properties to the database.

Sometimes, we may want to tweak those methods.

In my blog application, I have a field called updated_date. Whenever the post is updated, I want updated_date to store the date and time the change was made.

As I want to change the value updated_date every time a post is saved, I can override the save() method.

However, I don’t want to take any functionality away from the base class’s original save() method. I found the method in the source code and it’s 70 lines long, so copying and pasting the contents of the method isn’t a good idea. You could later update Django and have to copy the code all over again because the new version changed the code.

The best practice when overriding methods is to add your custom code is to use super(). Instead of copying 70 lines of code from the parent class, I’ve added one line of code and it does exactly the same thing. Much neater!

What super().save() does is call the save() method of the parent class.

Now, every time I update a post, the date and time of the update is saved to the database.

def save(self, **kwargs):
    self.updated_date = datetime.now()
    super().save(**kwargs)

The save method can take up to four optional keyword arguments. By using **kwargs, we can pass the keyword arguments the save function was called with without having to define them explicitly.

3. Custom methods

Custom methods are methods we can add to a model that are not defined on the parent class.

They are useful when you want to perform an operation on a model instance like a blog post.

In this example, I’ve written a method called def publish(self):. All it does is set the published date, change the post status and save the post.

Custom methods should be used when you have a bit of code that operates on a single instance of a model. It is particularly important when that bit of code is used in multiple places, as it will keep your code DRY (Don’t Repeat Yourself).

I also find that it makes the code easier to read. In views.py, I can now publish a post with one line of code: post.publish()

Model

def publish(self):
    self.published_date = datetime.now()
    self.status = "published"
    self.save()

View

def publish(request: HttpRequest, slug: str) -> HttpResponse:

    post = get_object_or_404(Post, slug=slug)
    post.publish()

    return redirect("index")

Examples of when a custom method could be useful

  • You have a model called Order. You could have a method called place() to allow you to place an order with one line of code: order.place()
  • You have a model called Task that stores payloads to be sent to an API. Your model could have a method submit() to submit the task.

4. Dunder methods

The final type of method are our dunder methods. These are methods that start and end with a double underscore. These are a feature of Python rather than Django specifically. The __str__ method defines the string representation.

You can find the string representation for any object by calling str(<object-name>).

By default, Django sets the string on model instances which provide very little context about the object is about.

Without setting __str__ Django uses a default constructed from the model name and the object ID. ‘Post object (1)’ isn’t very helpful at all. You will notice this when you go into admin.

You can change it by defining __str__ on your model:

def __str__(self):
    return self.title

Before:

>>> from blog.models import Post
>>> post = Post.objects.first()
>>> post
<Post: Post object (1)>
>>> str(post)
'Post object (1)'
>>>

After:

>>> post = Post.objects.first()
>>> post
<Post: How to Add CSS to a Django Project>
>>> str(post)
'How to Add CSS to a Django Project'
>>>

Conclusion

There are four kinds of model methods:

  1. Properties are used to define property that isn’t stored in the database. Instead they are calculated from other fields. Unlike custom methods, properties can be accessed without calling the method like a function.
  2. Overriding parent methods allow you to extend the functionality of Django’s base model. Using super() in these methods is important, so you can add your own code without losing the method’s original functionality.
  3. Custom methods are useful when you have a function that operates on a single object. They can be used in scenarios like publishing a blog post with def publish(self) or placing an order def place(self).
  4. Dunder methods are native to Python. They provide small but useful features like defining the string representation of an object.

Choosing the correct model method will help keep your code DRY and give you the tools to add custom features to your model classes.

Related Posts