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:
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.
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