Search code examples
djangoformview

Why does this form not save?


the code below is a simple register for an account now when I submit this form it redirects me to the home page and this means that my form is valid and works but when I check my Admin page I see that account is not registered and I get no error. therefor I can understand here that my code is already working but it hasn't been saved. so, how can I save member through FormView? thanks in advance

views.py

from django.shortcuts import render, redirect
from django.http import HttpResponse
from django.views.generic import TemplateView, View
from django.views.generic.edit import FormView, CreateView
from .forms import UserForm
from .models import User
from django.urls import reverse_lazy


class IndexView(TemplateView):
    template_name = "accounts/index.html"


class ProfileView(CreateView):
    template_name = 'accounts/register.html'
    success_url = reverse_lazy('accounts:index')
    form_class = UserForm

forms.py

from django import forms
from .models import User


class UserForm(forms.ModelForm):
    password = forms.CharField(widget=forms.PasswordInput)
    password2 = forms.CharField(label="Confirm Password", widget=forms.PasswordInput)

    class Meta:
        model = User
        fields = '__all__'
        exclude = ('staff', 'active', 'admin', 'last_login')

    def clean_password2(self):
        password = self.cleaned_data['password']
        password2 = self.cleaned_data['password2']
        if password and password2 and password != password2:
            raise forms.ValidationError("Passwords don't match")
        return password2

    def clean_email(self):
        email = self.cleaned_data['email']
        qs = User.objects.filter(email=email)
        if qs.exists():
            raise forms.ValidationError("email is taken")
        return email

    def save(self, commit=True):
        user = super().save(commit=False)
        user.set_password(self.cleaned_data['password'])

        if commit:
            user.save()
        return user

models.py

from django.db import models
from django.contrib.auth.models import (AbstractBaseUser, BaseUserManager)
from django.db.models.signals import post_save
from django.dispatch import receiver


class UserManager(BaseUserManager):

    def create_user(self, email, password, username, is_staff=True, is_admin=True, is_active=True):
        if not email:
            raise ValueError("This email is invalid")
        if not password:
            raise ValueError("This Password is invalid")
        if not username:
            raise ValueError("This Username is invalid")
        user = self.model(
            email=self.normalize_email(email)
        )
        user.staff = is_staff
        user.admin = is_admin
        user.active = is_active
        user.set_password(password)
        user.username = username
        user.save(using=self._db)
        return user

    def create_staffuser(self, email, password, username):
        user = self.create_user(
            email,
            password=password,
            username=username,
            is_staff=True,
        )
        return user

    def create_superuser(self, email, password, username):
        user = self.create_user(
            email=email,
            password=password,
            username=username,
            is_staff=True,
            is_admin=True,
        )
        return user


class User(AbstractBaseUser):
    email = models.EmailField(max_length=255, unique=True, verbose_name="Email")
    first_name = models.CharField(max_length=255, verbose_name="First Name")
    last_name = models.CharField(max_length=255, verbose_name="Last Name")
    username = models.CharField(max_length=50, unique=True, verbose_name="Username")
    active = models.BooleanField(default=True, verbose_name="Active")
    staff = models.BooleanField(default=False, verbose_name="Staff")
    admin = models.BooleanField(default=False, verbose_name="Admin")
    timestamp = models.DateTimeField(auto_now_add=True, verbose_name="Time Stamp")

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["username"]
    objects = UserManager()

    def __str__(self):
        return self.username

    def get_short_name(self):
        return self.username

    def get_full_name(self):
        return self.username

    def has_perm(self, perm, obj=None):
        return True

    def has_module_perms(self, app_label):
        return True

    @property
    def is_staff(self):
        return self.staff

    @property
    def is_admin(self):
        return self.admin

    @property
    def is_active(self):
        return self.active


class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    article = models.TextField(blank=True, max_length=500, verbose_name="Article")


def create_profile(sender, **kwargs):
    if kwargs['created']:
        user_profile = User.objects.create(user=kwargs['instance'])


post_save.connect(receiver=create_profile, sender=User)

Solution

  • Because a FormView doesn't save() the form. It's meant to be used with any form, not just ModelForms. Not every Form has a save() method.

    The only thing the FormView does in form_valid() is redirect to the success url. You have to tell it yourself what you it to do after the form was verified to be valid:

    def form_valid(self, form):
        form.save()
        return super().form_valid(form)
    

    You can see the inner workings of FormView here.

    You could use a CreateView instead of a FormView. That would do the saving for you.