Ich habe eine Form
mit einer ModelMultipleChoiceField
welche queryset is generated at Form instanciation. Ich möchte auch, dass die ersten drei Optionen zunächst überprüft werden. Hier ist mein Code:Vermeiden Sie doppelte Abfrage mit vorgefüllten ModelMultipleChoiceField
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
ist eine Unterklasse von ModelMultipleChoiceField
, die mir einen komplexen Widget angezeigt werden kann:
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
Dies funktioniert wie ein Charme.
Nun, nicht ganz, weil die folgenden Zeilen wertet die queryset:
initial['sponsors'] = suitable_sponsors[:3]
Und die queryset auch danach bewertet wird, um die Wahlmöglichkeiten zu generieren. . Obwohl nur eine Abfrage ausreichen würde (da suitable_sponsors[:3]
eine Teilmenge von suitable_sponsors
ist
habe ich versucht, mit den queryset Auswertung zu erzwingen:
# 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))
jedoch ModelMultipleChoiceField
beschwert sich über queryset
keine QuerySet
sein Genauer gesagt. es beschwert sich über queryset.all
undefiniert:
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'
Kann ich „leicht“ vermeiden abfragen der Datenbank zweimal während ich es onl nicht abfragen y einmal für diesen Fall?
Wenn die zusätzliche Abfrage keine Leistungsprobleme verursacht, kann dies eine vorzeitige Optimierung sein. – Alasdair
@Alasdair Sicher, deshalb bitte ich um eine "einfache" Lösung. Es geht nicht darum, eine übertriebene Maschinerie zu erstellen, um diese zusätzliche Abfrage zu vermeiden. Wenn ich es leicht vermeiden kann, gut. Wenn dies etwas exotische Arbeit erfordert, würde ich lieber die zusätzliche Abfrage behalten. –
@Antoine: "Geben Sie einen Zoll und eine Meile", müssen Sie wachsam sein mit der Leistung :) dann wieder einmal müssen Sie geben –