I think @tghw's answer is the correct one. That being said, there are ways to make your code clean while calling methods in the template.
With the following tag, you can call methods in your templates:
from django import template
register = template.Library()
@register.simple_tag
def call(obj, method_name, *args, **kwargs):
"Call obj's method and pass it the given parameters"
return getattr(obj, method_name)(*args, **kwargs)
You can use it like this:
{% call some_obj 'method_name' param1 param2 %}
You can use named parameters, and if you need to store it in a variable, you can do:
{% call some_obj 'method_name' param1 param2 as my_result %}
The thing is, adding a method to your model which only will be used by the template could be considered bad, as it's mixing presentation logic with business logic (although it could be argued business logic should not live in the database layer either, as Django ties those up, but w/e).
One thing you can do is create a Presenter/Decorator and wrap your model in it, so all presentation logic lives in its own layer and it can be tested separately.
# models.py
class MyModel(models.Model):
class Statuses(models.IntegerChoices):
IN_PROGRESS = 0
COMPLETED = 1
status = models.IntegerField(choices=Statuses.choices, default=0)
# presenters.py
class MyModelPresenter:
def __init__(self, model):
self.model = model
def is_completed(self):
return self.status == MyModel.Statuses.COMPLETED
def is_in_progress(self):
return self.status == MyModel.Statuses.IN_PROGRESS
# views.py
def my_view(request, pk):
my_model = MyModelPresenter(MyModel.get(pk=pk))
render(request, 'my_template.html', {
'my_model': my_model
})
Another pattern you can use is "view components", like Rail's ViewComponent. So instead of calling a method in the template, you just collect everything you need in your component and then just pass it. This is Django's original approach, but the view is in charge of so much more than just presenting the data. It has to fetch things from the database, validate stuff, authenticate, authorize, etc. So having a component layer with testable reusable components is a great pattern to have.