Reputation: 112
I have made a custom signup form but it seems that my app doesn't try to save all the data from a post request. SQL query that is executed is clearly missing few fields. Do you have any idea what's happening? Should I make a custom view for signup also?
Error
django.db.utils.IntegrityError: (1048, "Column 'birth_date' cannot be null")
Post request body
csrfmiddlewaretoken 'S6s1kocaG5nKQyeKfJd4cTD6do3Dv2VjrElQ4DYESybICDQtueQG5TkqQf5HgP7W'
username 'User123'
email '[email protected]'
first_name 'John'
last_name 'Doe'
city '2'
address 'blahblahblah'
phone '005006007'
birth_date '2006-06-09'
password1 'somerandpass'
password2 'somerandpass'
Executed query
db
<_mysql.connection open to 'localhost' at 02154B30>
q
(b'INSERT INTO `users_user` (`email`, `username`, `password`, `date_joined`, `l'
b'ast_login`, `is_admin`, `is_active`, `is_staff`, `is_superuser`, `first_name'
b"`, `last_name`, `phone`, `city_id`, `address`, `birth_date`) VALUES ('example"
b"@gmail.com', 'User123', 'pbkdf2_sha256$216000$6H02ElhAxQ3y$y56l29/sTf0Oyy"
b"+sa39MX2cgLlgvPzsA+K5HWOb/NjU=', '2021-03-21 18:05:49.199287', '2021-03-21 1"
b"8:05:49.199287', 0, 1, 0, 0, 'John', 'Doe', '', NULL, '', NULL)")
self
<MySQLdb.cursors.Cursor object at 0x05194BB0>
models.py
from django.db import models
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.contrib.auth.hashers import make_password
class City(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=20,null=False)
def __str__(self):
return self.name
class UserManager(BaseUserManager):
def create_user(self, email, username, first_name, last_name, city, address,phone, birth_date, password=None):
if not email:
raise ValueError('Korisnik mora imati email adresu')
if not username:
raise ValueError('Korisnik mora imati korisničko ime')
if not first_name:
raise ValueError("Korisnik mora imati ime")
if not last_name:
raise ValueError("Korisnik mora imati prezime")
if not city:
raise ValueError("Korisnik mora mesto stanovanja")
if not address:
raise ValueError("Korisnik mora imati adresu stanovanja")
if not phone:
raise ValueError("Korisnik mora imati broj telefona")
if not birth_date:
raise ValueError("Korisnik mora imati datum rođenja")
user = self.model(email=self.normalize_email(email),
username=username,
first_name=first_name,
last_name=last_name,
city=city,
address=address,
phone=phone,
birth_date=birth_date,
password=password,)
user.password=make_password(user.password)
user.save()
return user
def create_superuser(self, email, username,first_name,last_name,city,address,phone,birth_date, password):
user = self.create_user(email=self.normalize_email(email),
password=password,
username=username,
first_name=first_name,
last_name=last_name,
city=city,
address=address,
phone=phone,
birth_date=birth_date,)
user.is_admin = True
user.is_staff = True
user.is_superuser = True
user.save()
return user
class User(AbstractBaseUser):
class Meta:
verbose_name = 'korisnik'
email = models.EmailField(verbose_name="email", max_length=60, unique=True)
username = models.CharField(max_length=30, verbose_name="korisničko ime", unique=True)
password = models.CharField(verbose_name="lozinka", max_length=1000)
date_joined = models.DateTimeField(verbose_name='datum pridruživanja', auto_now_add=True)
last_login = models.DateTimeField(verbose_name='poslednji put ulogovan', auto_now=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
first_name = models.CharField(verbose_name="ime", max_length=20)
last_name = models.CharField(max_length=20, verbose_name="prezime")
phone = models.CharField(max_length=20,unique=True, verbose_name="broj telefona")
city = models.ForeignKey(City,null=True,on_delete=models.SET_NULL)
address = models.CharField(max_length=50, verbose_name="adresa")
birth_date= models.DateField(verbose_name="datum rođenja")
USERNAME_FIELD = 'email'
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = ['username','first_name','last_name','city','address','birth_date','phone']
objects = UserManager()
def get_absolute_url(self):
return "/users/%i/" % (self.pk)
def __int__(self):
return self.id
def has_perm(self, perm, obj=None):
return self.is_admin
def has_module_perms(self, app_label):
return True
def get_id(self):
return self.id
forms.py
from allauth.account.forms import SignupForm
from users import models as UserModel
from django import forms
from django.forms import ModelForm
import datetime
class MyCustomSignupForm(SignupForm):
first_name = forms.CharField(max_length=20, label='Ime')
last_name= forms.CharField(max_length=20, label='Prezime')
phone= forms.CharField(max_length=20, label='Broj telefona')
birth_date= forms.DateField(label="Datum rođenja", initial=datetime.date.today,
widget=forms.DateInput(attrs={'class':'form-control',
'id': "example-date-input",
'type':'date'}))
city = forms.ModelChoiceField(queryset=UserModel.City.objects.all(),
empty_label=None, label="Grad",
widget=forms.Select(attrs={'class':'form-control'}))
address = forms.CharField(max_length=50, label="Adresa")
def save(self, request):
user = super(MyCustomSignupForm, self).save(request)
user.first_name=self.cleaned_data['first_name']
user.last_name=self.cleaned_data['last_name']
user.phone=self.cleaned_data['phone']
user.birth_date=self.cleaned_data['birth_date']
user.city=self.cleaned_data['city']
user.address=self.cleaned_data['address']
user.save()
return user
signup.html (I have only copied form)
<form method="POST" action="{% url 'account_signup' %}">
{% csrf_token %}
<div class="form-group">
{% comment %} username {% endcomment %}
<label for="username">Korisničko ime</label>
<input type="text" class="form-control" id="username" name="username" placeholder="Unesi korisničko ime"
required>
<!-- email -->
<label for="email">Email adresa</label>
<input type="email" class="form-control" id="email" name="email" placeholder="Unesi email adresu"
aria-describedby="emailHelp" required>
<!-- Ime -->
<label for="first_name">Ime</label>
<input type="text" class="form-control" id="first_name" name="first_name"
placeholder="Unesite svoje ime" required>
<!-- Prezime -->
<label for="last_name">Prezime</label>
<input type="text" class="form-control" id="last_name" name="last_name"
placeholder="Unesite svoje prezime" required>
<!--Mesto-->
<label for="city">Mesto</label>
{% comment %} <select placeholder="Izaberite grad" class="form-control form-control" id="city" name="city">
{% for city in form.city %}
<option value={{ city.id }}>{{ city }}</option>
{% endfor %}
</select> {% endcomment %}
{{form.city}}
<!--Adresa-->
<label for="address">Adresa</label>
<input type="text" class="form-control" id="address" name="address" placeholder="Unesite adresu"
required>
<!-- Kontakt-->
<label for="phone">Kontakt telefon</label>
<input type="text" pattern="^[0-9/-]*$" class="form-control" id="phone" name="phone" placeholder="Unesite kontakt telefon (samo brojevi, / ili - bez razmaka)" required>
<!--Datum-->
<label for="example-date-input" class="col-form-label">Datum rođenja</label>
{% comment %} <input name="birth_date" class="form-control" type="date" value="1980-08-19" id="example-date-input"> {% endcomment %}
{{form.birth_date}}
<!-- password -->
<label for="password1">Lozinka</label>
<input type="password" class="form-control" id="password1" name="password1" required>
<!-- password confirmation -->
<label for="password2">Potvrdite lozinku</label>
<input type="password" class="form-control" id="password2" name="password2" required>
</div>
<input type="submit" class="btn btn-outline-dark btn-custom mt-3" value="Registruj se">
</form>
I have also changed the settings for allauth form, so I'm pretty sure this form is used
ACCOUNT_FORMS = {
'signup': 'users.forms.MyCustomSignupForm'
}
Upvotes: 0
Views: 1347
Reputation: 16
I would like to offer another version which does not skip the mandatory "filters" of the DefaultAccountAdapter
:
adapter.py:
from allauth.account.adapter import DefaultAccountAdapter
class MyAccountAdapter(DefaultAccountAdapter):
def save_user(self, request, user, form, commit=True):
from allauth.account.utils import user_email, user_field, user_username
data = form.cleaned_data
first_name = data.get("first_name")
last_name = data.get("last_name")
email = data.get("email")
username = data.get("username")
user.phone = data.get("phone")
user.birth_date = data.get("birth_date")
user.city = data.get("city")
user.address = data.get("address")
user_email(user, email)
user_username(user, username)
if first_name:
user_field(user, "first_name", first_name)
if last_name:
user_field(user, "last_name", last_name)
if "password1" in data:
user.set_password(data["password1"])
else:
user.set_unusable_password()
self.populate_username(request, user)
if commit:
user.save()
return user
In settings.py the same line:
ACCOUNT_ADAPTER = 'users.adapter.MyAccountAdapter'
And now you don't need save
method in your MyCustomSignupForm
Upvotes: 0
Reputation: 112
I managed to solve it myself. I had to override DefaultAccountAdapter because its save_user method was saving by default only username, password, last_name and first_name. I wish it was written in allauth docs. If anyone is struggling the answer is below. I have made a new file in my users app.
adapter.py
from django.conf import settings
from allauth.account.adapter import DefaultAccountAdapter
from django import utils
class MyAccountAdapter(DefaultAccountAdapter):
def save_user(self, request, user, form, commit=True):
data = form.cleaned_data
user.first_name = data["first_name"]
user.last_name = data["last_name"]
user.email = data["email"]
user.username = data["username"]
user.phone = data['phone']
user.birth_date = data['birth_date']
user.city = data['city']
user.address = data['address']
if "password1" in data:
user.set_password(data["password1"])
else:
user.set_unusable_password()
self.populate_username(request, user)
user.save()
return user
In settings.py I added this line
ACCOUNT_ADAPTER = 'users.adapter.MyAccountAdapter'
Upvotes: 2