Tworzenie dynamicznego pola wyboru

136

Mam pewne problemy ze zrozumieniem, jak utworzyć dynamiczne pole wyboru w django. Mam ustawiony model coś takiego:

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

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

To, co próbuję zrobić, to stworzyć pole wyboru, którego wartości są punktami drogi skojarzonymi z tym jeźdźcem (którym byłaby zalogowana osoba).

Obecnie nadpisuję init w moich formularzach:

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()])

Ale wszystko, co robi, to lista wszystkich punktów trasy, nie są one powiązane z żadnym konkretnym jeźdźcem. Jakieś pomysły? Dzięki.

co co
źródło

Odpowiedzi:

191

możesz filtrować punkty na drodze, przekazując użytkownika do formularza init

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)]
        )

z Twojego widoku podczas inicjowania formularza przekazać użytkownikowi

form = waypointForm(user)

w przypadku formy wzorcowej

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
źródło
20
Użyj ModelChoiceField bez względu na to, czy jest to ModelForm - działa również na zwykłych formularzach.
Daniel Roseman
9
Co jednak robisz, gdy chcesz uzyskać dane z żądania? waypointForm (request.POST) nie będzie walidować w pierwszym, ponieważ nie ma już danych do sprawdzenia.
Breedly
1
@Ashok W jaki sposób można użyć widżetu CheckboxSelectMultiple w tym przypadku? Szczególnie dla formy modelowej.
wasabigeek
2
Jeśli chcesz używać CheckboxSelectMultiplewidżetu z tym, będziesz chciał użyć MultipleChoiceFieldlub ModelMultipleChoiceField. Na początku wygląda na to, że działa ChoiceField, ale wewnętrzne elementy pękną, gdy spróbujesz zapisać formularz.
coredumperror
1
Dziękuję @CoreDumpError - walczyłem z tym przez prawie 2 godziny, zanim przeczytałem Twój komentarz (doh!) I to w końcu sprawiło, że wszystko działało. Inni, uważaj na przerwanie przesyłania formularza (problemy CheckboxSelectMultiplez kodem Unicode), jeśli planujesz używać widżetu z tym kodem. Pamiętaj, aby zmienić ChoiceFormnaMultipleChoiceForm
łagodnie
11

Istnieje wbudowane rozwiązanie problemu: ModelChoiceField .

Ogólnie rzecz biorąc, zawsze warto spróbować użyć, ModelFormgdy musisz stworzyć / zmienić obiekty bazy danych. Działa w 95% przypadków i jest znacznie czystszy niż tworzenie własnej implementacji.

Alexander Lebedev
źródło
8

problem pojawia się, kiedy to zrobisz

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)])

w żądaniu aktualizacji poprzednia wartość zostanie utracona!

Liang
źródło
3

Co powiesz na przekazanie instancji rider do formularza podczas inicjalizacji?

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
źródło
2

Pod rozwiązaniem roboczym z normalnym polem wyboru. mój problem polegał na tym, że każdy użytkownik miał własne NIESTANDARDOWE opcje pola wyboru w oparciu o kilka warunków.

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
źródło
Dokładnie to, czego szukałem! Dzięki!
Raptor,
to ułatwiło mi życie .. Wielkie dzięki .. :)
Aravind R Pillai
1

Jak zauważyli Breedly i Liang, rozwiązanie Ashoka uniemożliwi uzyskanie wybranej wartości podczas wysyłania formularza.

Trochę inny, ale wciąż niedoskonały sposób rozwiązania tego problemu to:

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)

Może to jednak powodować pewne problemy ze współbieżnością.

Haroldo_OK
źródło
1

Możesz zadeklarować pole jako atrybut pierwszej klasy swojego formularza i po prostu dynamicznie ustawiać opcje:

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

Możesz również użyć ModelChoiceField i ustawić zestaw zapytań na init w podobny sposób.

Zags
źródło
0

Jeśli potrzebujesz dynamicznego pola wyboru w panelu django; Działa to dla 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

Mam nadzieję że to pomoże.

Tobias Ernst
źródło