django admin list_filter foreign key subset

1.8k Views Asked by At

I currently have a model set up like so:

I have a Test class with a foreign key to a custom UserProfile class. This property is called student. So each UserProfile may have taken more than one Test. Then in turn, each UserProfile is tied to a User by the typical onetoone relation. Another level of complexity is added because I have declared two user groups tutors and students. And the UserProfile class has a ManyToMany relation on to itself labeled "tutors". The logic is that each student may have many tutors teaching him. Now what I would like to do is add filters on the right-hand side of the Test admin page that lets you filter by the student and filter by tutor as well. setting list_filter = ('student',) simply lists all available UserProfiles. But this would include the UserProfiles which are tied to tutors as well. Obviously I would like to filter this list down to only students, because clearly filtering by any of these tutors would result in an empty queryset. I would then like to do a similar thing with filtering by tutor, where in this case the shorthand would be list_filter = ('student__tutors'), but I'd want this UserProfile set to be filtered down to only include those where user_groups_name='tutors'. What is the best way to do this?

For clarity, my object model looks like this:

class UserProfile(models.Model):
    user = models.OneToOneField(User, unique=True,related_name='profile')
    tutors = models.ManyToManyField("self",related_name="students")
class Test(models.Model):
    student = models.ForeignKey(UserProfile,related_name='tests')

I tried subclassing django.contrib.admin.SimpleListFilter with the following:

class StudentListFilter(SimpleListFilter):
    title = 'student'
    parameter_name = 'student__id__exact'
    def lookups(self, request, model_admin):
        qs = model_admin.queryset(request)
        return User.objects.filter(profile__id__in=qs.values_list('student__id')).extra(select={'full_name':'first_name||" "||last_name'}).values_list('profile__id','full_name')

    def queryset(self, request, queryset):
        if self.value() is not None:
            return queryset.filter(student__id__exact=self.value())
        else:
            return queryset

Which seems to work for the first list_filter, but the trouble is for some reason there's a bug now where the selected option does not get highlighted in the custom filter. Only 'All' highlights but not the custom options. Here is an example of it that another user posted:

http://imgur.com/lyrYk

I am currently using the development version of django 1.4, so I'm not sure if this issue is tied to that or not.

1

There are 1 best solutions below

2
On BEST ANSWER

Interesting problem.

I think you need to convert your lookups to a string.

return [(str(x), y) for x, y in your_valuesqueryset]

Line 98 on django.admin.filters defines the selected filter as:

'selected': self.value() == lookup

Where the lookup is populated directly from the results of the lookups method.

The auto type coercion in your filter() call is making the filter succeed but '2' != 2