I am following a tutorial to do this in Django 3.1.7.
The problem I'm having here is I'm being forced to repeat my Profile Model in my Profile Form definition.
I want to use forms.ModelForm in my forms.py to inherit my Profile Model and auto-generate the forms. It seems redundant to have to spell everything out again in forms.py when it is already defined in my Models. But I'm not sure how to do that with this architecture.
I've tried this approach:
https://stackoverflow.com/a/2213802/4144483
But the problem with this is that UserForm is incomplete - 'password1' and 'password2' don't exist for model User. This is not a good solution for user registration. I seem to be bound to using UserCreationForm somehow.
#models.py
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
#receiver(post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
#forms.py
rom django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
class SignUpForm(UserCreationForm):
birth_date = forms.DateField(help_text='Required. Format: YYYY-MM-DD')
class Meta:
model = User
fields = ('username', 'birth_date', 'password1', 'password2', )
#views.py
from django.contrib.auth import login, authenticate
from django.shortcuts import render, redirect
from mysite.core.forms import SignUpForm
def signup(request):
if request.method == 'POST':
form = SignUpForm(request.POST)
if form.is_valid():
user = form.save()
user.refresh_from_db() # load the profile instance created by the signal
user.profile.birth_date = form.cleaned_data.get('birth_date')
user.save()
raw_password = form.cleaned_data.get('password1')
user = authenticate(username=user.username, password=raw_password)
login(request, user)
return redirect('home')
else:
form = SignUpForm()
return render(request, 'signup.html', {'form': form})
I generally use ModelForm instead of CreateUserForm for UserRegistration like this and add password1 and password2 fields in it. also, I check if they both are the same.:
forms.py
class UserRegistrationForm(forms.ModelForm):
password = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Repeat Password', widget=forms.PasswordInput)
email = forms.EmailField(label='Email')
date_of_birth = forms.DateField(widget=forms.widgets.DateInput(attrs={'type': 'date'}))
class Meta:
model = User
fields = ['username', 'first_name', 'last_name', 'email',
] #these ordering will be as follow in html form
def clean_password2(self):
cd = self.cleaned_data
if cd['password'] != cd['password2']:
raise forms.ValidationError("Passwords don't match")
return cd['password2']
Then in views, I create a user and their profile and save the password in encrypted form, and link their profile.
views.py
def register(request):
u_form = UserRegistrationForm(data=request.POST or None)
p_form = ProfileForm(data=request.POST or None, files=request.FILES or None)
if u_form.is_valid() and p_form.is_valid():
new_user = u_form.save(commit=False)
new_user.set_password(u_form.cleaned_data['password']) #this saves password in encrypted form instead of raw password
new_user.save()
profile = p_form.save(commit=False)
profile.user = new_user
profile.save()
return render(request, 'accounts/register_done.html', {'new_user': user})
return render(request, 'accounts/register.html', {'user_form': u_form, 'profile_form':p_form})
You can modify it as you like.
Related
When I register a new user, I can log in with my superuser account on the admin page. The error is that "Please enter the correct username and password for a staff account. Note that both fields may be case-sensitive".
I found out that the new user has permissions on Staff status and Superuser status in default. And I can log in admin page in the new user account. So, I thought the login error happens because of giving the new user Staff and Superuser permissions in default.
I thought these codes
password1 = forms.CharField(max_length=30, required=False, help_text='Optional', widget=forms.TextInput(attrs={'placeholder': 'Password'}))
password2 = forms.CharField(max_length=30, required=False, help_text='Optional', widget=forms.TextInput(attrs={'placeholder': 'Repeat your password'})) in forms.py are the reason. So I tried register both with them and not with them. But, the result was same.
How to make the new user has only active status permissions, not the Staff and Superuser permissions in default?
Is my assumption for the admin login error right?
2-1) If not, how to solve this?
This is views.py
from django.shortcuts import render, redirect
from django.http import HttpResponse
from django.views.generic import TemplateView, CreateView
from django.contrib.auth.mixins import LoginRequiredMixin
from .forms import SignUpForm, ProfileForm
from django.urls import reverse_lazy
from .models import *
from django.contrib import messages
from django.contrib.auth import authenticate, login
def update_profile(request):
if request.method == 'POST':
form = SignUpForm(request.POST, instance=request.user)
profile_form = ProfileForm(request.POST, instance=request.user.profile)
if form.is_valid() and profile_form.is_valid():
user = form.save()
profile = profile_form.save(commit=False)
profile.user = user
profile.save()
# username = form.cleaned_data.get('username')
# password = form.cleaned_data.get('password1')
# user = authenticate(request, username=username, password=password)
return redirect('login')
else:
form = SignUpForm()
profile_form = ProfileForm()
context = {'form': form, 'profile_form': profile_form}
return render(request, 'common/register.html', context)
This is forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from apps.userprofile.models import Profile
# from django.db.models import fields
# from django.db import models
# from django.shortcuts import render, redirect
class SignUpForm(UserCreationForm):
first_name = forms.CharField(max_length=30, required=False, help_text='Optional', widget=forms.TextInput(attrs={'placeholder': 'Your first name'}))
last_name = forms.CharField(max_length=30, required=False, help_text='Optional', widget=forms.TextInput(attrs={'placeholder': 'Your last name'}))
email = forms.EmailField(max_length=254, required=False, help_text='Enter a valid email address', widget=forms.EmailInput(attrs={'placeholder': 'Your email'}))
password1 = forms.CharField(max_length=30, required=False, help_text='Optional', widget=forms.TextInput(attrs={'placeholder': 'Password'}))
password2 = forms.CharField(max_length=30, required=False, help_text='Optional', widget=forms.TextInput(attrs={'placeholder': 'Repeat your password'}))
class Meta:
model = User
widgets = {
'username': forms.TextInput(attrs={'placeholder': 'Username'})
}
fields = [
'username',
'first_name',
'last_name',
'email',
'password1',
'password2',
]
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields= [ 'student_ID', 'CBNU_PW' ]
widgets = {
'student_ID': forms.TextInput(attrs={'placeholder': 'student_ID'}),
'CBNU_PW': forms.TextInput(attrs={'placeholder': 'CBNU_PW'})
}
This is models.py
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
student_ID = models.CharField(max_length=30, blank=True)
CBNU_PW = models.CharField(max_length=30, blank=True)
def __str__(self):
return '%s %s' % (self.user.first_name, self.user.last_name)
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
Suppose I create an account with username="Ali" and with email="ali5242#gmail.com" and then I create account with different username but with the same email then upon sending post request to sign up the user, django with through error of:
Exception Type: IntegrityError
Exception Value:
UNIQUE constraint failed: accounts_profile.email
error image:https://imgur.com/1CQeXSV
but even then it saves that user in the database with all the info that is provided
What should I do? I am new to django. I would love to get the solution and your advice on which topics should i research more.
I extended User model and now my models.py files look like this,
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
# Create your models here.
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True)
address = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
email = models.EmailField(unique=True, null=True)
email_confirmed = models.BooleanField(default=False)
def __str__(self):
return str(self.user)
#receiver(post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
My forms.py file look like this,
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
class SignupForm(UserCreationForm):
email = forms.EmailField(widget= forms.EmailInput(attrs={'placeholder':'Email', 'required':'True', 'class': 'form-control'}))
username = forms.CharField(widget= forms.TextInput(attrs={'placeholder':'Username', 'class': 'form-control'}))
password1 = forms.CharField(widget= forms.PasswordInput(attrs={'placeholder':'Enter password', 'class': 'form-control'}))
password2 = forms.CharField(widget= forms.PasswordInput(attrs={'placeholder':'Confirm password', 'class': 'form-control'}))
class Meta():
model = User
fields = ('username','email','password1','password2',)
my signup function look like this,
if request.method == 'POST':
form = SignupForm(request.POST)
if form.is_valid():
user = form.save()
user.refresh_from_db()
user.profile.email = form.cleaned_data.get('email')
user.save()
raw_password = form.cleaned_data.get('password1')
user = authenticate(username=user.username, password=raw_password)
login(request, user)
return redirect('/shop/index')
else:
form = SignupForm()
return render(request, "pillow_site_html/signup.html", { 'form':form })
So I have almost completed my Django app. But whenever I create the new user, his/her profile is not created automatically and when I open the profile after logging in, I get this error RelatedObjectDoesNotExist at /esacp/profile/ User has no profile
My signals.py is below:
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import Profile
#receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_profile(sender, instance, **kwargs):
instance.profile.save()
My views.py of register() and profile() are below:
from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import UserRegisterForm, UserUpdateForm, ProfileUpdateForm
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
def register(request):
if request.method == 'POST':
form = UserRegisterForm(request.POST)
if form.is_valid():
form.save()
username = form.cleaned_data.get('username')
messages.success(request, f'Your Student Account has been created! You can log in ESACP now.')
return redirect('login')
else:
form = UserRegisterForm()
return render(request, 'users/register.html', {'form': form})
#login_required
def profile(request):
if request.method == 'POST':
u_form = UserUpdateForm(request.POST, instance=request.user)
p_form = ProfileUpdateForm(request.POST,
request.FILES,
instance=request.user.profile)
if u_form.is_valid() and p_form.is_valid():
u_form.save()
p_form.save()
messages.success(request, f'Your Profile information has been updated.')
return redirect('profile')
else:
u_form = UserUpdateForm(instance=request.user)
p_form = ProfileUpdateForm(instance=request.user.profile)
context = {
'u_form': u_form,
'p_form': p_form
}
My forms.py is below:
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from .models import Profile
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
first_name = forms.CharField(max_length=50)
last_name = forms.CharField(max_length=50)
class Meta:
model = User
fields = ['username', 'first_name', 'last_name', 'email', 'password1', 'password2']
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'first_name', 'last_name', 'email']
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['image']
and my apps.py is below:
from django.apps import AppConfig
class UsersConfig(AppConfig):
name = 'users'
def ready(self):
import users.signals
Everything to me seems to be fine, anyone knows what's wrong or what am I missing?
It seems like your receivers are not working
Check if you have,
INSTALLED_APPS = [
....
users.apps.UsersConfig
]
if UsersConfig is not there, it will not be initialized and ready wont be executed, resulting in not importing your receivers
or you can add in init.py inside users app
default_app_config = 'users.apps.UsersConfig'
To ensure receiver works, just add print("I'm working") inside receivers functions and watch the console
I have a signup form which I have defined below
forms.py
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from django import forms
class SignUpForm(UserCreationForm):
display_name = forms.CharField(max_length=30, required=True, help_text='Please provide a display name for your profile')
class Meta:
model = User
fields = ('username', 'password1', 'password2', 'display_name')
In the views.py, I handle the signup process
views.py
from django.contrib.auth.models import User
from django.contrib.auth import login, authenticate
from .forms import SignUpForm
from django.shortcuts import render, redirect
#csrf_exempt
def signup_users(request):
if request.method == 'POST':
form = SignUpForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
username = form.cleaned_data.get('username')
display_name = form.cleaned_data.get('display_name')
raw_password = form.cleaned_data.get('password1')
user.set_password(raw_password)
user.display_name = display_name
user.save()
user = authenticate(username=username, password=raw_password)
print("signup authencticate", user)
login(request, user)
return render(request, 'index.html')
else:
form = SignUpForm()
return render(request, 'signup.html', {'form': form})
The user is able to signup successfully however their display_name doesn't get stored in the django User model.Even in the django admin, I do not see the field display_name.I have even performed a query like serializers.serialize("json",User.objects.filter(username=username)) but even here it shows every other field except the display_name.
What am I doing wrong?
Well quite simply the django.contrib.auth.models.User model has not field named display_name so of course it doesn't get saved. If you want to add fields to your User model you'll have to provide a custom User model.
I have the following error:
AttributeError: 'User' object has no attribute 'set_password'
The problem is I didn't override the class User:
My model.py:
class User(models.Model):
username = models.CharField(max_length=30)
password = models.CharField(max_length=30)
email = models.EmailField()
def __str__(self):
return self.username
My view.py:
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
user = form.save(commit=False)
print type(user)
# Cleaning and normalizing data
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user.set_password(password)
user.save()
# returns User objects if the credential are correct
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
return redirect('website:home')
return render(request, self.template_name, {'form': form})
And this is my form.py:
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control',
'type': 'password',
'placeholder': 'Enter your password'}))
class Meta:
model = models.User
I don't really know also if I should override the User class. In which case I should and in which case I shouldn't?
You need to inherit from AbstractUser to get access to set_password attribute. Instead of using models.Model use:
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
...
Your User model is not the same as django's User model.
Reference custom user model
from django.contrib.auth.hashers import make_password
replace
user.set_password(password) by user.password = make_password('password')
it clear and work for me.
The User model in Django has .set_password but if you made your own you should try OneToOneField(User) from there you just have to make sure you save both in the views.
user_form = UserForm(data=request.POST)
if user_form.is_valid():
user = user_form.save()
user.set_password(user.password)
profile = user.userprofile
profile.bio = request.POST['bio']
profile.save()