Django admin добавить пользовательский фильтр
я использую 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.
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
3 ответов
Я бы пошел с customized FieldListFilter
поскольку это позволяет привязать фильтр к различным полям модели на основе ваших требований.
что мы на самом деле делаем для реализации такого фильтра:
- build lookup_kwargs gte и lte и укажите их как
expected_parameters
- определить выбор вернуть пустой список, иначе
NotImplementedError
- создать форму для проверки полей ухода
- создать пользовательский шаблон, который просто выводит форму, например {{спекуляция.форма}}
- если форма действительна, возьмите очищенные данные, отфильтруйте 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()
это не совсем то, что вы просили, но вы могли бы иметь фильтр на JobAdDuration
modelAdmin. Таким образом, вы можете получить соответствующие задания, отфильтрованные в соответствии с ad_activated
и ad_finished
поля. И я добавил ссылку на job
поле, поэтому вы можете сразу щелкнуть его для более легкой навигации.
чтобы сделать его фильтром html5 даты, я использовал django-admin-фильтр дальности библиотека.
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
), то все будет довольно сложно.
недавно я столкнулся с аналогичной проблемой, когда мне нужно было фильтровать данные на основе значения из другой модели. Это можно сделать с помощью SimpleListFilter. Вам просто нужно немного настроить функцию lookup и queryset. Я предлагаю вам установить панель инструментов отладки django, чтобы вы могли знать, какие SQL-запросы выполняются внутри django.
#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'.