Lelouch
Lelouch

Reputation: 2193

Django filtering and querying: do it in views.py, template, or filters?

So I am working on a small Django project, which for the moment doesn't require optimization. But to prepare for the future, I'd like to know a bit more about the three approaches.

For instances, as part of the models, I have User and UserProfile, Transaction.

class User(models.Model):
    name = ...
    email = ...

class UserProfile(models.Model):
    user = models.ForeignKey(User, related_name='profile')
    photo = models.URLField(...)
    ...

class Transaction(models.Model):
    giver = models.ForeignKey(User, related_name="transactions_as_giver")
    receiver = models.ForeignKey(User, related_name='transactions_as_receiver')
    ...

I frequently need to do something like "return transactions that the request.user is giver or receiver", or "return the profile photo of a user". I have several choices, for instance to get a list of pending transactions and photos of both parties, I can do it at views.py level:

1.

#views.py
transactions = Transaction.objects.filter(Q(giver=request.user)|Q(receiver=request.user))
for transaction in transactions:
    giver_photo = transactions.giver.profile.all()[0].photo
    # or first query UserProfile by
    # giver_profile = UserProfile.objects.get(user=transaction.giver),
    # then giver_photo = giver_profile.photo
    # 
    # Then same thing for receiver_photo
    transaction['giver_photo'] = giver_photo
    ...
  1. Or I can do it more on template level:

    # some template
    <!-- First receive transactions from views.py without photo data -->
    {% for t in transactions %}
    {{t.giver.profile.all.0.photo}}, ...
    {% endfor %}
    
  2. Or I can move some or even all of the above stuffs into filters.py

    # some template
    {{ for t in request.user|pending_transactions }}
    {{ t.giver|photo  }} {{ t.receiver|photo }}
    {{ endfor }}
    

where photo and pending_transactions are roughly the same code in original views.py but moved to a filter.

So I wonder is there a best practice/guide line on how to choose which approach?

From Django documentation, lower level is faster, and therefore 2. 3. should be slower than 1; but how about comparing the 2. and 3.?

In getting a user photo, which of the two should be recommended, transactions.giver.profile.all()[0].photo OR profile = UserProfile.objects.get(...) --> photo = profile.photo?

Upvotes: 2

Views: 1737

Answers (1)

catavaran
catavaran

Reputation: 45575

Move this logic into models and managers. Views and templates must be as short as possible.

class User(models.Model):
    ...
    def transactions(self):
        return Transaction.objects.filter(Q(giver=self)|Q(receiver=self))

    def photo(self):
        return self.profile.all().first().photo

So the template will be:

{% for t in request.user.transactions %}
    {{ t.giver.photo  }} {{ t.receiver.photo }}
{% endfor %}

My experience says that business logic in model as much easier to test, support and reuse than in the views/templates.

Upvotes: 1

Related Questions