I understand that django comes with a User model built in and the following code works and properly saves a user to the db, I just don't understand why it works:
from django.shortcuts import render
from django.http import HttpResponse
from .forms import UserCreationForm
def index(request):
if request.method == "POST":
form = UserCreationForm(request.POST)
if form.is_valid():
user = form.save()
return HttpResponse('Saved')
else:
form = UserCreationForm()
return render(request, 'core/index.html', {'form':form})
what is the line form = UserCreationForm(request.POST) saying? is the (request.POST) the contents of the submitted form? and how does user = form.save() save a user to the database? I was thinking that maybe the variable had to be named user for django to recognize it as a User object but this is not the case as I changed the statement to test = form.save()
and it still saved a User to my database.
The line form = UserCreationForm(request.POST) talks of itself actually. It posts data passed through your form to the variable form
The 'request' object is an HttpRequest object that carries metadata about the request (for example in our case, we have a post request , and the request object carries the user data). Check this for more details.
Yes, user = form.save() saves a User instance in the database. UserCreationForm has a function called save() and here we're just calling that function.
The name of the variable has nothing to do with it because in the UserCreationForm's definition, the model is already defined as User. So, it already recognizes that it's the User model.
I this you should read the UserCreationForm code to make things clear in your head.
This is django doc :
[docs]class UserCreationForm(forms.ModelForm):
"""
A form that creates a user, with no privileges, from the given username and
password.
"""
error_messages = {
'password_mismatch': _("The two password fields didn't match."),
}
password1 = forms.CharField(label=_("Password"),
widget=forms.PasswordInput)
password2 = forms.CharField(label=_("Password confirmation"),
widget=forms.PasswordInput,
help_text=_("Enter the same password as above, for verification."))
class Meta:
model = User
fields = ("username",)
def clean_password2(self):
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError(
self.error_messages['password_mismatch'],
code='password_mismatch',
)
return password2
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
You can see the "save" method, it use user model with super. Result UserCreationForm use User model and can save in User database
Related
I have created a registration form using the class models.User (refer) in Django as follows:
from Django.shortcuts import render, redirect
from django.contrib.auth.models import User
def register(request):
if request.method == 'POST':
username = request.POST['username']
email = request.POST['email']
password = request.POST['password']
confirm_password = request.POST['confirm_password']
if password == confirm_password:
# some code
user = User.objects.create_user(username=username, email=email, password=password)
user.save()
return redirect('login')
else:
return redirect('register')
return render(request, 'register.html')
My problems:
Now I want to make Full Name as optional but all other fields as required, also I want to apply length constraint on my fields, how can I do that? As this is an inbuilt model (class models.User), I am not sure how to set blank=True or use max_length for any field.
Also I want to remove the spaces (if any) at the end and beginning of the entered data, before saving it using user.save() (e.g. if someone entered the name as " abc efg " I want to save it as "abc efg"). Basically, I want to use .is_valid() feature of the Django forms. I even tried doing that as:
from django.shortcuts import render, redirect
from django.contrib.auth.models import User
def register(request):
if request.method == 'POST':
form = User(request.POST or None)
if form.is_valid():
username = request.POST['username']
email = request.POST['email']
password = request.POST['password']
confirm_password = request.POST['confirm_password']
if password == confirm_password:
# some code
user = User.objects.create_user(username=username, email=email, password=password)
user.save()
return redirect('login')
else:
return redirect('register')
else:
return redirect('register')
return render(request, 'register.html')
but this is giving me the following error: 'User' object has no attribute 'is_valid'
Any ideas about how can I make the fields optional/required and set the max_length of the fields & remove the trailing spaces (if any) all while using the inbuilt model class models.User?
You need to declare a form first. If you are using default django user, then you can user UserCreationForm to validate the data:
from django.contrib.auth.forms import UserCreationForm
def register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST or None)
if form.is_valid():
form.save()
# rest of the code
Now, lets say you want want to clean the data which you get from the HTML form, then you can use clean_<field_name> method. To do that inside UserCreationForm, you can override it(also adding some code to show how to customize forms):
class CustomUserForm(UserCreationForm):
first_name = forms.CharField(max_length=30, required=False, help_text='Optional.')
def clean_first_name(self):
return self.cleaned_data['first_name'].strip()
Please see the documentation for more information on validation and cleaning data in forms.
As per the django docs, it is best to create your own user model by inheriting from the AbstractBaseUser: https://docs.djangoproject.com/en/3.1/topics/auth/customizing/#specifying-a-custom-user-model
There you can define what's optional and what not.
Oh, and while you're at it - maybe it's also worth looking at django-allauth. Maybe not too relevant right now but for sure down the road! Also check out the custom forms.
Custom User Model (models.py):
class Users(AbstractBaseUser):
user_id = models.AutoField(primary_key=True)
user_email = models.EmailField(max_length=100, unique=True)
password = models.CharField(max_length=100)
registration_date = models.DateField(auto_now_add=True)
last_login = models.DateField(auto_now_add=True)
is_administrator = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
email_verified = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'user_email'
Custom User Manager (models.py)
class UserManager(BaseUserManager):
def create_user(self, user_email, password):
user = self.model(
email=self.normalize_email(user_email)
)
user.set_password(password)
user.save(using=self._db)
return user
Registration Form (forms.py):
class RegistrationForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput)
password_confirm = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = get_user_model()
fields = ('user_email',)
def clean_password(self):
password1 = self.cleaned_data.get("password")
password2 = self.cleaned_data.get("password_confirm")
if password1 and password2 and password1 != password2:
raise forms.ValidationError( self.error_messages['password_mismatch'],
code='password_mismatch' )
return password2
def save(self, commit=True):
user = super(RegistrationForm, self).save(commit=False)
user.registration_date = datetime.date.today()
user.last_login = datetime.date.today()
user.set_password("password")
if commit:
user.save()
return user
Register view (views.py)
def register(request):
if request.method == "POST":
form = RegistrationForm(request.POST)
if form.is_valid:
form.save()
else:
form = RegistrationForm()
return render(request, 'web/page.html', {'form' : form})
This is my first question on .. anything online (I'm an avid user of the search facility), but I've been struggling with this for days and I feel like it shouldn't be so difficult.
For my question, it is more like a two part query.
1) I want a custom user model, but want to use as much of what Django is offering. But getting rid of the username field and adding a couple other fields seems to require me to make "custom" everything else. Including a custom user manager and custom forms - not ideal! The registration form above works but I was hoping for something a bit more simple, along the lines of:
class RegistrationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = get_user_model()
fields = ('user_email', 'password1', 'password2')
but I seem to be struggling, running it gives me the error:
The Users could not be created because the data didn't validate.
Ideally I would want something simple like that without the error. I'm hoping it to give fewer errors and hopefully more future proof if Django adds new security features.
2) Why tf won't the login code below work?
Login Form (forms.py):
class LoginForm(forms.Form):
user_email = forms.EmailField(max_length=100)
password = forms.CharField(max_length=100)
Login View (views.py):
def login_user(request):
if request.method == "POST":
user_email = request.POST.get('email')
password = request.POST.get('password')
UserModel = get_user_model()
user = UserModel.objects.get(user_email=user_email)
if user.check_password(password):
login(request, user)
return render(request, 'login.html')
I'm pretty sure the problem lies with the second if statement. It just won't give a True value when the correct password is entered. Having tried print(user.user_id), the correct id is given for the email and password entered. Having played with some plaintext passwords, it works, but I rather like the set_password and check_password functions - provided they work!
Many thanks!
I worked it out. Both naive errors!
For point two, the Login View (views.py) code works well, the problem was the Registration Form (forms.py). In particular:
user.set_password("password")
was literally saving 'password' as the password. Changed to:
user.set_password(self.cleaned_data["password_confirm"])
made it work.
Not that it matters (for point one), because the 4 line Registration Form inheriting the UserCreationForm worked all along and will replace the first one. Turns out the issue was I wasn't putting a valid password (minimum 8 letters). Why it validates the data in views.py during:
if form.is_valid:
but then tells me off later I have no idea. I wish Django would be a bit more detailed on what the issue was!
I would recommend that you look into Django all-auth. It's an open source solution to user registration and management that offers a lot of customization, and works well with custom user models.
https://github.com/pennersr/django-allauth
I've overwrite "save_model" method to manage user's password in my admin's panel application. What I want is:
Create a new random password when I create new user (if password field is empty)
Encrypt the password (if I set it)
Use user's password (if I change user but not set the password)
How can I define the last condition?
def save_model(self, request, obj, form, change):
if not change and (not form.cleaned_data['password']) :
password = User.objects.make_random_password()
obj.set_password(password)
elif form.cleaned_data['password'] :
obj.set_password(form.cleaned_data['password'])
else
?
super(UserAdmin, self).save_model(request, obj, form, change)
You are doing all this a bit too late. The user creation form won't even allow you to post a form without fields password1 and password2 filled in. If those fields are empty you will never even get to UserAdmin.save_model() method.
But you can override the user creation form's save() method, in it allow password fields to be empty and set random password if fields are empty (or use user's password if entered).
As a bonus, this code keeps in place form validation if user entered the password (password mismatch) as well as checking AUTH_PASSWORD_VALIDATORS (all except 'django.contrib.auth.password_validation.MinimumLengthValidator' since empty password field mean 0 chars).
### admin.py
from django import forms
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
class CustomUserCreationForm(UserCreationForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['password1'].required = False
self.fields['password2'].required = False
def _clean_form(self):
super()._clean_form()
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 != password2:
self.add_error('password2', self.error_messages['password_mismatch'])
def save(self, commit=True):
user = super().save(commit=False)
user_password = self.cleaned_data["password1"]
if not user_password:
user_password = User.objects.make_random_password()
user.set_password(user_password)
if commit:
user.save()
return user
class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)
I have created a custom User registration form, from the UserCreationForm. When I try to register, it does register successfully, and I can see a newly created user with the username and its email. But there's no password for that user.
In the admin, the password field for that user is No password set.. Please correct me where I am wrong. Thank you.
forms.py:
from album.forms import MyRegistrationForm
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
class MyRegistrationForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = ('username', 'email', 'password1', 'password2',)
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.email = self.cleaned_data['email']
if commit:
user.save()
return user
views.py:
def register_user(request):
if request.method == "POST":
form = MyRegistrationForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/accounts/register_success/')
else:
form = MyRegistrationForm()
return render(request, 'register.html', {'form':form})
When calling save on the superclass using super, use the form MyRegistrationForm, not its superclass UserCreationForm.
user = super(MyRegistrationForm, self).save(commit=False)
I've been stuck on this for a while now and can't seem to figure out what's going on. I'm just starting to learn Django and I got my login set up and now want to implement a registration page.
I used the UserCreationForm form at first and that worked fine, but I want to add fields for Email, First name, and Last name. I figured I could just subclass UserCreationForm and add the fields but that doesn't seem to work. Also I tried overriding the save method, but it still doesn't work.
My custom form looks like this:
class RegistrationForm(UserCreationForm):
first_name = forms.CharField(max_length=30)
last_name = forms.CharField(max_length=30)
email = forms.EmailField(max_length=75)
class Meta:
model = User
fields = ("first_name", "last_name", "email",)
def save(self, commit=True):
user = super(RegistrationForm, self).save(commit=False)
user.first_name = self.cleaned_data["first_name"]
user.last_name = self.cleaned_data["last_name"]
user.email = self.cleaned_data["email"]
if commit:
user.save()
return user
The view to handle this is the following:
def Register(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
new_user = form.save();
new_user = authenticate(username=request.POST['username'], password=request.POST['password1'])
login(request, new_user)
return HttpResponseRedirect('/members/home')
else:
form = RegistrationForm()
return render_to_response('register.html', {'form' : form}, context_instance=RequestContext(request))
The form loads just fine with the new fields and everything, but when I submit I get the error:
AttributeError at /register/
'AnonymousUser' object has no attribute 'backend'
Oh, and also I'm using Django 1.3.
Any idea what I'm doing wrong?
My guess is that your meta class fields doesn't include username.
You are inheriting the form field from UserCreationForm but it's not saving to the User model and therefore authenticate is failing, and crashing on login()
The docs suggest you check whether authenticate() is successful before using login()