dotty
dotty

Reputation: 41433

make ChoiceField() take any value

I'm using the forms framework and I'm using a but of javascript magic to inject some values on the fly into a select box (the values of this select box relies on a value form another).

Basically you have Team and Players. You need to define a player, but the players data comes from a Team.

In my form class i have

team = forms.ModelChoiceField(queryset=Team.objects.all()
player = forms.ChoiceField()

In the front end when the user selects a team from the select box, the (empty) players boxes gets correctly updated with new data. However when i hit submit, the player select box throws a error Select a valid choice. 2 is not one of the available choices. 2 IS a valid choice however.

Is there any way around this? Can i make the ChoiceField() take any value?

Upvotes: 1

Views: 2520

Answers (4)

Sachin Roy
Sachin Roy

Reputation: 1

You can use the data parameter passed to init function to update choices dynamically.

class PlayerTeamForm(forms.ModelForm):
    team = forms.ModelChoiceField(queryset=Team.objects.all()
    player = forms.ChoiceField(choices = [])
    class Meta:
        model = PlayerTeamModel
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if kwargs.get('data'): # When form is submitted
            # prefix for formsets
            prefix = kwargs.get('prefix') + '-' if kwargs.get('prefix') else ''
            player = Player.objects.get(
                pk = kwargs.get('data').get(prefix + 'player')
            )
            self.fields['player'].choices = [[
                player.pk, player.name
            ]]
        else:
            self.fields['player'].choices = [[
                self.instance.player_id, self.instance.player.name
            ]]

Upvotes: 0

AdonisN
AdonisN

Reputation: 489

You can define the field as a CharField with forms.Select as the widget. That way the field is rendered as an empty select element and will accept any value.

player = forms.CharField(widget=forms.Select)

this is useful if you're using Select2 and loading values via ajax.

Upvotes: 3

rajasaur
rajasaur

Reputation: 5450

You could create your own ChoiceField, say DynamicChoiceField that extends from ChoiceField. You can then override the "clean" method to not do any validation as it would do with a regular ChoiceField.

class DynamicChoiceField(forms.ChoiceField):     
    def clean(self, value):
        // Do whatever validation needs to happen

In the form, you could use DynamicChoiceField instead of ChoiceField

Upvotes: 1

DrTyrsa
DrTyrsa

Reputation: 31951

Ordered by complexity DESC:

  1. Prepopulate it with all possible choices. For example, if you select player's id, it will be choices = Player.objects.values_list('id', flat=True)
  2. Define it as CharField, but render it manually in template as select box.
  3. Don't define it in form, but pass it's value to form's constructor (which you need to override to process this value.)

Upvotes: 3

Related Questions