Django 管理员添加自定义过滤器
Posted
技术标签:
【中文标题】Django 管理员添加自定义过滤器【英文标题】:Django admin add custom filter 【发布时间】:2018-07-15 04:01:05 【问题描述】:我正在使用 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)
这是另一个持有我的第一个外键引用的:
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_display 中调用它,它工作正常。 但是,我无法使用这些条件设置筛选字段。
如果我只是将它添加到我的 list_filter 中,那么我会收到一个错误,即我的模型中没有这样的字段,这是正确的(因为该字段位于另一个引用我的作业表的表中)。所以我想知道解决这个问题的正确方法是什么?我是否需要为过滤器本身创建另一个函数,但即便如此我也不确定我应该如何在 list_filter 中调用它。
这是我的 Django 管理页面的 sn-p。
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),表示开始和结束时间,然后根据这些值返回数据。这是我的简单过滤器的“原型”功能,它可以工作,但会返回硬编码数据。
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
【问题讨论】:
你需要一个自定义过滤器类,见docs和sample tutorial 是的,我这样做了,但我无法将日期选择器添加到过滤器中,从那里我可以从用户那里获取信息。我想要 2 个字段,我将在其中输入开始日期和结束日期,获取这些值并根据它过滤我的数据。我忘了用那个更新我的问题,现在就做 澄清一下,您是希望JobAdDuration
和Job
之间存在ManyToOne
关系,还是OneToOne
?
多对一。
【参考方案1】:
我会使用自定义的FieldListFilter
,因为它允许根据您的要求将过滤器绑定到不同的模型字段。
接下来我们要如何实现这样的过滤器:
构建lookup_kwargs gte 和lte 并将它们指定为expected_parameters
定义选择以返回空列表,否则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()
【讨论】:
谢谢,我还在忙其他事情,但我会在周末尝试你的解决方案,然后再报告。感谢您的帮助 呃,我仍然没有时间测试它,但它似乎可以工作......我稍后会联系你寻求帮助:P 对不起,我忘了给你赏金【参考方案2】:这并不完全符合您的要求,但您可以在 JobAdDuration
modelAdmin 上使用过滤器。这样就可以得到根据ad_activated
和ad_finished
字段过滤的对应jobs。我已经添加了指向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
中过滤),那么事情会变得相当复杂。
【讨论】:
在这种情况下,我必须使用 JobAdmin 中的过滤器。【参考方案3】:我最近遇到了类似的问题,我需要根据来自另一个模型的值过滤数据。这可以使用 SimpleListFilter 来完成。您只需要对查找和查询集功能进行一些调整。我会建议你安装 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
并在 list_filter 中
list_filter = (StartTimeFilter) # no quotes else it will search for a field in the model 'job'.
【讨论】:
这不会给我一个包含所有可能日期的列表吗?我想要两个“输入框”,我可以在其中放置我的日期并根据它进行过滤。 在这种情况下@bellum 的解决方案已经足够好了。自定义 FieldListFilter,创建 datetimerangeform,将解决您的问题。以上是关于Django 管理员添加自定义过滤器的主要内容,如果未能解决你的问题,请参考以下文章