2016-03-24 19 views
0

FormModelMultipleChoiceFieldです。queryset is generated at Form instanciationです。私はまた、3つの最初の選択肢を最初にチェックしたい。ここに私のコードは次のとおりです。Prefilled ModelMultipleChoiceFieldを使用した重複クエリーを回避する

class DeliverySponsorsRenderer(CheckboxFieldRenderer): 
    outer_html = '<ul{id_attr} class="media-list">{content}</ul>' 
    inner_html = '<li class="media">[...]</li>' 

    def render(self): 
     id_ = self.attrs.get('id') 
     output = [] 
     for i, choice in enumerate(self.choices): 
      choice_value, sponsor = choice 
      widget = self.choice_input_class(self.name, self.value, 
              self.attrs.copy(), choice, i) 
      output.append({ 
       'x': sponsor.x, 'y': sponsor.y, 'z': sponsor.z, ...}) 

     content = format_html_join('\n', self.inner_html, output) 
     # I have my own `format_html_join` function that handles keyword arguments 
     return format_html(self.outer_html, 
          id_attr=format_html(' id="{}"', id_) if id_ else '', 
          content=content) 

class DeliverySponsorsWidget(CheckboxSelectMultiple): 
    renderer = DeliverySponsorsRenderer 

class DeliverySponsorsField(ModelMultipleChoiceField): 
    widget = DeliverySponsorsWidget 

    def label_from_instance(self, obj): 
     return obj 

これは魔法のように動作:

class DeliveryForm(forms.Form): 

    content = forms.CharField(
     label=_("Contenu"), validators=[ 
      MinLengthValidator(20), 
      MaxLengthValidator(5000), 
     ], widget=Wysiwyg) 

    sponsors = DeliverySponsorsField(
     label=_("Commanditaires"), validators=[ 
      MaxLengthValidator(3), 
     ], error_messages={ 
      'max_length': _(
       "Vous ne pouvez pas sélectionner plus de 3 commanditaires."), 
     }, queryset=None) 

    def __init__(self, *args, **kwargs): 
     quote_request = kwargs.pop('quote_request') 
     suitable_sponsors = Sponsor.objects.all().suitable_for_quote_request(
      quote_request) 

     initial = kwargs.pop('initial', None) or {} 
     if 'content' not in initial: 
      initial['content'] = quote_request.description 
     if 'sponsors' not in initial: 
      initial['sponsors'] = suitable_sponsors[:3] 

     kwargs['initial'] = initial 

     super().__init__(*args, **kwargs) 

     self.fields['sponsors'].queryset = suitable_sponsors 

DeliverySponsorsFieldは私が複雑なウィジェットを表示することができますModelMultipleChoiceFieldのサブクラスです。

まあ、正確に次の行は、クエリセット評価されるためではない:

initial['sponsors'] = suitable_sponsors[:3] 

をそしてクエリセットも可能な選択肢を生成するために、後で評価されます。 。

# Replaced 
suitable_sponsors = Sponsor.objects.all().suitable_for_quote_request(
     quote_request) 
# with 
suitable_sponsors = list(
    Sponsor.objects.all().suitable_for_quote_request(quote_request)) 

しかし、ModelMultipleChoiceFieldquerysetQuerySetをされていない文句より正確には、:suitable_sponsors[:3]suitable_sponsorsのサブセットであるため、唯一のクエリは(十分であろうけれども

私はクエリセットの評価を強制しようとしました。それはおよそqueryset.allが未定義されて文句を言う:

File "/home/antoine/.venvs/aladom_v6/lib/python3.4/site-packages/django/forms/widgets.py" in get_renderer 
    763.   choices = list(chain(self.choices, choices)) 

File "/home/antoine/.venvs/aladom_v6/lib/python3.4/site-packages/django/forms/models.py" in __iter__ 
    1105.   queryset = self.queryset.all() 

Exception Type: AttributeError at /admin/quotation/requalification/141369/deliver/ 
Exception Value: 'list' object has no attribute 'all' 

私はONLそれを問い合わせることができながら、私は「簡単に」二度データベースを照会避けることができますこの場合は一度ですか?

+0

余分なクエリでパフォーマンスの問題が発生していない限り、これは時期尚早の最適化のケースかもしれません。 – Alasdair

+0

@Alasdair確かに、私は「シンプルな」ソリューションを求めています。重要なのは、この余分なクエリを避けるために、誇張された機械を作成しないことです。私が簡単にそれを避けることができれば、良い。これはいくつかのエキゾチックな仕事が必要な場合は、私はむしろ余分なクエリを保つだろう。 –

+0

@Antoine: "1インチを出して1マイルくらい"、パフォーマンスに注意を払わなければなりません:)もう一度、いつかあなたに与えなければなりません –

答えて

1

フィールドにクエリセットを提供する代わりに、選択ボックスに値の一覧を入力することができます。

suitable_sponsors = Sponsor.objects.suitable_for_quote_request(quote_request)\ 
    .values_list('id', 'sponsor_name') 

if 'sponsors' not in initial: 
    initial['sponsors'] = [s.id for s in suitable_sponsors[:3]] 

self.fields['sponsors'].choices = suitable_sponsors 

この場合、ModelMultipleChoiceFieldをMultipleChoiceFieldに変更する必要があります。

+0

私は実際に 'ModelMultipleChoiceField'のカスタムサブクラスを使用しています。これにより、スポンサー名だけではなく、より多くのデータを持つ複雑なウィジェットを表示することができます。しかし、これは良いアイデアのように見えます。私は 'ModelMultipleChoiceField'ではなく' MultipleChoiceField'をサブクラス化できると思います。 –

+0

残念ながら、 'MultipleChoiceField'や' TypedMultipleChoiceField'を使うと、私のカスタムフィールドが必要以上に複雑になります。 –

+0

self.fields ['sponsors']を試してみることができますか?widget.choices = suitable_sponsors?私はこれが有効な構文であるかどうかわかりません。 –

関連する問題