代码之家  ›  专栏  ›  技术社区  ›  Proxy

Django admin添加自定义筛选器

  •  19
  • Proxy  · 技术社区  · 6 年前

    我使用的是django 1.10,我需要显示数据并根据不同模型的值创建一个过滤器(该模型有一个外键引用我在管理模板上使用的模型) 以下是我的两款车型: 这个用于生成模板:

    class Job(models.Model):
        company = models.ForeignKey(Company)
        title = models.CharField(max_length=100, blank=False)
        description = models.TextField(blank=False, default='')
        store = models.CharField(max_length=100, blank=True, default='')
        phone_number = models.CharField(max_length=60, null=True, blank=True)
    

    这是另一个保存对我的第一个外键引用的外键:

    class JobAdDuration(models.Model):
        job = models.ForeignKey(Job)
        ad_activated = models.DateTimeField(auto_now_add=True)
        ad_finished = models.DateTimeField(blank=True, null=True)
    

    在我的模板中,我能够显示(最新的)开始和结束时间

    def start_date(self,obj):
        if JobAdDuration.objects.filter(job=obj.id).exists():
            tempad = JobAdDuration.objects.filter(job=obj).order_by("-id")[0]
            return tempad.ad_activated
    

    然后我在list\u显示中调用它,它工作正常。 但是,我无法使用这些条件设置过滤器字段。

    如果我只是将其添加到我的list\u过滤器中,那么我会得到一个错误,即在我的模型中没有这样的字段,这是真的(因为该字段位于引用我的作业表的另一个表中)。所以我想知道解决这个问题的正确方法是什么?我是否需要为过滤器本身创建另一个函数,但即使这样,我也不确定应该如何在list\u过滤器中调用它。

    这是我的Django管理页面的一个片段。

    class JobAdmin(admin.OSMGeoAdmin, ImportExportModelAdmin):
        inlines = [
        ]
    
        readonly_fields = ( 'id', "start_date", )
    
        raw_id_fields = ("company",)
    
        list_filter = (('JobAdDuration__ad_activated', DateRangeFilter), 'recruitment', 'active', 'deleted', 'position', ('created', DateRangeFilter), 'town')
        search_fields = ('title', 'description', 'company__name', 'id', 'phone_number', 'town')
        list_display = ('title', 'id', 'description', 'active', 'transaction_number', 'company', 'get_position', 'town','created', 'expires', 'views', 'recruitment', 'recruits', 'paid', 'deleted', "start_date", "end_Date", "ad_consultant")
    
    
        def start_date(self,obj):
            if JobAdDuration.objects.filter(job=obj.id).exists():
                tempad = JobAdDuration.objects.filter(job=obj).order_by("-id")[0]
                return tempad.ad_activated
    

    编辑: 与此同时,我试图用一个简单的列表过滤器来解决这个问题,但我无法让它正常工作。我想放置两个带有日历(如默认DateRangeFilter)的输入字段,该日历表示开始和结束时间,然后根据这些值返回数据。这是我的简单过滤器的“原型”功能,它工作正常,但返回硬编码数据。

    class StartTimeFilter(SimpleListFilter):
        title = ('Start date')
        parameter_name = 'ad_finished'
    
        def lookups(self, request, model_admin):
           #return JobAdDuration.objects.values_list("ad_finished")
           return (
           ('startDate', 'stest1'),
           ('startDate1', 'test2')
           )
    
        def queryset(self, request, queryset):
            if not self.value():
                return queryset
    
     
            assigned = JobAdDuration.objects.filter(ad_finished__range=(datetime.now() - timedelta(minutes=45000), datetime.now()))
            allJobs = Job.objects.filter(pk__in=[current.job.id for current in assigned])
            return allJobs
    
     
    
    3 回复  |  直到 4 年前
        1
  •  9
  •   bellum    6 年前

    我会选择定制 FieldListFilter 因为它允许根据您的需求将过滤器绑定到不同的模型字段。

    下一步我们要做什么来实现这种过滤器:

    • 构建lookup\u kwargs gte和lte,并将其指定为 expected_parameters
    • 定义选项以返回空列表,否则 NotImplementedError
    • 创建表单以维护字段验证
    • 创建只输出表单的自定义模板,例如{{spec.form}}
    • 如果表单有效,则将其清除数据,过滤掉Nones并过滤queryset,否则会处理错误(在下面的代码中,错误将被静音)

    筛选器代码:

    class StartTimeFilter(admin.filters.FieldListFilter):
        # custom template which just outputs form, e.g. {{spec.form}}
        template = 'start_time_filter.html'
    
        def __init__(self, *args, **kwargs):
            field_path = kwargs['field_path']
            self.lookup_kwarg_since = '%s__gte' % field_path
            self.lookup_kwarg_upto = '%s__lte' % field_path
            super(StartTimeFilter, self).__init__(*args, **kwargs)
            self.form = StartTimeForm(data=self.used_parameters, field_name=field_path)
    
        def expected_parameters(self):
            return [self.lookup_kwarg_since, self.lookup_kwarg_upto]
    
        # no predefined choices
        def choices(self, cl):
            return []
    
        def queryset(self, request, queryset):
            if self.form.is_valid():
                filter_params = {
                    p: self.form.cleaned_data.get(p) for p in self.expected_parameters()
                    if self.form.cleaned_data.get(p) is not None
                }
                return queryset.filter(**filter_params)
            else:
                return queryset
    

    形式可以简单如下:

    class StartTimeForm(forms.Form):
    
        def __init__(self, *args, **kwargs):
            self.field_name = kwargs.pop('field_name')
            super(StartTimeForm, self).__init__(*args, **kwargs)
            self.fields['%s__gte' % self.field_name] = forms.DateField()
            self.fields['%s__lte' % self.field_name] = forms.DateField()
    
        2
  •  6
  •   shad0w_wa1k3r    6 年前

    这并不完全是您所要求的,但您可以在 JobAdDuration 模型管理员。这样,您可以根据 ad_activated ad_finished 领域。我添加了一个链接到 job 字段,以便您可以直接单击它以便于导航。

    为了使其成为日期html5过滤器,我使用了 django-admin-rangefilter 图书馆

    from django.urls import reverse
    from django.contrib import admin
    from .models import Job, JobAdDuration
    from django.utils.html import format_html
    from rangefilter.filter import DateRangeFilter
    
    
    @admin.register(JobAdDuration)
    class JobAdDurationAdmin(admin.ModelAdmin):
    
        list_filter = (('ad_activated', DateRangeFilter), ('ad_finished', DateRangeFilter))
        list_display = ('id', 'job_link', 'ad_activated', 'ad_finished')
    
        def job_link(self, obj):
            return format_html('<a href="{}">{}</a>', reverse('admin:job_job_change', args=[obj.job.id]), obj.job.title)
        job_link.short_description = 'Job'
    

    如果您确实想走现有路线(内部过滤 JobAdmin ),那么事情就会变得相当复杂。

        3
  •  3
  •   trigo    6 年前

    我最近遇到了类似的问题,我需要根据另一个模型的值过滤数据。这可以使用SimpleListFilter完成。您只需要在lookup和queryset函数中稍作调整。我建议您安装django调试工具栏,这样您就可以知道django正在内部执行哪些sql查询。

    #import your corresponding models first
    
    class StartTimeFilter(SimpleListFilter):
    title = ('Start date')
    parameter_name = 'ad_finished'
    
      def lookups(self, request, model_admin):
    
       data = []
       qs = JobAdDuration.objects.filter()   # Note : if you do not have distinct values of ad_activated apply distinct filter here to only get distinct values
       print qs
       for c in qs:
           data.append([c.ad_activated, c.ad_activated])  # The first c.activated is the queryset condition your filter will execute on your Job model to filter data ... and second c.ad_activated is the data that will be displayed in dropdown in StartTimeFilter
       return data
    
      def queryset(self, request, queryset):
         if self.value():
           assigned = JobAdDuration.objects.filter(ad_activated__exact = self.value())  # add your custom filter function based on your requirement
           return Job.objects.filter(pk__in=[current.job.id for current in assigned])
         else:
           return queryset
    

    和in list\u过滤器

    list_filter = (StartTimeFilter) # no quotes else it will search for a field in the model 'job'.