web-dev-qa-db-ja.com

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)

これは私の最初のものへの外部キー参照を保持するもう1つのものです。

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

編集:その間、私は単純なリストフィルターで解決しようとしましたが、機能させることができません。開始時刻と終了時刻を表すカレンダー(デフォルトのDateRangeFilterなど)を使用して2つの入力フィールドを配置し、それらの値に基づいてデータを返します。これは、単純なフィルターの「プロトタイプ」機能です。機能しますが、ハードコードされたデータを返します。

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
18
Proxy

要件に基づいてフィルターをさまざまなモデルフィールドにバインドできるため、カスタマイズされたFieldListFilterを使用します。

そのようなフィルターを実装するために実際に行うことは次のとおりです。

  • lookup_kwargs gteおよびlteをビルドし、expected_parametersとして指定します
  • それ以外の場合は空のリストを返すように選択肢を定義しますNotImplementedError
  • フィールド検証をケアするフォームを作成する
  • フォームを出力するだけのカスタムテンプレートを作成します。 {{spec.form}}
  • フォームが有効な場合は、データを消去し、Noneをフィルターで除外し、クエリセットをフィルターします。それ以外の場合は、エラーが発生して何かを実行します(以下のコードではエラーは表示されません)。

フィルターコード:

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

これは正確には要求したものではありませんが、代わりに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内のフィルター)に移動したい場合、状況は非常に複雑になります。

5
shad0w_wa1k3r

私は最近、別のモデルの値に基づいてデータをフィルター処理する必要があるという同様の問題に直面しました。これは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'.
3
Aman trigunait