69

次のような2つのモデルを持つdjangoアプリケーションがあります。

class MyModel(models.Model):
    name = models.CharField()
    country = models.ForeignKey('Country')

class Country(models.Model):
    code2 = models.CharField(max_length=2, primary_key=True)
    name = models.CharField()

の管理クラスはMyModel次のようになります。

class MyModelAdmin(admin.ModelAdmin):
    list_display = ('name', 'country',)
    list_filter = ('country',)
admin.site.register(models.MyModel, MyModelAdmin)

Country表には約 250 か国が含まれています。MyModel実際に何らかの事例で言及されている国はほんの一握りです。

問題は、django admin のリストフィルターがフィルター パネルにすべての国を一覧表示することです。すべての国 (インスタンスによって参照されている国だけでなく) を一覧表示すると、この場合、リスト フィルターを使用する目的がほとんど無効になります。

MyModelリストフィルターの選択肢として参照されている国のみを表示するものはありますか? (私は Django 1.3 を使用しています。)

4

7 に答える 7

100

RelatedOnlyFieldListFilterDjango 1.8 の時点で、関連する国を表示するために使用できる が組み込まれています。

class MyModelAdmin(admin.ModelAdmin):
    list_display = ('name', 'country',)
    list_filter = (
        ('country', admin.RelatedOnlyFieldListFilter),
    )

Django 1.4-1.7 ではlist_filter、 のサブクラスを使用できますSimpleListFilter。必要な値をリストする単純なリスト フィルターを作成できるはずです。

Django 1.3 からアップグレードできない場合は、文書化されていない内部FilterSpecAPI を使用する必要があります。Django Adminの Stack Overflow の質問Custom Filter は、正しい方向を示しているはずです。

于 2012-08-31T13:19:46.517 に答える
35

質問は Django 1.3 に関するものでしたが、すぐに 1.4 にアップグレードすることについて言及されました。また、私のように 1.4 の解決策を探していたが、このエントリを見つけた人々にも、SimpleListFilter (利用可能な Django 1.4) を使用して、参照された (関連する、使用された) 外部キー値のみを表示する完全な例を示すことにしました。

from django.contrib.admin import SimpleListFilter

# admin.py
class CountryFilter(SimpleListFilter):
    title = 'country' # or use _('country') for translated title
    parameter_name = 'country'

    def lookups(self, request, model_admin):
        countries = set([c.country for c in model_admin.model.objects.all()])
        return [(c.id, c.name) for c in countries]
        # You can also use hardcoded model name like "Country" instead of 
        # "model_admin.model" if this is not direct foreign key filter

    def queryset(self, request, queryset):
        if self.value():
            return queryset.filter(country__id__exact=self.value())
        else:
            return queryset

# Example setup and usage

# models.py
from django.db import models

class Country(models.Model):
    name = models.CharField(max_length=64)

class City(models.Model):
    name = models.CharField(max_length=64)
    country = models.ForeignKey(Country)

# admin.py
from django.contrib.admin import ModelAdmin

class CityAdmin(ModelAdmin):
    list_filter = (CountryFilter,)

admin.site.register(City, CityAdmin)

例では、City と Country の 2 つのモデルを見ることができます。City には Country への ForeignKey があります。通常の list_filter = ('country',) を使用すると、セレクターにすべての国が表示されます。ただし、このスニペットは、関連する国のみをフィルタリングします。つまり、都市と少なくとも 1 つの関係がある国です。

ここから元のアイデア。著者に感謝します。ハードコーディングされたモデル名の代わりに model_admin.model をより明確にし、使用するためにクラス名を改善しました。

Django スニペットでも使用できる例: http://djangosnippets.org/snippets/2885/

于 2013-01-28T19:48:32.653 に答える
25

Django 1.8以降、次のものがあります:admin.RelatedOnlyFieldListFilter

使用例は次のとおりです。

class BookAdmin(admin.ModelAdmin):
    list_filter = (
        ('author', admin.RelatedOnlyFieldListFilter),
    )
于 2015-01-08T09:41:14.810 に答える
5

darklow のコードのルックアップを次のように変更します。

def lookups(self, request, model_admin):
    users = User.objects.filter(id__in = model_admin.model.objects.all().values_list('user_id', flat = True).distinct())
    return [(user.id, unicode(user)) for user in users]

これはデータベースにとってはるかに優れています;)

于 2014-03-28T09:10:34.917 に答える
2

これは、Django 1.4 の一般的で再利用可能な実装に関する私の見解です。これは、現在 Django 1.8 以降の一部である組み込みバージョンに触発されています。また、それを 1.5 ~ 1.7 に適応させるのは非常に小さな作業であるはずです。主にクエリセット メソッドの名前が変更されています。私はフィルター自体coreを私が持っているアプリケーションに入れましたが、明らかにどこにでも置くことができます。

実装:

# myproject/core/admin/filters.py:

from django.contrib.admin.filters import RelatedFieldListFilter


class RelatedOnlyFieldListFilter(RelatedFieldListFilter):
    def __init__(self, field, request, params, model, model_admin, field_path):
        self.request = request
        self.model_admin = model_admin
        super(RelatedOnlyFieldListFilter, self).__init__(field, request, params, model, model_admin, field_path)

    def choices(self, cl):
        limit_choices_to = set(self.model_admin.queryset(self.request).values_list(self.field.name, flat=True))
        self.lookup_choices = [(pk_val, val) for pk_val, val in self.lookup_choices if pk_val in limit_choices_to]
        return super(RelatedOnlyFieldListFilter, self).choices(cl)

使用法:

# myapp/admin.py:

from django.contrib import admin
from myproject.core.admin.filters import RelatedOnlyFieldListFilter
from myproject.myapp.models import MyClass


class MyClassAdmin(admin.ModelAdmin):
    list_filter = (
        ('myfield', RelatedOnlyFieldListFilter),
    )

admin.site.register(MyClass, MyClassAdmin)

後で Django 1.8 に更新する場合は、このインポートを変更するだけで済みます。

from myproject.core.admin.filters import RelatedOnlyFieldListFilter

これに:

from django.contrib.admin.filters import RelatedOnlyFieldListFilter
于 2015-05-07T07:41:36.963 に答える
1

@andi、Django 1.8にこの機能があるという事実を知らせてくれてありがとう。

それがどのように実装され、Django 1.7 で動作する作成されたバージョンに基づいているかを調べました。これは、このフィルターを任意の外部キー フィールドで再利用できるため、以前の回答よりも優れた実装です。Django 1.7 でのみテストされています。以前のバージョンで動作するかどうかは不明です。

これが私の最終的な解決策です:

from django.contrib.admin import RelatedFieldListFilter

class RelatedOnlyFieldListFilter(RelatedFieldListFilter):
    def __init__(self, field, request, params, model, model_admin, field_path):
        super(RelatedOnlyFieldListFilter, self).__init__(
            field, request, params, model, model_admin, field_path)
        qs = field.related_field.model.objects.filter(
            id__in=model_admin.get_queryset(request).values_list(
                field.name, flat=True).distinct())
        self.lookup_choices = [(each.id, unicode(each)) for each in qs]

使用法:

class MyAdmin(admin.ModelAdmin):
    list_filter = (
        ('user', RelatedOnlyFieldListFilter),
        ('category', RelatedOnlyFieldListFilter),
        # ...
    )
于 2015-03-24T13:57:45.860 に答える