2012-08-31 5 views
41

私はこのような二つのモデルがあるDjangoアプリケーションがあります。django管理者のlist_filterに、参照先のForeignKeysのみを表示することはできますか?

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

Country表は〜250個の国が含まれています:MyModelのための管理クラスは次のようになります

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インスタンスによって参照されています。

問題は、django管理者のリストフィルタは、フィルタパネル内のすべての国をリストしていることです。この例では、リストフィルタを持つ目的をほとんど無視しています(インスタンスによって参照される国だけでなく)。

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

答えて

58

Django 1.8では、RelatedOnlyFieldListFilterが組み込まれており、関連する国を表示することができます。 Djangoの1.4から1.7、list_filterについては

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

あなたがSimpleListFilterのサブクラスを使用することができます。必要な値をリストする簡単なリストフィルタを作成することは可能です。

Django 1.3からアップグレードできない場合は、内部で未登録のFilterSpec apiを使用する必要があります。スタックオーバーフローの質問Custom Filter in Django Adminは、正しい方向にあなたを指す必要があります。

+0

返信いただきありがとうございます。近い将来、Django 1.4への移行が予定されていますので、それまでに問題の修正を延期します。 – m000

+1

'1.8'以来... http://stackoverflow.com/a/27836981/953553 – andi

+0

@andiありがとう、私は新しい情報で答えを更新しました – Alasdair

30

私は、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) 

例では、都市と国の2つのモデルが表示されます。市は外国籍を持っています。通常のlist_filter =( 'country'、)を使用すると、すべての国が選択されます。ただし、このスニペットは関連する国のみをフィルタリングします。関連する国は都市と少なくとも1つの関係があります。

元の考えはhereです。著者に大きな感謝。ハードコードされたモデル名の代わりに、model_admin.modelの明快さと使いやすさのためにクラス名が改良されました。私はこのようなdarklowのコード内の検索に変更したい http://djangosnippets.org/snippets/2885/

+0

この例は私が探しているものに非常に近いですが、私はUserProfileモデルのuser_typeという名前のフィールドでUserオブジェクトのlist_filterを追加しようとしています。 私はクラスUserTypeFilter(SimpleListFilter)を定義します。 しかし、私はあなたがqueryset関数のreturn queryset.filter( = self.value())に何を入れているのか分かりません。 –

+0

私の質問への回答はこちら:http://stackoverflow.com/questions/19187027/django-1-4-user-admin-list-filter-using-userprofile-field/19187261?noredirect=1#19187261 –

+0

この偉大な答えの一般化された再利用可能なバージョンが必要です、私の答えは下記を参照してください:http://stackoverflow.com/a/29501136/304209 –

5

ジャンゴスニペットでも利用できる例これは、データベースのためのはるかに優れて

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] 

を;)

18

ジャンゴ1ので、 。8があります:admin.RelatedOnlyFieldListFilter

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

class BookAdmin(admin.ModelAdmin): 
    list_filter = (
     ('author', admin.RelatedOnlyFieldListFilter), 
    ) 
+1

1.8が到着し、この機能が利用可能になりました。 –

+1

@Rrrrrrrrrkは、現在の状態をクリアするために自分の回答を更新しました;) – andi

+0

いくつかのものについては、オブジェクトリストを参照してください。理由は、これはgeodjangoの管理者ではないと思われ、通常の管理者から引き上げると動作しないようです – Chozabu

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), 
     # ... 
    ) 
+0

残念なことに、Django 1.4では動作しません:(''ForeignKey'オブジェクトには 'related_field''属性はありません –

+0

Django 1.6:D –

1

偉大@のdarklowの答えの一般的な再利用可能なバージョン:

def make_RelatedOnlyFieldListFilter(attr_name, filter_title): 

    class RelatedOnlyFieldListFilter(admin.SimpleListFilter): 
     """Filter that shows only referenced options, i.e. options having at least a single object.""" 
     title = filter_title 
     parameter_name = attr_name 

     def lookups(self, request, model_admin): 
      related_objects = set([getattr(obj, attr_name) for obj in model_admin.model.objects.all()]) 
      return [(related_obj.id, unicode(related_obj)) for related_obj in related_objects] 

     def queryset(self, request, queryset): 
      if self.value(): 
       return queryset.filter(**{'%s__id__exact' % attr_name: self.value()}) 
      else: 
       return queryset 

    return RelatedOnlyFieldListFilter 

使用法:

ここ

は私の最終的な解決策であります
2

これは私がDjango 1.4の一般的で再利用可能な実装を取ったものです。それはbuilt-in version that is now part of 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) 

後であなたはこの輸入を変更することができるはずジャンゴ1.8にアップデートした場合:

from myproject.core.admin.filters import RelatedOnlyFieldListFilter 

これまで:

from django.contrib.admin.filters import RelatedOnlyFieldListFilter 
関連する問題