python - Django admin добавить пользовательский фильтр




python-2.7 django-admin (2)

Я использую 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

Недавно я столкнулся с подобной проблемой, когда мне нужно было отфильтровать данные на основе значения из другой модели. Это можно сделать с помощью SimpleListFilter. Вам просто нужно немного настроить функцию поиска и набора запросов. Я предлагаю вам установить панель инструментов отладки 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'.

Это не совсем то, что вы просили, но вы могли бы вместо этого иметь фильтр в JobAdDuration modelAdmin. Таким образом, вы можете фильтровать соответствующие вакансии по 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 ), то все будет довольно сложно.





django-admin-filters