I have created an Update Profile page. So the fields are: Email, First Name, Last Name.
In the validation, I'm trying to exclude logged user's email address, and also I'm filtering other user's email addresses. I think if you see the code you will understand what I'm talking about.
I read several questions here but couldn't find something. Some users had the same issue.
So with the code below, the issue I'm getting is:
type object 'User' has no attribute 'email'.
I tried many ways to get the current user's email address(before I save the form) but still nothing.
forms.py
class UpdateProfile(forms.ModelForm):
email = forms.EmailField(required=True)
first_name = forms.CharField(required=False)
last_name = forms.CharField(required=False)
class Meta:
model = User
fields = ('email', 'first_name', 'last_name')
def clean_email(self):
email = self.cleaned_data.get('email')
current_user_email = User.email
if User.objects.filter(email__iexact=email).exclude(email__iexact=current_user_email).count() > 0:
raise forms.ValidationError('This email address is already in use.'
'Please supply a different email address.')
return email
def save(self, commit=True):
user = super(UpdateProfile, self).save(commit=False)
if commit:
user.save()
return user
I suppose that in line current_user_email = User.email "User" isn't really a actual user instance, it is a model class which you imported for setting model = User in Meta.
If you want to use this form only for editing user data You should do something like this:
urls.py
urlpatterns = patterns('project.apps.app_name.views',
url(r'^edit-user/(?P<pk>\d+)/?$','edit_user',name='edit_user'),
)
app_name/views.py:
def edit_user(request,pk):
user_instance = User.objects.get(pk=pk)
form = UpdateProfile(request.POST, instance=user_instance)
.... more code ....
And in forms.py you should change line from:
current_user_email = User.email
to:
current_user_email = self.instance.email
But the proper way to do that (if you use django >1.6) would be to create custom User model, and set email field attribute unique=True.
Related
Im trying to register users hashing their passwords before add to database as follows
settings.py
PASSWORD_HASHERS = (
'django.contrib.auth.hashers.MD5PasswordHasher',
)
Models.py
from django.contrib.auth.models import AbstractBaseUser
class Users(AbstractBaseUser):
name = models.CharField(max_length=200)
email = models.CharField(max_length=200)
password = models.CharField(max_length=255)
Views.py
name = form.cleaned_data.get('name')
email = form.cleaned_data.get('email')
password = form.cleaned_data.get('password')
date_joined = date.today()
if Users.objects.filter(email=email).exists() and Users.objects.filter(name=name).exists() :
messages.info(request, 'User already exists')
else:
Users.objects.create(name=name, email=email, password=set_password(password), date_joined=date_joined)
but when i actually trying to create the user i get Python : name 'set_password' is not defined
What is wrong? do i need to import something at Views.py?
You must use set_password() on a user instance, not when creating a user.
Try this:
user = Users.objects.create(first_name=name, email=email, date_joined=date_joined)
user.set_password(password)
user.save()
I can see you are extending the user model. I'd have a look at some articles for ways to do this, I recommend this article.
Here i am simply using User model from django.contrib.auth.models import User and I have a custom userprofile model where user foreign key in that django built in User model , well i have created an email_address field which can manually update by a user, and there is an other email field which is built in inside django User model , I want to update that email field as userprofile email_address field update.
I am simply getting user object which username and try to get email of that user object but getting an error : 'QuerySet' object has no attribute 'email'
models.py
def save(self,*args, **kwargs):
user_obj = User.objects.filter(username=self.user.username)
print(user_obj,'user object')
print(user_obj.email,'email have a user')
email = self.email_address
user_obj.email = self.email_address
print(user_obj.email,'email have a user')
user_obj.save(user_obj.email)
super(UserProfile,self).save(*args, **kwargs)
user_obj is not a User object, it is a QuerySet of User objects.
You can retrieve a single User object with .get(…) [Django-doc]:
def save(self,*args, **kwargs):
user_obj = User.objects.get(username=self.user.username)
user_obj.email = self.email_address
user_obj.save()
super(UserProfile,self).save(*args, **kwargs)
But here you can actually simply use the self.user object:
def save(self,*args, **kwargs):
user = self.user
user.email = self.email_address
user.save()
super(UserProfile,self).save(*args, **kwargs)
That being said, I would advise not to store data twice. So in case the User object has an email address, there is no need to store this in the UserProfile model as well. You can easily access this with:
from django.conf import settings
class UserProfile(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
#property
def email_address(self):
return self.user.email
This thus means that you access the email address of the related User object. This avoids data duplication, which will make it hard to keep data in sync. Especially since the .save() method will not run for example for bulk create/updates by the Django ORM.
I have used django user model and extended it to add a phone field. I was able to use either username or email to login but cannot access phone number field from extended user model.
models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
phonenumber = models.CharField(max_length=10)
backends.py
class AuthenticationBackend(backends.ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
usermodel = get_user_model()
print(usermodel)
try:
user = usermodel.objects.get(Q(username__iexact=username) | Q(
email__iexact=username))
if user.check_password(password):
return user
except user.DoesNotExist:
pass
backend.py is what I used to implement login via username or email but I couldn't do it for phonenumber in extended user model.
There are two ways to solve your problem.
Extend the User model by subclassing it, and changing the default User model. I would recommend you go this route, since the Phone number is used for authentication.
Set related_name on Profile, and query the User model from that name.
For the first one, I recommend you check out the Django documentation on creating custom users.
There is also a pretty extensive tutorial on how to do this here.
If you want to go the easy route, you just need to set a reverse accessor on the Profile's phonenumber field.
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
phonenumber = models.CharField(max_length=10, related_name='profile')
You can then search users by their profile's phonenumbers:
class AuthenticationBackend(backends.ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
usermodel = get_user_model()
print(usermodel)
try:
user = usermodel.objects.get(Q(username__iexact=username) | Q(
email__iexact=username) | Q(profile__phonenumber__iexact=username)
if user.check_password(password):
return user
except user.DoesNotExist:
pass
Although this is not within the scope of your question, I think you should be aware of the following issues with your code:
Phone numbers are often more complex than a 10 character string. +31 6 12345678, is a valid phone number, but it's more than 10 characters. Check out this question for pointers on how to store and validate phone numbers.
If you use a field for authentication, make sure it's unique. The phonenumber field should actually be phonenumber = models.CharField(max_length=10, related_name='profile', unique=True)
Your usermodel.objects.get(...) query will return more than one user if there is a user with phone number '0123456789' and a different user with username '0123456789'. You should constrain usernames to not contain phone numbers, which is only possible by extending the default User class.
You can extend the AbstractUser and add the phonenumber on it.
class User(AbstractUser):
phonenumber = models.CharField(max_length=10)
USERNAME_FIELD = 'phonenumber'
Then you should specify the custom model as the default user model in settings.py
AUTH_USER_MODEL = 'app_name.User'
Customizing authentication in Django
I am a beginner in Django an am using version 2.2 .I created a user form to sign a user in the site but it cant add other field information to the database
I have tried adding other fields in the fields list add adding fields but nothing works`
forms.py
from django import forms
from django.contrib.auth import (
authenticate,
get_user_model
)
User = get_user_model()
class UserRegisterForm(forms.ModelForm):
username = forms.CharField(label='PUsername')
email = forms.EmailField(label='Email address')
email2 = forms.EmailField(label='Confirm Email')
password = forms.CharField(widget=forms.PasswordInput,label='Password')
password2 = forms.CharField(widget=forms.PasswordInput,label='ConfirmPassword')
age = forms.CharField(label='your age')
info = forms.CharField(label='info about you')
class Meta:
model = User
fields = [
'username',
'email',
'email2',
'password',
'password2',
'age'
'info'
]
def clean(self, *args, **kwargs):
username = self.cleaned_data.get('email')
email = self.cleaned_data.get('email')
email2 = self.cleaned_data.get('email2')
password = self.cleaned_data.get('password')
password2 = self.cleaned_data.get('password2')
if email != email2:
raise forms.ValidationError("Emails must match")
email_qs = User.objects.filter(email=email)
if password != password2:
raise forms.ValidationError("Passwords must match")
email_qs = User.objects.filter(email=email)
if email_qs.exists():
raise forms.ValidationError(
"This email has already been registered")
username_ex = User.objects.filter(username=username)
if username_ex.exists():
raise forms.ValidationError("This username is taken")
return super(UserRegisterForm, self).clean(*args, **kwargs)
views.py
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate,get_user_model,login,logout
from .forms import CreateUserForms
import random
import string
def register_view(request):
if request.method=='POST':
frm=CreateUserForm(request.POST)
if frm.is_valid():
username, email, password = frm.cleaned_data['username'], frm.cleaned_data['email'], frm.cleaned_data['password1']
new_user = User.objects.create_user(username, email, password)
new_user.is_active = True # if you want to set active
new_user.save()
else:
frm=CreateUserForm()
return render(request,'registration/signup.html',{'form':frm})
def logout_view(request):
logout(request)
return redirect('/')
Expected results are the working form,but actual results are only saving the username, password and email
read the documentation on python super . you are using it in wrong way. calling super at last of the clean method mean it it execute the modelform clean method and code before super get modified .
The title of your question is
How can i create custom users with only forms?
The answer to that is: That's not possible. Whatever you want to save to the database, you have to have a corresponding model for it. Forms only determine what data is submitted by the clients and how that data is processed.
So if you want to save columns age and info for your users, you'll have to create a Model and the corresponding table and columns in your database to hold these values.
Now, the default Django User model only takes username, email and password (plus some booleans like is_staff, is_superuser, is_active etc...). If you want to save additional information, you have a few options that are well described in the official docs, either by extending the default User model or by substituting it.
The recommendation when starting a new Django project is to always at least substitute with your own User model (see this). If you will only ever have one type of user, then add the age and info fields on your custom User model. But if there's any chance that you might have different types of users where age and info might not be relevant in the future, extend your custom User model with a profile.
I'm trying to learn auth django system and I have several questions.
I want to create multiple users with email authentication. So, I used AbtractBaseUser and AbtractBaseManager. I read django doc and this question on the website : Implementing multiple user types with Django 1.5
and I would like to use it. So, I try to implement the second point : all fields for my two user types are in 1 models and with user_type variable I can choose fields to show. My code is down below :
models.py
# -*- coding: utf-8 -*-
from django.db import models
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser
)
TYPE_USER = (
('student', _('Student')),
('employee', _('Employee')),
)
class MyuserManager(BaseUserManager):
def create_user(self, email, last_name, first_name, date_of_birth, user_type, password=None):
if not email:
raise ValueError("users must have an email address")
user = self.model(
email = self.normalize_email(email),
last_name = last_name,
first_name = first_name,
date_of_birth = date_of_birth,
user_type = user_type,
)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, password, last_name, first_name, date_of_birth, user_type):
user = self.create_user(email,
password = password,
date_of_birth = date_of_birth,
last_name = last_name,
first_name = first_name,
user_type = user_type,
)
user.is_admin = True
user.is_staff = True
user.save()
return user
class MyUser(AbstractBaseUser):
"""Based model user"""
email = models.EmailField(
verbose_name = 'email',
max_length=255,
unique=True,
)
last_name = models.CharField(max_length=30)
first_name = models.CharField(max_length=30)
date_of_birth = models.DateField()
user_type = models.CharField(_('Type'), choices=TYPE_USER, max_length=20, default='student')
phone = models.CharField(max_length=20, blank=True)
friends = models.ManyToManyField("self", blank=True)
faculty = models.ForeignKey(Faculty, null=True, blank=True)
desk = models.CharField(max_length=30, blank=True)
campus = models.ForeignKey(Campus, null=True, blank=True)
job = models.ForeignKey(Function, null=True, blank=True)
cursus = models.ForeignKey(Cursus, null=True, blank=True)
year = models.IntegerField(null=True, blank=True)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
objects = MyuserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['last_name', 'first_name', 'date_of_birth', 'user_type']
Question : Do you think it's correct ? or should i create 1 class with common fields and 2 classes for distinct type of user ?
Next, i would like to use my own backend so i implement a backend.py file in my project
backend.py
from fbLike.models import MyUser
from django.contrib.auth.backends import ModelBackend
class EmailAuthBackend(ModelBackend):
def authenticate(self, email=None, password=None, **kwargs):
try:
user = MyUser.objects.get(email=email)
if user.check_password(password):
return user
except MyUser.DoesNotExist:
return None
def get_user(self, user_id):
try:
return MyUser.objects.get(pk=user_id)
except:
return None
For me, it seems that is a correct implementation.
Questions : But, how to proceed with multi-table inheritence ? is it possible ? how to know what is the type of user before check password if i have for example :
class BaseUser(AbstractBaseUser):
email = models.EmailField(max_length=254, unique=True)
# common fields
class Student(BaseUser):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
# ...
class employee(BaseUser):
company_name = models.CharField(max_length=100)
# ...
Finally, with my models.py, backend.py i would try to use the CRUD system in my views.py to create and update users.
Below my files :
forms.py
class StudentProfileForm(forms.ModelForm):
phone = forms.CharField(required=True)
faculty = forms.ModelChoiceField(Faculty.objects.all())
campus = forms.ModelChoiceField(Campus.objects.all())
cursus = forms.ModelChoiceField(Cursus.objects.all())
year = forms.IntegerField(required=True)
class Meta:
model = MyUser
fields = ('email', 'password', 'first_name', 'last_name',
'date_of_birth', 'phone', 'faculty', 'campus', 'cursus', 'year'
)
widgets = {
'password': forms.PasswordInput(render_value=True),
}
class EmployeeProfileForm(forms.ModelForm):
date_of_birth = forms.DateField(widget = AdminDateWidget)
phone = forms.CharField(required=True)
faculty = forms.ModelChoiceField(Faculty.objects.all())
campus = forms.ModelChoiceField(Campus.objects.all())
job = forms.ModelChoiceField(Function.objects.all())
desk = forms.IntegerField(required=True)
class Meta:
model = MyUser
fields = ('email', 'password', 'first_name', 'last_name',
'date_of_birth', 'phone', 'faculty', 'campus', 'job', 'desk'
)
widgets = {
'password': forms.PasswordInput(render_value=True),
}
Here, I think it's too complex but i don't know how to reduce complexity.
views.py
class RegisterProfile(CreateView):
model = MyUser
template_name = 'fbLike/user_profile.html'
form_class = StudentProfileForm
second_form_class = EmployeeProfileForm
def get_object(self):
return super(RegisterProfile, self).get_object()
def get_success_url(self):
return reverse('login',)
def get_context_data(self, **kwargs):
context = super(RegisterProfile, self).get_context_data(**kwargs)
if 'studenForm' not in context:
context['studentForm'] = self.form_class
if 'employeeForm' not in context:
context['employeeForm'] = self.second_form_class
return context
def form_valid(self, form):
self.object = MyUser.objects.create_user(**form)
return super(RegisterProfile, self).form_valid(form)
def form_invalid(self, form):
return super(RegisterProfile, self).form_invalid(form)
def post(self, request, *args, **kwargs):
self.object = None
if request.POST['profileType'] == 'student':
form = self.form_class(request.POST, prefix='st')
form.user_type = 'student'
else:
form = self.second_form_class(request.POST, prefix='em')
form.user_type = 'employee'
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
class UpdateProfile(UpdateView):
model = MyUser
template_name = 'fbLike/modify_profile.html'
def get_form_class(self):
if self.request.user.user_type == 'student':
return StudentProfileForm
return EmployeeProfileForm
def get_success_url(self):
return reverse('welcome',)
For CreateProfile, I would like to send 2 forms in my template and thanks to javscript, i can choose the correct form. But when i submit the form, i have an error with the user password. When i check my users in the admin page, the hashpassword system seems to fail.
So, my first attempt to solve : it was to override save method in my form to use the request.user.set_password but it doesn't work.
I know it's a long explanation. But everyone could give me an example of CreateView class with 2 forms please ? If it isn't possible to use a class but a function how to implement this function ?
I thank you in advance
I will attempt to answer your multiple questions, but please in the future - one question per post. I realize you are new, but StackOverflow is not a discussion forum, have a look at the help section (link at the bottom of each page) specifically the section on asking questions.
I want to create multiple users with email authentication. [...] Do
you think it's correct ? or should i create 1 class with common fields
and 2 classes for distinct type of user ?
Its correct if it works with you. These kinds of questions are better suited for codereview.stackexchange.com. Especially as there isn't a bug.
But, how to proceed with multi-table inheritence ? is it possible ?
how to know what is the type of user before check password if i have
for example [...]
First, authentication backends are different topic altogether; they don't have anything to do with multiple table inheritance (I think you mean relationships or multiple user profiles).
You only need multiple authentication backends if your password authentication algorithm is different for each user type. If all user passwords are stored in the same location/system/database, then you don't need multiple authentication backends.
You should implement a method in your base user class, that returns the type of user. You can then use user_passes_check decorator to restrict access in your views.
This way you will achieve what I think is what you are after "this section only for students", etc.
Finally, with my models.py, backend.py i would try to use the CRUD system in my views.py to create and update users [..] Here, I think it's too complex but i don't know how to reduce
complexity.
You don't need to repeat all the fields of your model in your ModelForm, you only need to override fields if you need to modify them, or to add extra "non-model" fields to the form. You can start with the simple ModelForm, like this:
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ('name', 'email', 'password')
A common use of adding an additional field is to ask for password confirmation, by having an extra password field.
For CreateProfile, I would like to send 2 forms in my template and
thanks to javscript, i can choose the correct form. But when i submit
the form, i have an error with the user password. When i check my
users in the admin page, the hashpassword system seems to fail. So, my
first attempt to solve : it was to override save method in my form to
use the request.user.set_password but it doesn't work.
Use a form wizard to implement a multi-step registration process. The first step, you ask the basic information and account type to be created; on the second step, you only send the relevant form instead of sending two forms and then switching them around using javascript.
I don't see anywhere in your code where you use set_password, so can't comment on the second part.