I'm having some trouble trying to understand how to create a dynamic choice field in django. I have a model set up something like:
class rider(models.Model):
user = models.ForeignKey(User)
waypoint = models.ManyToManyField(Waypoint)
class Waypoint(models.Model):
lat = models.FloatField()
lng = models.FloatField()
What I'm trying to do is create a choice Field whos values are the waypoints associated with that rider (which would be the person logged in).
Currently I'm overriding init in my forms like so:
class waypointForm(forms.Form):
def __init__(self, *args, **kwargs):
super(joinTripForm, self).__init__(*args, **kwargs)
self.fields['waypoints'] = forms.Choi开发者_如何转开发ceField(choices=[ (o.id, str(o)) for o in Waypoint.objects.all()])
But all that does is list all the waypoints, they're not associated with any particular rider. Any ideas? Thanks.
you can filter the waypoints by passing the user to the form 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)]
)
from your view while initiating the form pass the user
form = waypointForm(user)
in case of model form
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
There's built-in solution for your problem: ModelChoiceField.
Generally, it's always worth trying to use ModelForm
when you need to create/change database objects. Works in 95% of the cases and it's much cleaner than creating your own implementation.
the problem is when you do
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)])
in a update request, the previous value will lost!
You can declare the field as a first-class attribute of your form and just set choices dynamically in __init__
:
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
This approach also works with a ModelChoiceField.
This approach is superior if you are using a ModelForm, and want to override choices of an autogenerated field.
How about passing the rider instance to the form while initializing it?
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)
If you need a dynamic choice field in django admin; This works for 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
Hope this helps.
Underneath working solution with normal choice field. my problem was that each user have their own CUSTOM choicefield options based on few conditions.
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
As pointed by Breedly and Liang, Ashok's solution will prevent you from getting the select value when posting the form.
One slightly different, but still imperfect, way to solve that would be:
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)
This could cause some concurrence problems, though.
精彩评论