Chris Stoll
Chris Stoll

Reputation: 35

Django Conditional Choices

I am new to Django and have been working on a project where I am trying to have choice fields dependent on bool values attached to the current user. I have this in the model

user = models.OneToOneField(user_model, on_delete=models.CASCADE)
####
I_CAT = models.BooleanField(default=False)
I_DOG = models.BooleanField(default=False)
I_PAW = models.BooleanField(default=False)

I_OPTIONS = [
    [('star'), ('Star')],
    When(I_CAT=True, then=Value([('cat'), ('Cat')])),
]



icon = models.TextField(choices=I_OPTIONS, default='star')

I am looking to have it so that when the value I_CAT for the current user is set to True it will add that option to our choices list which is used as choices list for icon. I don't really know the best way to approach this. I have tried to use When and if statements in the model file itself and also in the views for this page. I haven't managed to get anything to work yet the closest I got was this

if I_CAT:
    I_OPTIONS.append([('cat'), ('Cat')])

This succeeds in adding something to the options list but it doesn't actually check if I_CAT is true or not and just adds it every time no matter what. I also read on another post that if statements inside a model are a bad idea so idk. Any guidance would be appreciated!

EDIT: I added this code to the Form as suggested by Brobin. I had to add an additional if statement to keep the option from being re-added every time the page was refreshed. Everything is now working as intended!

    def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    cat = [('cat'), ('Cat')]
    dog = [('dog'), ('Dog')] 
    paw = [('paw'), ('Paw')]
    choices = [
        [('star'), ('Star')],
    ]
    if self.instance.I_CAT:
        if cat not in choices:
            choices.append(cat)
    if self.instance.I_DOG:
        if dog not in choices:
            choices.append(dog)
    if self.instance.I_PAW:
        if paw not in choices:
            choices.append(paw)
    self.fields['icon'].choices = choices  

icon = forms.ChoiceField(required=True)

Upvotes: 0

Views: 3063

Answers (1)

Brobin
Brobin

Reputation: 3326

I would rethink the models, so that the form is dynamic and based on a query. Something like this. This has the added bonus of not needing to add more booleans for new icons.

class Icon(models.Model):
    code = models.CharField(max_length=16)
    name = models.CharField(max_length=16)


class Profile(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL)
    icons_unlocked = models.ManyToManyField(Icon)
    icon = mdoels.ForeignKey(Icon)

Then in your form, you can set the queryset choices for the field based on what the user has available.


class IconSelectForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['icon'].queryset = self.instance.icons_unlocked.all()

    class Meta:
        model = Profile
        fields = ['icon']

If you don't want to change the models, you could do a similar thing with your booleans in the form

class IconSelectForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        choices = []
        if self.instance.I_CAT:
            choices.append(('cat', 'Cat'))
        self.fields['icon'].choices = choices

    class Meta:
        model = Profile
        fields = ['icon']

Upvotes: 1

Related Questions