Filter Generic ListView by User

Posted on by andrew
1

I’ve been working quite a bit with Django’s class based generic views the past few weeks. Normally, if you just want to take advantage of class based views you can edit your urls.py and add the generic views directly there. For example, if you just want to list all the objects, you can use the generic ListView and add the following snippet to your urls.py urlpatterns:

Python
1
2
3
4
url(r'^myobjects/$',
        ListView.as_view(
            queryset = MyObject.objects.order_by('-date'),
            template_name ='myapp/list.html'), name='list'),

A problem arises, however, if you want to return only the objects associated with the logged in user. Normally in a view you simply filter based on request.user:

Python
1
user_objects = MyObject.objects.filter(user=request.user).order_by('-date')

In your urls.py, there is no request object, so it is simply not possible to filter based on the user in urls.py. Instead, you’ll have to do a little bit more work.

The generic class based ListView inherits a method get_queryset(self) whose function is to return a list of objects for the view. By default, this method looks like:

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def get_queryset(self):
        """
        Get the list of items for this view. This must be an iterable, and may
        be a queryset (in which qs-specific behavior will be enabled).
        """
        if self.queryset is not None:
            queryset = self.queryset
            if hasattr(queryset, '_clone'):
                queryset = queryset._clone()
        elif self.model is not None:
            queryset = self.model._default_manager.all()
        else:
            raise ImproperlyConfigured("'%s' must define 'queryset' or 'model'"
                                       % self.__class__.__name__)

The method first checks to see if we’ve already stored the queryset, and if so it returns it. Otherwise, the method returns all the objects the database manager cares to provide (line 11).

Luckily for us, we can also get the user who made the request in get_queryset(self) because the dispatch method of ListView (which is also inherited) stores the request made by the user in self.request. As you would expect, the user object is found at self.request.user.

Now, its simply a matter of creating a new class based view in our views.py that inherits from ListView. This new class overrides the get_queryset(self) method to return a filtered queryset:

Python
1
2
3
4
5
6
7
class MyObjectListView(ListView):
    """Use django generic ListView to list all the the MyObjects for the current
    user."""
    def get_queryset(self):
        """Override get_querset so we can filter on request.user """
        return MyObject.objects.filter(user=self.request.user).order_by('-date')

Hooking this new class based view up in our urls.py is also straightforward. You simply have to add the following line to your urlpatterns (of course you also have to import the new class based view as well):

Python
1
2
3
4
url(r'^myobjectlist/$',
       MyObjectListView.as_view(
            template_name='myapp/list.html'
        ), name='list'),

 

 

Posted in Django | 1 Reply

Logging Dajaxice Errors

Posted on by andrew
Reply

One of the problems when using Dajaxice in Django is that obtaining error messages is not well documented. Luckily, Dajaxice makes use of Python’s logging module. This means you can modify your settings.py to include a log handler for Dajaxice.

In your django settings module, look for the LOGGING directive. It should look similar to the following block of code:

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'mail_admins': {
            'level': 'ERROR',
            'class': 'django.utils.log.AdminEmailHandler'
         }
    },
    'loggers': {
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
         },
    }
}

By default, Django tries to mail errors sent to the django.requests logger to the admin email(s) by using the error handler mail_admins, which is provided by the django.utils.log.AdminEmailHandler class.

Instead of using the django.requests logger, Dajaxice sends its error to the dajaxice logger. To capture the errors sent to this log, we must define the logger and set up a handler to handle these errors.

Because I use Heroku and like to see errors in real time with the command heroku logs -tail, I opted to set up an additional handler to log output to the console. The following snippet, which goes in the handlers dictionary listed above, defines a handler called console that outputs anything sent to it:

Python
1
2
3
4
'console': {
    'level': 'DEBUG',
    'class': 'logging.StreamHandler'
}

Now that a handler is defined, it’s a few short lines to set up the logger using this handler. The following logger, named dajaxice, responds to log level WARNING or higher:

Python
1
2
3
4
5
'dajaxice': {
    'handlers': ['console'],
    'level': 'WARNING',
    'propagate': False,
},

Now when Dajaxice spits out an error, it will be outputted to the console. See the full code below:

Python