Ed.
Ed.

Reputation: 4577

Catching validation errors in django forms

I have two models:

class Studio(models.Model):
    name = models.CharField("Studio", max_length=30, unique=True)

class Film(models.Model):
    studio = models.ForeignKey(Studio, verbose_name="Studio")
    name = models.CharField("Film Name", max_length=30, unique=True)

I have a Film form that allows the user to either select a preexisting Studio, or type in a new one (with help from an earlier question:

class FilmForm(forms.ModelForm):
    required_css_class = 'required'
    studio = forms.ModelChoiceField(Studio.objects, required=False, widget = SelectWithPlus)
    new_studio = forms.CharField(max_length=30, required=False, label = "New Studio Name", widget = DeSelectWithX(attrs={'class' : 'hidden_studio_field'}))

    def __init__(self, *args, **kwargs):
        super(FilmForm, self).__init__(*args,**kwargs)
        self.fields['studio'].required = False

    def clean(self):
        cleaned_data = self.cleaned_data
        studio = cleaned_data.get('studio')
        new_studio = cleaned_data.get('new_studio')

        if not studio and not new_studio:
            raise forms.ValidationError("Must specify either Studio or New Studio!")
        elif not studio:
            studio, created = Studio.objects.get_or_create(name = new_studio)
            self.cleaned_data['studio'] = studio

        return super(FilmForm,self).clean()

    class Meta:
        model = Film

Now, my first issue is that when both studio and new_studio are missing I get a django ValueError: Cannot assign None: "Film.studio" does not allow null values error. I thought I was capturing all the errors, thus django should never get so far as to realize Film.studio is empty.

A second issue is an order of operations. What if I want to only save the new_studio after I'm sure the rest of the FilmForm is valid (thus preventing a bunch of studio names getting saved before full Film entries go through as well)? Am I in the clear or do I risk premature saving because new_studio's are saved in the form's cleaning?

Edit: Added Traceback and edited validation if-statements

Upvotes: 2

Views: 5541

Answers (2)

Ed.
Ed.

Reputation: 4577

FYI on preventing the presaving of new_studio:

New def clean in form: def clean(self): cleaned_data = self.cleaned_data studio = cleaned_data.get('studio') new_studio = cleaned_data.get('new_studio')

    if not studio and not new_studio:
        del cleaned_data['studio'], cleaned_data['new_studio']
        raise forms.ValidationError("Must specify either Studio or New Studio!")
    elif not studio:
        del cleaned_data['studio']

    return super(FilmForm,self).clean()

And in the view:

def testone(request):
    if request.method == 'POST': # If the form has been submitted...
        form = FilmForm(request.POST) # A form bound to the POST data

        if form.is_valid(): # All validation rules pass
            if form.cleaned_data['new_studio']:
                studio, created = Studio.objects.get_or_create(name = form.cleaned_data['new_studio'])
                new_film = form.save(commit=False)
                new_film.studio = studio
            else:
                new_film = form

            new_film.save()
            return HttpResponseRedirect('/') # Redirect after POST
        else:
            form = FilmForm() # An unbound form

        return render_to_response('testone.html', {
            'form': form
        }, context_instance=RequestContext(request))

Upvotes: 1

rockingskier
rockingskier

Reputation: 9346

Delete studio and new_studio from cleaned_data.

    if not studio and not new_studio:
        del cleaned_data['studio'], cleaned_data['new_studio']
        raise forms.ValidationError("Must specify either Studio or New Studio!")

Upvotes: 2

Related Questions