2

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

8
  • Check that this line does what you mean: if studio is None and new_studio == '':. Could studio be '' or new_studio be None? Does it correctly show the form error if you change that to if not (studio or new_studio):? Commented Jan 25, 2012 at 22:08
  • I put a print "here" right before the ValidationError. It came out in the terminal, so I can confirm that the code is reaching that portion. I tried changing the criteria as you suggested and got the same error Commented Jan 25, 2012 at 22:21
  • Can you pastebin the traceback? Commented Jan 25, 2012 at 22:25
  • Why did you change the if and elif checks? The one's your using now open up holes in the logic that allows certain scenarios to fall through unchecked. Should've stuck to what I gave you. Additionally, the clean method is only ran after the individual clean_FIELD methods are run, so effectively, you only reach this point if everything else is valid. You don't have to worry about Studios getting created when there's other problems in the form still. Commented Jan 25, 2012 at 22:41
  • -ChrisPratt: Fair point. The first few times I ran it, I couldn't get the instances to work correctly. Was probably user error. Edited above, but I still have the same issue Commented Jan 25, 2012 at 23:16

2 Answers 2

2

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!")
Sign up to request clarification or add additional context in comments.

2 Comments

Excellent. This works. Any input on how to prevent new_studio being created before the rest of the form is fully validated? If I enter a new_studio, but omit film name, the form doesn't go through because name is required, but the new_studio gets created anyway.
You could change the elif to be more specific. Check that studio and film are in cleaned_data and then only create the new Studio if everything is valid.
1

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))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.