2010-11-24 16 views
12

モデルフィールドの比較に基づいて管理者でフィルタリングする方法を知っていますか?F()式を使用したDjango管理フィルタ

のは、我々がモデル次ていると仮定しましょう:今すぐ

class Transport(models.Model): 
    start_area = models.ForeignKey(Area, related_name='starting_transports') 
    finish_area = models.ForeignKey(Area, related_name='finishing_transports') 

、私は何をしたいので-分野であるイン・エリアおよびトランスエリアオブジェクトのフィルタリングが可能になり、管理フィルタを作ることですstart_areaとfinish_areaが同じで、trans-areaが他のものです。

私はカスタムFilterSpecにを作成することによって、これを達成しようとしたが、2つの問題があります。

  • FilterSpecには、唯一のフィールドにバインドされています。
  • FilterSpecはF()式をサポートせず、除外します。

第2の問題は、カスタムChangeListクラスを定義することで解決できますが、最初の問題を解決する方法はありません。

また、私は、クエリーセットメソッドをオーバーロードし、余分なコンテキストをチェンジリストテンプレートに送信することでModelAdminインスタンスでフィルタを直接エミュレートしようとしました。この場合、フィルタ自体はハードコードされ、手動で印刷されます。残念ながら、DjangoはModelAdminインスタンスにはわかっていないので、(フィルタリンクで使用される)GETパラメータを取り出し、代わりに、何らかのエラーを通知するはずの?e = 1だけを入れるという問題があるようです。

ありがとうございます。

EDIT:これは、次のDjangoリリースのために予定されている機能だと思われます。http://code.djangoproject.com/ticket/5833を参照してください。それでも、誰かがDjango 1.2でそれを達成するための手がかりを持っていますか?

+0

「トランスエリア」とは何ですか? :-) –

+0

何も特別な;)start_areaがfinish_areaと異なるdatabseのTransportオブジェクトのすべてのインスタンスであるはずです。 – xaralis

+1

まあ、誰も答えがないようです。私がこれまで思いついた唯一の解決策は、トランスポートモデルが別のフィールドをトランスポートモデルに追加して、トランスポートがインエリアまたはトランスエリアの場合に情報を保持するために更新されます:(これらの冗長フィールドは嫌いです:( – xaralis

答えて

1

解決策には、FilterSpecを追加すること、および独自のChangeListを実装することが含まれます。フィルター名が検証されると、モデル・フィールド名でフィルターに名前を付ける必要があります。下に、同じフィールドにデフォルトのフィルタを使用できるハックが表示されます。

標準のFilterSpecの前にFilterSpecを追加します。

以下は、Django 1で動作する実装です。3

from django.contrib.admin.views.main import * 
from django.contrib import admin 
from django.db.models.fields import Field 
from django.contrib.admin.filterspecs import FilterSpec 
from django.db.models import F 
from models import Transport, Area 
from django.contrib.admin.util import get_fields_from_path 
from django.utils.translation import ugettext as _ 


# Our filter spec 
class InAreaFilterSpec(FilterSpec): 

    def __init__(self, f, request, params, model, model_admin, field_path=None): 
     super(InAreaFilterSpec, self).__init__(
      f, request, params, model, model_admin, field_path=field_path) 
     self.lookup_val = request.GET.get('in_area', None) 

    def title(self): 
     return 'Area' 

    def choices(self, cl): 
     del self.field._in_area 
     yield {'selected': self.lookup_val is None, 
       'query_string': cl.get_query_string({}, ['in_area']), 
       'display': _('All')} 
     for pk_val, val in (('1', 'In Area'), ('0', 'Trans Area')): 
      yield {'selected': self.lookup_val == pk_val, 
        'query_string': cl.get_query_string({'in_area' : pk_val}), 
        'display': val} 

    def filter(self, params, qs): 
     if 'in_area' in params: 
      if params['in_area'] == '1': 
       qs = qs.filter(start_area=F('finish_area')) 
      else: 
       qs = qs.exclude(start_area=F('finish_area')) 
      del params['in_area'] 
     return qs 

def in_area_test(field): 
    # doing this so standard filters can be added with the same name 
    if field.name == 'start_area' and not hasattr(field, '_in_area'): 
     field._in_area = True 
     return True  
    return False 

# we add our special filter before standard ones 
FilterSpec.filter_specs.insert(0, (in_area_test, InAreaFilterSpec)) 


# Defining my own change list for transport 
class TransportChangeList(ChangeList): 

    # Here we are doing our own initialization so the filters 
    # are initialized when we request the data 
    def __init__(self, request, model, list_display, list_display_links, list_filter, date_hierarchy, search_fields, list_select_related, list_per_page, list_editable, model_admin): 
     #super(TransportChangeList, self).__init__(request, model, list_display, list_display_links, list_filter, date_hierarchy, search_fields, list_select_related, list_per_page, list_editable, model_admin) 
     self.model = model 
     self.opts = model._meta 
     self.lookup_opts = self.opts 
     self.root_query_set = model_admin.queryset(request) 
     self.list_display = list_display 
     self.list_display_links = list_display_links 
     self.list_filter = list_filter 
     self.date_hierarchy = date_hierarchy 
     self.search_fields = search_fields 
     self.list_select_related = list_select_related 
     self.list_per_page = list_per_page 
     self.model_admin = model_admin 

     # Get search parameters from the query string. 
     try: 
      self.page_num = int(request.GET.get(PAGE_VAR, 0)) 
     except ValueError: 
      self.page_num = 0 
     self.show_all = ALL_VAR in request.GET 
     self.is_popup = IS_POPUP_VAR in request.GET 
     self.to_field = request.GET.get(TO_FIELD_VAR) 
     self.params = dict(request.GET.items()) 
     if PAGE_VAR in self.params: 
      del self.params[PAGE_VAR] 
     if TO_FIELD_VAR in self.params: 
      del self.params[TO_FIELD_VAR] 
     if ERROR_FLAG in self.params: 
      del self.params[ERROR_FLAG] 

     if self.is_popup: 
      self.list_editable =() 
     else: 
      self.list_editable = list_editable 
     self.order_field, self.order_type = self.get_ordering() 
     self.query = request.GET.get(SEARCH_VAR, '') 
     self.filter_specs, self.has_filters = self.get_filters(request) 
     self.query_set = self.get_query_set() 
     self.get_results(request) 
     self.title = (self.is_popup and ugettext('Select %s') % force_unicode(self.opts.verbose_name) or ugettext('Select %s to change') % force_unicode(self.opts.verbose_name)) 
     self.pk_attname = self.lookup_opts.pk.attname 


    # To be able to do our own filter, 
    # we need to override this 
    def get_query_set(self): 

     qs = self.root_query_set 
     params = self.params.copy() 

     # now we pass the parameters and the query set 
     # to each filter spec that may change it 
     # The filter MUST delete a parameter that it uses 
     if self.has_filters: 
      for filter_spec in self.filter_specs: 
       if hasattr(filter_spec, 'filter'): 
        qs = filter_spec.filter(params, qs) 

     # Now we call the parent get_query_set() 
     # method to apply subsequent filters 
     sav_qs = self.root_query_set 
     sav_params = self.params 

     self.root_query_set = qs 
     self.params = params 

     qs = super(TransportChangeList, self).get_query_set() 

     self.root_query_set = sav_qs 
     self.params = sav_params 

     return qs 


class TransportAdmin(admin.ModelAdmin): 
    list_filter = ('start_area','start_area') 

    def get_changelist(self, request, **kwargs): 
     """ 
     Overriden from ModelAdmin 
     """ 
     return TransportChangeList 


admin.site.register(Transport, TransportAdmin) 
admin.site.register(Area) 
+0

残念ながら、これは不要なので、1つの冗長なフィールドを追加する方法を選択することになりました。うまくいけば、djangoの開発者は次のリリースでこれを修正します(http://code.djangoproject.com/ticket/5833)。 – xaralis

3

それが最善の方法*ではないのですが、それは残念ながら、FilterSpecsは非常にジャンゴに現在制限されてい

class TransportForm(forms.ModelForm): 
    transports = Transport.objects.all() 
    list = [] 
    for t in transports: 
     if t.start_area.pk == t.finish_area.pk: 
      list.append(t.pk) 
    select = forms.ModelChoiceField(queryset=Page.objects.filter(pk__in=list)) 

    class Meta: 
     model = Transport 
+0

はいこれは解決策のようです – KronnorK

0

を動作するはずです。単に、カスタマイズを念頭に置いて作成されたものではありません。

しかし、ありがたいことに、多くの人がFilterSpecへのパッチを取り組んでいます。それは1.3マイルストーンを欠場しましたが、最終的にはトランクに入っているように見え、次のリリースで打つべきです。

#5833 (Custom FilterSpecs)

あなたがトランクにあなたのプロジェクトを実行したい場合、あなたは今、それを活用することもできますし、現在のインストールにパッチを適用することができるかもしれません。さもなければ、あなたは待たなければならないが、少なくともそれはすぐに来るだろう。

関連する問題