Django-Admin: list_filter attribute from UserProfile

后端 未结 2 1010
别那么骄傲
别那么骄傲 2020-12-28 23:46

I want to allow the admins of my site to filter users from a specific country on the Admin Site. So the natural thing to do would be something like this:

#ad         


        
相关标签:
2条回答
  • 2020-12-28 23:49

    Django 1.3 fixed it. You're now allowed to span relations in list_filter

    https://docs.djangoproject.com/en/1.3/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter

    0 讨论(0)
  • 2020-12-29 00:01

    What you are looking for is custom admin FilterSpecs. The bad news is, the support for those might not supposed to ship soon (you can track the discussion here).

    However, at the price of a dirty hack, you can workaround the limitation. Some highlights on how FilterSpecs are built before diving in the code :

    • When building the list of FilterSpec to display on the page, Django uses the list of fields you provided in list_filter
    • Those fields needs to be real fields on the model, not reverse relationship, nor custom properties.
    • Django maintains a list of FilterSpec classes, each associated with a test function.
    • For each fields in list_filter, Django will use the first FilterSpec class for which the test function returns True for the field.

    Ok, now with this in mind, have a look at the following code. It is adapted from a django snippet. The organization of the code is left to your discretion, just keep in mind this should be imported by the admin app.

    from myapp.models import UserProfile, Country
    from django.contrib.auth.models import User
    from django.contrib.auth.admin import UserAdmin
    
    from django.contrib.admin.filterspecs import FilterSpec, ChoicesFilterSpec
    from django.utils.encoding import smart_unicode
    from django.utils.translation import ugettext_lazy as _
    
    class ProfileCountryFilterSpec(ChoicesFilterSpec):
        def __init__(self, f, request, params, model, model_admin):
            ChoicesFilterSpec.__init__(self, f, request, params, model, model_admin)
    
            # The lookup string that will be added to the queryset
            # by this filter
            self.lookup_kwarg = 'userprofile__country__name'
            # get the current filter value from GET (we will use it to know
            # which filter item is selected)
            self.lookup_val = request.GET.get(self.lookup_kwarg)
    
            # Prepare the list of unique, country name, ordered alphabetically
            country_qs = Country.objects.distinct().order_by('name')
            self.lookup_choices = country_qs.values_list('name', flat=True)
    
        def choices(self, cl):
            # Generator that returns all the possible item in the filter
            # including an 'All' item.
            yield { 'selected': self.lookup_val is None,
                    'query_string': cl.get_query_string({}, [self.lookup_kwarg]),
                    'display': _('All') }
            for val in self.lookup_choices:
                yield { 'selected' : smart_unicode(val) == self.lookup_val,
                        'query_string': cl.get_query_string({self.lookup_kwarg: val}),
                        'display': val }
    
        def title(self):
            # return the title displayed above your filter
            return _('user\'s country')
    
    # Here, we insert the new FilterSpec at the first position, to be sure
    # it gets picked up before any other
    FilterSpec.filter_specs.insert(0,
      # If the field has a `profilecountry_filter` attribute set to True
      # the this FilterSpec will be used
      (lambda f: getattr(f, 'profilecountry_filter', False), ProfileCountryFilterSpec)
    )
    
    
    # Now, how to use this filter in UserAdmin,
    # We have to use one of the field of User model and
    # add a profilecountry_filter attribute to it.
    # This field will then activate the country filter if we
    # place it in `list_filter`, but we won't be able to use
    # it in its own filter anymore.
    
    User._meta.get_field('email').profilecountry_filter = True
    
    class MyUserAdmin(UserAdmin):
      list_filter = ('email',) + UserAdmin.list_filter
    
    # register the new UserAdmin
    from django.contrib.admin import site
    site.unregister(User)
    site.register(User, MyUserAdmin)
    

    It's clearly not a panacea but it will do the job, waiting for a better solution to come up.(for example, one that will subclass ChangeList and override get_filters).

    0 讨论(0)
提交回复
热议问题