Search code examples
djangodjango-modelsdjango-templatesdjango-querysetdjango-managers

How do I attach an instance-specific queryset to a model in Django?


I am making a very minimal user-to-user messaging app using Django 1.4. I want to fetch unread messages that a user received in a straightforward way from my templates. My model looks something like this:

from django.contrib.auth.models import User

class Message(models.Model):
    sender = models.ForeignKey(User, related_name='messages_sent')
    receiver = models.ForeignKey(User, related_name='messages_received')
    read = models.BooleanField(default=False)

Now I can easily access the messages that a user has received from user.messages_received. I'd like to filter this queryset, though, to quickly access the unread messages in an easy way. I know that I can always filter the queryset user.messages_received.filter(read=False), but I'd like to get at them directly in templates, possibly like this:

<a href="{% url inbox %}"> Inbox ({{ user.unread_messages.count }})</a>

I suspect I want to make a Manager, but I'm not sure how to write it or where to attach it.

Thanks in advance.


Solution

  • There are two ways to accomplish this that come to my mind.

    First, you could extend the user model with a custom function.

    from django.contrib.auth.models import User
    
    def unread_messages(self):
        return self.messages_received.filter(read=False)
    User.add_to_class('unread_messages', unread_messages)
    

    But that is slightly hacky. The "clean" way would be not to extend the User model directly, but to create a UserProfile for your users and add the function there. The docs describe this quite well.

    In your case:

    models.py

    from django.db import models
    from django.contrib.auth.models import User
    
    class UserProfile(models.Model):
        user = models.OneToOneField(User)
        def unread_messages(self):
            return self.user.messages_received.filter(read=False)
    

    settings.py

    AUTH_PROFILE_MODULE = 'appname.UserProfile'
    

    template

    {{ user.get_profile.unread_messages.count }}
    

    The code is untested, but should work :)