How to override the queryset giving the filters in list_filter?

后端 未结 4 2225
盖世英雄少女心
盖世英雄少女心 2020-12-15 18:42

Given the following models

class AnotherModel(models.Model):
    n = models.IntegerField()

class MyModel(models.Model):
    somefield = models.ForeignKey(An         


        
相关标签:
4条回答
  • 2020-12-15 19:04

    I found another method similar to @seddonym, but doesn't mess with the caching. It is based on this Django code, but uses undocumented method field_choices, which can be subject to change in the future Django releases. The code for @seddonym's case would be:

    from django.contrib.admin.filters import RelatedFieldListFilter
    
    class SpeciesFilter(RelatedFieldListFilter):
        def field_choices(self, field, request, model_admin):
            return field.get_choices(include_blank=False, limit_choices_to={'name': 'Tarantula'})
    

    Or in my case the working code is:

    from django.contrib.admin.filters import RelatedFieldListFilter
    
    class UnitFilter(RelatedFieldListFilter):
        def field_choices(self, field, request, model_admin):
            return field.get_choices(include_blank=False, limit_choices_to={'pk__in': request.user.administrated_units.all()})
    
    
    0 讨论(0)
  • 2020-12-15 19:07

    Edit - this method has been pointed out to have issues, see below

    You can do it like this:

    Let's say you have a model called Animal, which has a ForeignKey field to a model called Species. In a particular admin list, you want to allow only certain species to be shown in the animals filter choices.

    First, specify a custom ListFilter called SpeciesFilter in the Animal's ModelAdmin:

    class AnimalAdmin(ModelAdmin):
        list_filter = (('species', SpeciesFilter),)
    

    Then define the SpeciesFilter:

    from django.contrib.admin.filters import RelatedFieldListFilter
    
    class SpeciesFilter(RelatedFieldListFilter):
        def __init__(self, field, request, *args, **kwargs):
            """Get the species you want to limit it to.
            This could be determined by the request,
            But in this example we'll just specify an
            arbitrary species"""
            species = Species.objects.get(name='Tarantula')
    
            #Limit the choices on the field
            field.rel.limit_choices_to = {'species': species}
    
            #Let the RelatedFieldListFilter do its magic 
            super(SpeciesFilter, self).__init__(field, request, *args, **kwargs)
    

    That should do it.

    0 讨论(0)
  • 2020-12-15 19:09

    See ModelAdmin.queryset and ModelAdmin.formfield_for_foreignkey. From the docs:

    The queryset method on a ModelAdmin returns a QuerySet of all model instances that can be edited by the admin site. One use case for overriding this method is to show objects owned by the logged-in user:

    class MyModelAdmin(admin.ModelAdmin):
        def queryset(self, request):
            qs = super(MyModelAdmin, self).queryset(request)
            if request.user.is_superuser:
                return qs
            return qs.filter(author=request.user)
    

    The formfield_for_foreignkey method on a ModelAdmin allows you to override the default formfield for a foreign keys field. For example, to return a subset of objects for this foreign key field based on the user:

    class MyModelAdmin(admin.ModelAdmin):
        def formfield_for_foreignkey(self, db_field, request, **kwargs):
            if db_field.name == "car":
                kwargs["queryset"] = Car.objects.filter(owner=request.user)
            return super(MyModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
    

    This uses the HttpRequest instance to filter the Car foreign key field to only display the cars owned by the User instance.

    [update]

    Sorry, I failed to read the "filter" part. In Django >= 1.4 you can pass a subclass of django.contrib.admin.SimpleListFilter in the list_filter argument list, which you can use in order to override the lookups and queryset methods.

    from datetime import date
    
    from django.contrib import admin
    from django.utils.translation import ugettext_lazy as _
    
    class DecadeBornListFilter(admin.SimpleListFilter):
        # Human-readable title which will be displayed in the
        # right admin sidebar just above the filter options.
        title = _('decade born')
    
        # Parameter for the filter that will be used in the URL query.
        parameter_name = 'decade'
    
        def lookups(self, request, model_admin):
            """
            Returns a list of tuples. The first element in each
            tuple is the coded value for the option that will
            appear in the URL query. The second element is the
            human-readable name for the option that will appear
            in the right sidebar.
            """
            return (
                ('80s', _('in the eighties')),
                ('90s', _('in the nineties')),
            )
    
        def queryset(self, request, queryset):
            """
            Returns the filtered queryset based on the value
            provided in the query string and retrievable via
            `self.value()`.
            """
            # Compare the requested value (either '80s' or '90s')
            # to decide how to filter the queryset.
            if self.value() == '80s':
                return queryset.filter(birthday__gte=date(1980, 1, 1),
                                    birthday__lte=date(1989, 12, 31))
            if self.value() == '90s':
                return queryset.filter(birthday__gte=date(1990, 1, 1),
                                    birthday__lte=date(1999, 12, 31))
    
    class PersonAdmin(admin.ModelAdmin):
        list_filter = (DecadeBornListFilter,)
    
    0 讨论(0)
  • I had to create my lookup fields from db table. I created custom filter class as below and displaying only related values to logged in user and filter accordingly:

    class ShiftFilter_Org(admin.SimpleListFilter):
    title = 'Organisation'
    parameter_name = 'org'
    
    def lookups(self, request, model_admin):
    """Get the organisations you want to limit"""
        qs_org = Organisation.objects.filter(users=request.user)
        list_org = []
        for og in qs_org:
            list_org.append(
                (og.id, og.name)
            )
        return (
            sorted(list_org, key=lambda tp:tp[1])
        )
    
    def queryset(self, request, queryset):
        if self.value():
            return queryset.filter(org=self.value())
    

    For more visit Getting the most out of Django Admin filters

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