Erstellen eines dynamischen Auswahlfelds

136

Ich habe einige Probleme beim Versuch zu verstehen, wie ein dynamisches Auswahlfeld in Django erstellt wird. Ich habe ein Modell eingerichtet wie:

class rider(models.Model):
     user = models.ForeignKey(User)
     waypoint = models.ManyToManyField(Waypoint)

class Waypoint(models.Model):
     lat = models.FloatField()
     lng = models.FloatField()

Ich versuche, ein Auswahlfeld zu erstellen, dessen Werte die Wegpunkte sind, die diesem Fahrer zugeordnet sind (dies wäre die angemeldete Person).

Derzeit überschreibe ich init in meinen Formularen wie folgt:

class waypointForm(forms.Form):
     def __init__(self, *args, **kwargs):
          super(joinTripForm, self).__init__(*args, **kwargs)
          self.fields['waypoints'] = forms.ChoiceField(choices=[ (o.id, str(o)) for o in Waypoint.objects.all()])

Aber alles, was Sie tun müssen, ist, alle Wegpunkte aufzulisten. Sie sind keinem bestimmten Fahrer zugeordnet. Irgendwelche Ideen? Vielen Dank.

was was
quelle

Antworten:

191

Sie können die Wegpunkte filtern, indem Sie den Benutzer an das Formular init übergeben

class waypointForm(forms.Form):
    def __init__(self, user, *args, **kwargs):
        super(waypointForm, self).__init__(*args, **kwargs)
        self.fields['waypoints'] = forms.ChoiceField(
            choices=[(o.id, str(o)) for o in Waypoint.objects.filter(user=user)]
        )

aus Ihrer Sicht beim Initiieren des Formulars den Benutzer übergeben

form = waypointForm(user)

im Falle einer Modellform

class waypointForm(forms.ModelForm):
    def __init__(self, user, *args, **kwargs):
        super(waypointForm, self).__init__(*args, **kwargs)
        self.fields['waypoints'] = forms.ModelChoiceField(
            queryset=Waypoint.objects.filter(user=user)
        )

    class Meta:
        model = Waypoint
Ashok
quelle
20
Verwenden Sie ModelChoiceField, unabhängig davon, ob es sich um ein ModelForm handelt oder nicht - es funktioniert auch in normalen Formularen.
Daniel Roseman
9
Was tun Sie, wenn Sie die Anforderungsdaten herausholen möchten? waypointForm (request.POST) wird im ersten nicht validiert, da die zu validierenden Daten nicht mehr vorhanden sind.
Breedly
1
@Ashok Wie könnte das CheckboxSelectMultiple-Widget in dieser Instanz verwendet werden? Besonders für die Modellform.
Wasabigeek
2
Wenn Sie das CheckboxSelectMultipleWidget damit verwenden möchten, möchten Sie MultipleChoiceFieldoder verwenden ModelMultipleChoiceField. Zuerst scheint es zu funktionieren ChoiceField, aber die Interna brechen, wenn Sie versuchen, das Formular zu speichern.
Coredumperror
1
Vielen Dank an @CoreDumpError - habe gerade 2 Stunden lang gekämpft, bevor ich deinen Kommentar gelesen habe (doh!) Und das hat endlich alles zum Laufen gebracht. Andere sollten sich davor hüten, Ihre Formularübermittlung zu unterbrechen (Unicode-Probleme), wenn Sie das CheckboxSelectMultipleWidget mit diesem Code verwenden möchten. ChoiceFormMultipleChoiceForm
Stellen
11

Es gibt eine integrierte Lösung für Ihr Problem: ModelChoiceField .

Im Allgemeinen lohnt es sich immer, zu versuchen, ModelFormwenn Sie Datenbankobjekte erstellen / ändern müssen. Funktioniert in 95% der Fälle und ist viel sauberer als das Erstellen einer eigenen Implementierung.

Alexander Lebedev
quelle
8

Das Problem ist, wenn Sie es tun

def __init__(self, user, *args, **kwargs):
    super(waypointForm, self).__init__(*args, **kwargs)
    self.fields['waypoints'] = forms.ChoiceField(choices=[ (o.id, str(o)) for o in Waypoint.objects.filter(user=user)])

Bei einer Update-Anfrage geht der vorherige Wert verloren!

Liang
quelle
3

Wie wäre es, wenn Sie die Rider-Instanz beim Initialisieren an das Formular übergeben?

class WaypointForm(forms.Form):
    def __init__(self, rider, *args, **kwargs):
      super(joinTripForm, self).__init__(*args, **kwargs)
      qs = rider.Waypoint_set.all()
      self.fields['waypoints'] = forms.ChoiceField(choices=[(o.id, str(o)) for o in qs])

# In view:
rider = request.user
form = WaypointForm(rider) 
Manoj Govindan
quelle
2

Unter Arbeitslösung mit normalem Auswahlfeld. Mein Problem war, dass jeder Benutzer seine eigenen CUSTOM-Auswahlfeldoptionen hat, die auf wenigen Bedingungen basieren.

class SupportForm(BaseForm):

    affiliated = ChoiceField(required=False, label='Fieldname', choices=[], widget=Select(attrs={'onchange': 'sysAdminCheck();'}))

    def __init__(self, *args, **kwargs):

        self.request = kwargs.pop('request', None)
        grid_id = get_user_from_request(self.request)
        for l in get_all_choices().filter(user=user_id):
            admin = 'y' if l in self.core else 'n'
            choice = (('%s_%s' % (l.name, admin)), ('%s' % l.name))
            self.affiliated_choices.append(choice)
        super(SupportForm, self).__init__(*args, **kwargs)
        self.fields['affiliated'].choices = self.affiliated_choice
Deil
quelle
Genau das, wonach ich gesucht habe! Vielen Dank!
Raptor
Das hat mir das Leben leichter gemacht. Vielen Dank. :)
Aravind R Pillai
1

Wie von Breedly und Liang ausgeführt, verhindert die Lösung von Ashok, dass Sie beim Senden des Formulars den ausgewählten Wert erhalten.

Ein etwas anderer, aber immer noch unvollkommener Weg, dies zu lösen, wäre:

class waypointForm(forms.Form):
    def __init__(self, user, *args, **kwargs):
        self.base_fields['waypoints'].choices = self._do_the_choicy_thing()
        super(waypointForm, self).__init__(*args, **kwargs)

Dies kann jedoch zu Übereinstimmungsproblemen führen.

Haroldo_OK
quelle
1

Sie können das Feld als erstklassiges Attribut Ihres Formulars deklarieren und die Auswahl einfach dynamisch festlegen:

class WaypointForm(forms.Form):
    waypoints = forms.ChoiceField(choices=[])

    def __init__(self, user, *args, **kwargs):
        super().__init__(*args, **kwargs)
        waypoint_choices = [(o.id, str(o)) for o in Waypoint.objects.filter(user=user)]
        self.fields['waypoints'].choices = waypoint_choices

Sie können auch ein ModelChoiceField verwenden und das Abfrageset auf ähnliche Weise auf init setzen.

Zags
quelle
0

Wenn Sie ein dynamisches Auswahlfeld in django admin benötigen; Dies funktioniert für django> = 2.1.

class CarAdminForm(forms.ModelForm):
    class Meta:
        model = Car

    def __init__(self, *args, **kwargs):
        super(CarForm, self).__init__(*args, **kwargs)

        # Now you can make it dynamic.
        choices = (
            ('audi', 'Audi'),
            ('tesla', 'Tesla')
        )

        self.fields.get('car_field').choices = choices

    car_field = forms.ChoiceField(choices=[])

@admin.register(Car)
class CarAdmin(admin.ModelAdmin):
    form = CarAdminForm

Hoffe das hilft.

Tobias Ernst
quelle