views:

355

answers:

3

I have a model like:

CAMPAIGN_TYPES = (
                  ('email','Email'),
                  ('display','Display'),
                  ('search','Search'),
                  )

class Campaign(models.Model):
    name = models.CharField(max_length=255)
    type = models.CharField(max_length=30,choices=CAMPAIGN_TYPES,default='display')

And a form:

class CampaignForm(ModelForm):
    class Meta:
        model = Campaign

Is there a way to limit what choices are available for the 'type' field? I know for a single value field I can do: CampaignForm(initial={'name':'Default Name'}) but I can't find any way to do this for a choice set.

+1  A: 

Choices are for lists only, not CharFields. What you need to do is create a custom validator on clean().

in forms.py

CAMPAIGN_TYPES = ('email', 'display', 'search')

# this would be derived from your Campaign modelform
class EnhancedCampaignForm(CampaignForm):
    # override clean_FIELD
    def clean_type(self):
        cleaned_data = self.cleaned_data
        campaign_type = cleaned_data.get("type")

        # strip whitespace and lowercase the field string for better matching
        campaign_type = campaign_type.strip().lower()

        # ensure the field string matches a CAMPAIGN_TYPE, otherwise 
        # raise an exception so validation fails
        if not campaign_type in CAMPAIGN_TYPE:
            raise forms.ValidationError("Not a valid campaign type.")

        # if everything worked, return the field's original value
        return cleaned_data
Soviut
I'm not sure how this works exactly. I'm trying to get just 'email' and 'display' showing on the form's select list.
Adam Nelson
A: 

It seems that this is the best way to do it by overriding the 'type' field:

class CampaignForm(ModelForm):
    type = forms.ModelChoiceField(queryset=OtherModel.objects.filter(type__id=1))
    class Meta:
        model = Campaign

I'm not sure right now how to pass '1' but this will be good enough even if it needs to be hardcoded. Plus, it lets Django do most of the heavy lifting.

@soviut I will change the field name to a non-reserved word. Thanks for the heads up.

Adam Nelson
Any help on passing a variable to type__id would be greatly appreciated. While inside the form, there is no 'self'. I don't know what is accessible in there.
Adam Nelson
This can be done at instantiation time with:form = CampaignForm()form.fields['type'].choices = TestModel.objects.\ filter(filtercriterion__id=1).values_list('id','name')
Adam Nelson
This page really helped out: http://excess.org/article/2009/07/django-forms-quick-reference/
Adam Nelson
+1  A: 

This is how I limited the options displayed:

In forms.py add an init method for your form

class TaskForm(forms.ModelForm):
    ....

def __init__(self, user, *args, **kwargs):  
    '''
    limit the choice of owner to the currently logged in users hats
    '''

    super(TaskForm, self).__init__(*args, **kwargs)  

    # get different list of choices here
    choices = Who.objects.filter(owner=user).values_list('id','name')
    self.fields["owner"].choices = choices