Crear un campo de elección dinámico

136

Tengo problemas para entender cómo crear un campo de elección dinámico en django. Tengo un modelo configurado como:

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

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

Lo que intento hacer es crear un campo de elección cuyos valores sean los puntos de ruta asociados con ese corredor (que sería la persona que inició sesión).

Actualmente estoy anulando init en mis formularios de esta manera:

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

Pero todo lo que hace es enumerar todos los puntos de referencia, no están asociados con ningún piloto en particular. ¿Algunas ideas? Gracias.

que que
fuente

Respuestas:

191

puede filtrar los puntos de referencia pasando al usuario al formulario 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)]
        )

desde su vista al iniciar el formulario, pase al usuario

form = waypointForm(user)

en caso de forma modelo

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
fuente
20
Use ModelChoiceField, sea o no un ModelForm, también funciona en formularios normales.
Daniel Roseman
9
¿Qué haces cuando quieres obtener los datos de la solicitud? waypointForm (request.POST) no se validará en el primero, porque los datos para validar ya no existen.
Breedly
1
@Ashok ¿Cómo podría usarse el widget CheckboxSelectMultiple en esta instancia? Para la forma modelo especialmente.
wasabigeek
2
Si quieres usar el CheckboxSelectMultiplewidget con esto, querrás usar MultipleChoiceFieldo ModelMultipleChoiceField. Al principio, parece funcionar ChoiceField, pero las partes internas se romperán cuando intentes guardar el formulario.
coredumperror
1
Gracias @CoreDumpError: luché contra esto durante casi 2 horas antes de leer tu comentario (¡sí!) Y esto finalmente hizo que todo funcionara. Otros, tenga cuidado de romper el envío de su formulario (problemas Unicode) si planea usar el CheckboxSelectMultiplewidget con este código. Asegúrese de cambiar ChoiceFormaMultipleChoiceForm
suavemente
11

Hay una solución integrada para su problema: ModelChoiceField .

En general, siempre vale la pena intentar usarlo ModelFormcuando necesita crear / cambiar objetos de base de datos. Funciona en el 95% de los casos y es mucho más limpio que crear su propia implementación.

Alexander Lebedev
fuente
8

el problema es cuando lo haces

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

en una solicitud de actualización, se perderá el valor anterior!

Liang
fuente
3

¿Qué le parece pasar la instancia del piloto al formulario mientras lo inicializa?

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

Debajo solución de trabajo con campo de elección normal. mi problema es que cada usuario tiene sus propias opciones de campo de elección PERSONALIZADAS basadas en pocas condiciones

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
fuente
¡Exactamente lo que estaba buscando! ¡Gracias!
Raptor
esto me facilitó la vida .. Muchas gracias .. :)
Aravind R Pillai
1

Como señalaron Breedly y Liang, la solución de Ashok le impedirá obtener el valor seleccionado al publicar el formulario.

Una forma ligeramente diferente, pero aún imperfecta, de resolver eso sería:

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)

Sin embargo, esto podría causar algunos problemas de concurrencia.

Haroldo_OK
fuente
1

Puede declarar el campo como un atributo de primera clase de su formulario y simplemente establecer opciones dinámicamente:

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

También puede usar un ModelChoiceField y establecer el conjunto de consultas en init de manera similar.

Zags
fuente
0

Si necesita un campo de elección dinámica en django admin; Esto funciona para 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

Espero que esto ayude.

Tobias Ernst
fuente