Multiple form classes in django generic (class) views

前端 未结 3 1227
广开言路
广开言路 2020-12-01 15:02

I\'d like to use the class based generic views of django 1.3 for forms, but sometimes have to manage multiple form classes in one form. However, it looks like the existing v

3条回答
  •  余生分开走
    2020-12-01 15:14

    Present fields from two models on a single view page

    You have to extend django.views.generic.View class and override get(request) and post(request) methods.

    This is how I did that.

    I'm using Django 1.11.

    This is how my form (consisted of two forms) looks like:

    My View class which renders my two forms:

    from django.views.generic import View
    
    class UserRegistrationView(View):
        # Here I say which classes i'm gonna use
        # (It's not mandatory, it's just that I find it easier)
        user_form_class = UserForm
        profile_form_class = ProfileForm
        template_name = 'user/register.html'
    
        def get(self, request):
            if request.user.is_authenticated():
                return render(request, 'user/already_logged_in.html')
            # Here I make instances of my form classes and pass them None
            # which tells them that there is no additional data to display (errors, for example)
            user_form = self.user_form_class(None)
            profile_form = self.profile_form_class(None)
            # and then just pass them to my template
            return render(request, self.template_name, {'user_form': user_form, 'profile_form': profile_form})
    
        def post(self, request):
            # Here I also make instances of my form classes but this time I fill
            # them up with data from POST request
            user_form = self.user_form_class(request.POST)
            profile_form = self.profile_form_class(request.POST)
    
            if user_form.is_valid() and profile_form.is_valid():
                user = user_form.save(commit=False)
                user_profile = profile_form.save(commit=False)
    
                # form.cleaned_data is a dictionary which contains data validated
                # by fields constraints (Say we have a field which is a number. The cleaning here would 
                # be to just convert a string which came from the browser to an integer.)
                username = user_form.cleaned_data['username']
                password = user_form.cleaned_data['password']
    
                # This will be clarified later 
                # You can save each object individually if they're not connected, as mines are (see class UserProfile below)
                user.set_password(password)
                user.userprofile = user_profile
                user.save()
    
                user = authenticate(username=username, password=password)
    
                if user is not None:
                    if user.is_active:
                        login(request, user)
                    return redirect('user:private_profile')
    
            # else: # form not valid - each form will contain errors in form.errors
            return render(request, self.template_name, {
                'user_form': user_form,
                'profile_form': profile_form
            })
    

    I have a User and UserProfile models. User is django.contrib.auth.models.User and UserProfile is as follows:

    class UserProfile(models.Model):
        user = models.OneToOneField(User, on_delete=models.CASCADE)
        friends = models.ManyToManyField('self', null=True, blank=True)
        address = models.CharField(max_length=100, default='Some address 42')
    
        def get_absolute_url(self):
            return reverse('user:public_profile', kwargs={'pk': self.pk})
    
        def __str__(self):
            return 'username: ' + self.user.username + '; address: ' + self.address
    
        @receiver(post_save, sender=User) # see Clarification 1 below
        def create_user_profile(sender, instance, created, **kwargs):
            if created: # See Clarification 2 below
                UserProfile.objects.create(user=instance, address=instance.userprofile.address)
    
        @receiver(post_save, sender=User)
        def update_user_profile(sender, instance, **kwargs):
            instance.userprofile.save()
    

    Clarification 1: @receiver(post_save, sender=User)

    • when User is saved (I wrote user.save() somewhere (user is an instance of class User) ) UserProfile will also be saved.

    Clarification 2: if created: (clarification from the View class)

    • If User is being CREATED, make a UserProfile where user = User instance which was just now submitted trough a UserForm

    • address is gathered from ProfileForm and added to the user instance before calling user.save()

    I have two forms:

    UserForm:

    class UserForm(forms.ModelForm):
        password = forms.CharField(widget=forms.PasswordInput(render_value=True), required=True)
        password_confirmation = forms.CharField(widget=forms.PasswordInput(render_value=True), required=True)
    
        first_name = forms.CharField(required=True)
        last_name = forms.CharField(required=True)
    
        class Meta:
            model = User
            fields = ('email', 'username', 'first_name', 'last_name', 'password', 'password_confirmation')
    
        def clean(self):
            cleaned_data = super(UserForm, self).clean()
            password = cleaned_data.get("password")
            password_confirmation = cleaned_data.get("password_confirmation")
    
            if password != password_confirmation:
                self.fields['password'].widget = forms.PasswordInput()
                self.fields['password_confirmation'].widget = forms.PasswordInput()
    
                self.add_error('password', "Must match with Password confirmation")
                self.add_error('password_confirmation', "Must match with Password")
                raise forms.ValidationError(
                    "Password and Password confirmation do not match"
                )
    

    ProfileForm:

    class ProfileForm(forms.ModelForm):
        class Meta:
            model = UserProfile
            fields = ('address',)
    

    I hope that I understood your question well and that this will help you (and others to come). :)

提交回复
热议问题