Membuat bidang pilihan dinamis

138

Saya mengalami kesulitan mencoba memahami cara membuat bidang pilihan dinamis di django. Saya memiliki model menyiapkan sesuatu seperti:

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

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

Apa yang saya coba lakukan adalah membuat pilihan Bidang yang nilainya adalah titik arah yang terkait dengan pengendara itu (yang akan menjadi orang yang masuk).

Saat ini saya mengganti init dalam formulir saya seperti ini:

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

Tapi yang dilakukan hanyalah mendaftar semua titik jalan, mereka tidak terkait dengan pengendara tertentu. Ada ide? Terima kasih.

apa apa
sumber

Jawaban:

194

Anda dapat memfilter titik lewat dengan meneruskan pengguna ke formulir 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)]
        )

dari pandangan Anda saat memulai formulir lulus pengguna

form = waypointForm(user)

dalam hal bentuk model

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
sumber
20
Gunakan ModelChoiceField apakah itu ModelForm atau bukan - ini juga berfungsi pada formulir normal.
Daniel Roseman
9
Apa yang Anda lakukan ketika Anda ingin mengeluarkan data permintaan? waypointForm (request.POST) tidak akan memvalidasi yang pertama, karena data yang akan divalidasi sudah tidak ada lagi.
Breedly
1
@Ashok Bagaimana cara menggunakan widget CheckboxSelectMultiple dalam hal ini? Untuk modelform khususnya.
wasabigeek
2
Jika Anda ingin menggunakan CheckboxSelectMultiplewidget dengan ini, Anda akan ingin menggunakan MultipleChoiceFieldatau ModelMultipleChoiceField. Pada awalnya, tampaknya berfungsi dengan ChoiceField, tetapi internal akan rusak saat Anda mencoba menyimpan formulir.
coredumperror
1
Terima kasih @CoreDumpError - baru saja memperjuangkan ini selama hampir 2 jam sebelum membaca komentar Anda (doh!) Dan ini akhirnya membuat semuanya bekerja. Yang lainnya, berhati-hatilah dalam merusak pengiriman formulir Anda (masalah unicode) jika Anda berencana menggunakan CheckboxSelectMultiplewidget dengan kode ini. Pastikan untuk mengubah ChoiceFormkeMultipleChoiceForm
sofly
11

Ada solusi bawaan untuk masalah Anda: ModelChoiceField .

Secara umum, itu selalu layak untuk dicoba digunakan ModelFormsaat Anda perlu membuat / mengubah objek database. Berfungsi di 95% kasus dan ini jauh lebih bersih daripada membuat implementasi Anda sendiri.

Alexander Lebedev
sumber
8

masalahnya adalah saat Anda melakukannya

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

dalam permintaan pembaruan, nilai sebelumnya akan hilang!

Liang
sumber
3

Bagaimana jika meneruskan contoh pengendara ke formulir saat memulainya?

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
sumber
2

Di bawah solusi kerja dengan bidang pilihan normal. masalah saya adalah bahwa setiap pengguna memiliki opsi kolom pilihan KUSTOM mereka sendiri berdasarkan beberapa kondisi.

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
sumber
Persis apa yang saya cari! Terima kasih!
Raptor
ini membuat hidup saya lebih mudah .. Terima kasih banyak .. :)
Aravind R Pillai
1

Seperti yang ditunjukkan oleh Breedly dan Liang, solusi Ashok akan mencegah Anda mendapatkan nilai pilihan saat memposting formulir.

Satu cara yang sedikit berbeda, tetapi masih belum sempurna, untuk mengatasinya adalah:

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)

Ini dapat menyebabkan beberapa masalah konkurensi.

Haroldo_OK
sumber
1

Anda dapat mendeklarasikan bidang tersebut sebagai atribut kelas satu dari formulir Anda dan cukup menyetel pilihan secara dinamis:

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

Anda juga dapat menggunakan ModelChoiceField dan menyetel queryset di init dengan cara yang sama.

Zags
sumber
0

Jika Anda membutuhkan bidang pilihan dinamis di django admin; Ini bekerja untuk 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

Semoga ini membantu.

Tobias Ernst
sumber