pawel.ad
pawel.ad

Reputation: 691

Validate nested form in Django

I have a UserProfile model which is linked OneToOne with Django User model. While adding a user through a form, I wanted to have one form for both of those. I found a snippet that does exactly that, and I didn't have any problem with it, until I wanted to validate the username. The form isn't throwing a ValidationError, like I would like it to, but returns an error page with ValueError: The User could not be created because the data didn't validate..

From what I understand (which obviously could be wrong), the current setup doesn't handle ValidationErrors from the nested form.

Is there any way to add that functionality? If not, how should I tackle the issue of having one form handle to models?

Code:

class CmUserForm(forms.ModelForm):
   password = forms.CharField(widget=forms.PasswordInput(),
                              required=False,
                              help_text=_("Leave empty if you don't want "
                                          "to change it"))

   def clean_password(self):
       data = self.cleaned_data['password']
       if data.strip():
           return make_password(data)
       else:
           # If password field is empty, then don't change it
           return self.instance.password

    def clean_username(self):
       username = self.cleaned_data['username']

       if get_user_model().objects.filter(username=username).exists():
           raise forms.ValidationError(_('This username is already in use.'))
       return username

    class Meta:
        model = get_user_model()
        fields = ('first_name', 'last_name', 'email', 'username', 'password', 'is_staff', 'is_active')


class CmUserProfileForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        # Take User if updating, None if creating
        try:
            self.user = kwargs['instance'].user
        except AttributeError:
            self.user = None

        user_kwargs = kwargs.copy()
        user_kwargs['instance'] = self.user
        self.uf = CmUserForm(*args, **user_kwargs)

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

        self.fields.update(self.uf.fields)
        self.initial.update(self.uf.initial)

    class Meta:
        model = UserProfile
        exclude = ['user']

    def save(self, *args, **kwargs):
        # Save User and pass it to UserProfile
        user = self.uf.save(*args, **kwargs)
        self.instance.user = user
        return super().save(*args, **kwargs)

Upvotes: 0

Views: 1522

Answers (1)

Venu
Venu

Reputation: 63

i implemented the same today. I learnt this from the this website tango with django. http://www.tangowithdjango.com/book/chapters/login.html. Also i provided my code how i achieved this. Inbuilt user model itself checks if the username already exists or not. hope this is helpful.

class Sam(models.Model):
      user = model.OneToOneField(User)
      #custom fields apart from the inbuilt User model
      region = models.CharField(max_length=10)
      designation = models.CharField(max_length=10)
#forms.py form models. Created SamProfileform  to capture the custom fields which are specific to One's Profile and SamForm to capture the password and then hash later in the view. 
#Please note that i didnt add any username check here. The inbuilt User does it for us. I verified it.
class SamForm(forms.ModelForm):
      #form model to capture inbuilt fields of "User" model
      password = forms.CharField(widget=PasswordInput())
      class Meta:
            model = User
            fields = ('username', 'email', 'password', 'firstname', 'lastname')
class SamProfileForm(forms.ModelForm):
       #form model to built the custom fields in my case region and designation
       class Meta:
             model = Sam
             fields = ('desgination', 'mgr')
def register(request):
    registered = False
    if request.method == 'POST':
         user_form = SamForm(data=request.POST)
         profile_form = SamProfileForm(request.POST)
         if user_form.is_valid() and profile_form.is_valid():
            user = user_form.save()
            user.set_password(user.password)
            user.save()
            profile = profile_form.save(commit=False)
            profile.user = user
            profile.save()
            registered = True
         else:
            print user_form.errors, profile_form.errors
   else:
       user_form = SamForm()
       profile_form = SamProfileForm()
   template = loader.get_template('sam/register.html')
   context = RequestContext(request, {
            'user_form' : user_form, 'profile_form' : profile_form, 'registered' : registered,
    })
   return HttpResponse(template.render(context))

Upvotes: 1

Related Questions