简体   繁体   English

Django admin 添加自定义过滤器

[英]Django admin add custom filter

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:我正在使用 django 1.10,我需要显示数据并根据来自不同模型的值创建过滤器(它有一个外键引用我在管理模板上使用的模型)这些是我的 2 个模型:这是用于生成模板:

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

And then I just call this inside the list_display and that is working fine.然后我只是在 list_display 中调用它,这工作正常。 However, i have trouble setting a filter field using these criteria.但是,我无法使用这些条件设置过滤器字段。

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).如果我只是将它添加到我的 list_filter 中,那么我会收到一个错误,即我的模型中没有这样的字段,这是真的(因为那个字段在另一个表中,该表引用了我的工作表)。 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.我是否需要为过滤器本身创建另一个函数,但即便如此,我也不知道我应该如何在 list_filter 中调用它。

Here is a snippet of my Django admin page.这是我的 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

EDIT: 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.我想放置 2 个带有日历的输入字段(如默认的 DateRangeFilter),代表开始和结束时间,然后根据这些值返回数据。 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

 

I would go with customized FieldListFilter as it allows to bind filter to different model fields based on your requirements.我会使用自定义的FieldListFilter因为它允许根据您的要求将过滤器绑定到不同的模型字段。

What is we actually do to implement such filter is next:接下来我们实际做的是实现这样的过滤器:

  • build lookup_kwargs gte and lte and specify them as expected_parameters构建 lookup_kwargs gte 和 lte 并将它们指定为expected_parameters
  • define choices to return empty list otherwise NotImplementedError定义选择返回空列表,否则NotImplementedError
  • create form to care fields validation创建表单以关注字段验证
  • create custom template which just outputs form, eg {{spec.form}}创建只输出表单的自定义模板,例如 {{spec.form}}
  • if form is valid take it's cleaned data, filter out Nones and filter queryset otherwise do something with errors (in code below errors are silenced)如果表单有效,则将其清除数据,过滤掉 None 并过滤查询集,否则对错误执行某些操作(在下面的代码中,错误被静音)

Filter code:过滤代码:

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

Form can be as simple as follows:表格可以简单如下:

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

This isn't exactly what you've asked for, but you could instead have the filter on the JobAdDuration modelAdmin.这并不完全是您所要求的,但您可以在JobAdDuration模型管理员上使用过滤器。 This way, you can get the corresponding jobs filtered according to the ad_activated and ad_finished fields.这样,您就可以根据ad_activatedad_finished字段过滤出相应的作业。 And I've added a link to the job field, so you can directly click it for easier navigation.而且我已经添加了一个指向job字段的链接,因此您可以直接单击它以更轻松地导航。

To make it a date html5 filter, I've used django-admin-rangefilter library.为了使它成为日期 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'

If you indeed want to go the existing route (filter inside JobAdmin ), then things will get quite complicated.如果您确实想走现有路线(在JobAdmin过滤),那么事情会变得非常复杂。

I have recently faced similar problem where I needed to filter data based on value from another model.我最近遇到了类似的问题,我需要根据另一个模型的值过滤数据。 This can be done using SimpleListFilter.这可以使用 SimpleListFilter 来完成。 You just need a little tweak in the lookup and queryset function.您只需要在查找和查询集函数中稍作调整。 I will suggest you to install django debug toolbar so that you may know what sql queries are being executed internally by django.我建议你安装 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

and in list_filter并在 list_filter 中

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM