Django 管理员添加自定义过滤器

问题描述

我正在使用 django 1.10,我需要显示数据并根据来自不同模型的值创建过滤器(该模型具有引用我在管理模板上使用的模型的外键)这些是我的 2 个模型:这个是用来生成模板的:

i'm using django 1.10 and I need to display data and create a filter based on a value from a different model(which has a foreign key referencing my model that is used on the admin template) These are my 2 models: This one is used to generate the template:

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)

这是另一个持有我的第一个外键引用的:

This is the other one that holds a foreign key reference to my first one:

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

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

Inside my template, I have been able to display the(latest)start and end times

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_display 中调用它,它工作正常.但是,我无法使用这些条件设置筛选字段.

And then I just call this inside the list_display and that is working fine. However, i have trouble setting a filter field using these criteria.

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

If I just add it to my list_filter then I get an error that there is no such field inside my model which is true (since that one is in another table that has reference to my job table). So I was wondering what is the right approach to solve this? Do I need to create another function for the filter itself but even then I'm not sure how should I call it inside the list_filter.

这是我的 Django 管理页面的片段.

Here is a snippet of my Django admin page.

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

与此同时,我尝试用一​​个简单的列表过滤器来解决它,但我无法让它工作.我想放置 2 个带有日历的输入字段(如默认的 DateRangeFilter),表示开始和结束时间,然后根据这些值返回数据.这是我的原型"简单过滤器的功能,它可以工作,但它返回硬编码的数据.

In the meantime, I tried to solve it with a simple list filter, but I am unable to get it to work. I would like to place 2 input fields with a calendar(like the default DateRangeFilter) that would represent the start and end time, and then return data based on those values. This is my "prototype" functionality for the simple filter, it works but it returns hard-coded data.

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

 


解决方案

我会使用自定义的 FieldListFilter,因为它允许根据您的要求将过滤器绑定到不同的模型字段.

I would go with customized FieldListFilter as it allows to bind filter to different model fields based on your requirements.

接下来我们要如何实现这样的过滤器:

What is we actually do to implement such filter is next:

  • 构建 lookup_kwargs gte 和 lte 并将它们指定为 expected_pa​​rameters
  • 定义选择以返回空列表,否则 NotImplementedError
  • 创建表单以关心字段验证
  • 创建只输出表单的自定义模板,例如{{spec.form}}
  • 如果表单有效,则获取已清理的数据,过滤掉 Nones 并过滤查询集,否则执行错误操作(在下面的代码中,错误被静音)

过滤代码:

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()

相关文章