This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Accepting email address as username in Django
The authentication model provided along with Django is based on
username.
What to do to change the authentication based on email instead of
username?
To be more specific:
With username authentication, to login user we do the following:
user = authenticate(name,password)
.......
login(request,user)
What to write for the above statements if we are authenticating
using email?
For form:
I am planning to write my own form which shows the fields
email, password and the validation.
Is this the correct approach?
I found this snippet when reading a duplicate question to this one. Also check this code:
class UserForm( forms.ModelForm ):
class Meta:
model= User
exclude= ('email',)
username = forms.EmailField(max_length=64,
help_text = "The person's email address.")
def clean_email( self ):
email= self.cleaned_data['username']
return email
class UserAdmin( admin.ModelAdmin ):
form= UserForm
list_display = ( 'email', 'first_name', 'last_name', 'is_staff' )
list_filter = ( 'is_staff', )
search_fields = ( 'email', )
admin.site.unregister( User )
admin.site.register( User, UserAdmin )
Neither answer is originally mine. Up vote on the other thread to the owners for the karma boost. I just copied them here to make this thread as complete as possible.
Check out this snippet, and read the comments for updates.
For the form, why not just inherit from (or directly use) the auth login form. See django/contrib/auth/forms.py
Please see the below link which illustrates the way in which we should solve the problem.
http://groups.google.com/group/django-users/browse_thread/thread/c943ede66e6807c
Sounds like you can just mask the username with the word "email" and all the usernames will just have the email show up instead.
Unless I missed something, the solution should be really simple; just make a normal form with a text field and a password field. When the HTTP request method is POST, try to fetch the user with the given e-mail address. If such a user doesn't exist, you have an error. If the user does exist, try to authenticate the user and log her in.
Related
I created a user with a password password123 but in the database the password field look like this pbkdf2_sha256$260000$rJZWVrYXlokRG8fGMS1fek$S7Dm9soflUsy0Q74CJP8sB60tgfRWuRPdqj5XL0DBV0=
the problem: is when I create new user via rest framework i got the poassword feidl look like passsword123
so how should i created new user in order to keep the django password encoding functionality
also how to deactivate this password encoding functionality
Django uses encryption middlewares to encrypt passwords (since the database sees passwords as VarChar fields, so Django's model sees them as plain text unless it is told otherwise). If you want the Django User model to use encryption, you must call
user_obj.set_password(passwd_text)
With this line of code, you tell Django to run encryption algorithms. For example, in your case, you can first use the serializer's extra_kwargs to exclude passwords from database-readable data, then create the user.
class CreateUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['email', 'username', 'password']
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
password = validated_data.pop("password")
user = User(**validated_data)
user.set_password(password)
user.save()
return user
if you want to read more on Django users and passwords read these docs
user model doc and
encryption types and password management doc
you need to override create method in User Create Serializer:
class UserCreateSerializer(serializers.ModelSerializer):
def create(self, validated_data):
user = User.objects.create_user(**validated_data)
return user
class Meta:
model = User
fields = "__all__" # or your specific fields
extra_kwargs = {
"password": {"write_only": True},
}
Now your user password will be saved as hashed password in database.
re. question 2.
Django does not store the password, only hashed value of the password, which it uses for comparison when the user logs in.
It is not possible to reverse engineer the password from this hash (well, mathematically it is possible, but you don't have the resources to run that process). Nor should it be.
If you wanted a password field that wasn't hashed, you would use just a string field. But... DON'T DO THIS! It is massively insecure.
There is no need for you to know the user's password.
As for question 1, I'm not sure why you're not seeing it hashed - you will need to post some code.
I want to register a user in django-rest-auth, if all username, password and email fields are provided. (I want to implement token authentication too to obtain a JSON response from the server.)
The default email field in django.contrib.auth.User is optional. But I want to set the email filed as required to register in the database so that the user gets an HTTP error response when a POST request has been made without an email.
In the project I am registering the new user through the following code.
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', User.USERNAME_FIELD, "password", 'full_name',
'is_active', 'links', )
write_only_fields = ('password',)
read_only_fields = ('id',)
def create(self, validated_data):
print("Validated Data",validated_data)
if 'email' in validated_data:
user = get_user_model().objects.create(
username=validated_data['username']
)
user.set_email(validated_data['email'])
user.set_password(validated_data['password'])
user.save()
email = EmailMessage('Hello', 'World',
to=[validated_data['email']])
email.send()
user.is_active=False
return user
else:
return None
However, the above gives:
create() did not return an object instance
How do I set the email field as a required field?
I want to register an user in django-rest-auth, if all username, password and email fields are provided.
The correct way to require a field on a serializer in Django REST framework is to set required=True when initializing the field, or using the extra_kwargs parameter to set it on automatically generated fields.
In default email field in django.contrib.auth.User is optional. But I want to set the email filed as required to register in the database so that the user gets HTTP error response when POST request has been made without a email.
This means that the automatically generated field will not be required by default, but you can still override it with required=True.
However, the above gives:
create() did not return an object instance
This is because you are not returning a User instance from the create method, just like it says on the tin. Specifically, you are returning None if the email field is not included. None is not a User instance, and DRF is warning you that you're doing something wrong.
I have a setup with django-rest-framework, rest-auth, allauth and facebook Oauth2. My problem here is that when I created an user using the facebook endpoint, the social user was created, the django user was also created but both have no username. Should I configure this somewhere?
The username field is no longer present, because it was deprecated with Graph API v2.0 one year ago...
See
https://developers.facebook.com/docs/apps/changelog#v2_0_graph_api
/me/username is no longer available.
Check if you're using the newest versions of your libraries.
you can set custom username by overriding DefaultSocialAccountAdapter
i just removed # from email and replaced by _ to generate username
#in setting added this
SOCIALACCOUNT_ADAPTER = 'trip2.users.adapter.SocialAdapter'
# and then override the Adapter
class SocialAdapter(DefaultSocialAccountAdapter):
def populate_user(self,
request,
sociallogin,
data):
"""
Hook that can be used to further populate the user instance.
For convenience, we populate several common fields.
Note that the user instance being populated represents a
suggested User instance that represents the social user that is
in the process of being logged in.
The User instance need not be completely valid and conflict
free. For example, verifying whether or not the username
already exists, is not a responsibility.
"""
username = data.get('username')
first_name = data.get('first_name')
last_name = data.get('last_name')
email = data.get('email')
name = data.get('name')
user = sociallogin.user
emailtouname = email.split('#')[0] + "_" + email.split('#')[1].split('.')[0]
user_email(user, valid_email_or_none(email) or '')
name_parts = (name or '').partition(' ')
user_field(user, 'first_name', first_name or name_parts[0])
user_field(user, 'last_name', last_name or name_parts[2])
user_username(user, username or emailtouname)
return user
You can pass a 'username' key along with other data retrieved via Facebook API. Or you can dig into allauth/socialaccount/adapter.py populate_user() method and customize the 'username' field (I simply make it equal to user email)
I want to increase the length of the username in django from 30 to around 80, I know it may be duplicate question but the previous answers are not working, for example https://kfalck.net/2010/12/30/longer-usernames-for-django
this is for Django 1.2.
Did anyone try similar hack for Django>1.5
Thanks in advance
In Django 1.5 and above, the recommended approach would be to create a custom user model. Then you can make the username field exactly as you want.
I had the same problem few days ago. Finally, I ended just with cutting off first 30 characters of the (old) username (into the new database table), and adding a custom authentication backend that will check the email instead of user name. Terrible hack I know, and I'm planning to fix it as soon as I have some time. The idea is following:
I already have a model class that has one-to-one relation with djangos auth.User. I will add another field there called full_username.
class MyCustomUserModel(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL, related_name="custom_user")
full_username = models.CharField(max_length=80, ...)
...
Then, I'll add another custom authentication backend that will check this field as username. It would look something like this:
from django.contrib.auth.backends import ModelBackend
class FullUsernameAuthBackend(ModelBackend):
def authenticate(self, username=None, password=None, **kwargs):
UserModel = get_user_model()
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
try:
user = UserModel._default_manager.filter(custom_user__full_username=username)
# If this doesn't work, will use (the second case):
# user = MyCustomUserModel.objects.filter(full_username=username).user
if user.check_password(password):
return user
except UserModel.DoesNotExist:
# Adding exception MyCustomUserModel.DoesNotExist in "(the second case)"
# Run the default password hasher once to reduce the timing
# difference between an existing and a non-existing user (#20760).
UserModel().set_password(password)
After this, you need to change settings.py:
AUTHENTICATION_BACKENDS = (
"....FullUsernameAuthBackend",
# I will have the email auth backend here also.
)
I hope that it will work.
Custom User Models are a huge change to make and aren't always compatible with apps. I solved it by running this very pragmatic migration. Note this only solves it at the database level.
migrations.RunSQL("alter table auth_user alter column username type varchar(254);")
I need to patch the standard User model of contrib.auth by ensuring the email field entry is unique:
User._meta.fields[4].unique = True
Where is best place in code to do that?
I want to avoid using the number fields[4]. It's better to user fields['email'], but fields is not dictionary, only list.
Another idea may be to open a new ticket and upload a patch with new parameter inside settings.py:
AUTH_USER_EMAIL_UNIQUE = True
Any suggestions on the most correct way to achieve email address uniqueness in the Django User model?
Caution:
The code below was written for an older version of Django (before Custom
User Models were introduced). It contains a race condition, and
should only be used with a Transaction Isolation Level of SERIALIZABLE
and request-scoped transactions.
Your code won't work, as the attributes of field instances are read-only. I fear it might be a wee bit more complicated than you're thinking.
If you'll only ever create User instances with a form, you can define a custom ModelForm that enforces this behavior:
from django import forms
from django.contrib.auth.models import User
class UserForm(forms.ModelForm):
class Meta:
model = User
def clean_email(self):
email = self.cleaned_data.get('email')
username = self.cleaned_data.get('username')
if email and User.objects.filter(email=email).exclude(username=username).exists():
raise forms.ValidationError(u'Email addresses must be unique.')
return email
Then just use this form wherever you need to create a new user.
BTW, you can use Model._meta.get_field('field_name') to get fields by name, rather than by position. So for example:
# The following lines are equivalent
User._meta.fields[4]
User._meta.get_field('email')
UPDATE
The Django documentation recommends you use the clean method for all validation that spans multiple form fields, because it's called after all the <FIELD>.clean and <FIELD>_clean methods. This means that you can (mostly) rely on the field's value being present in cleaned_data from within clean.
Since the form fields are validated in the order they're declared, I think it's okay to occasionally place multi-field validation in a <FIELD>_clean method, so long as the field in question appears after all other fields it depends on. I do this so any validation errors are associated with the field itself, rather than with the form.
What about using unique_together in a "different" way? So far it works for me.
class User(AbstractUser):
...
class Meta(object):
unique_together = ('email',)
Simply use below code in models.py of any app
from django.contrib.auth.models import User
User._meta.get_field('email')._unique = True
In settings module:
# Fix: username length is too small,email must be unique
from django.contrib.auth.models import User, models
User._meta.local_fields[1].__dict__['max_length'] = 75
User._meta.local_fields[4].__dict__['_unique'] = True
It's amazing, but I found a best solution for me!
django-registration have form with checking uniqueness of email field: RegistrationFormUniqueEmail
example of usage here
Your form should look something like this.
def clean_email(self):
email = self.cleaned_data.get('email')
username = self.cleaned_data.get('username')
print User.objects.filter(email=email).count()
if email and User.objects.filter(email=email).count() > 0:
raise forms.ValidationError(u'This email address is already registered.')
return email
To ensure a User, no matter where, be saved with a unique email, add this to your models:
#receiver(pre_save, sender=User)
def User_pre_save(sender, **kwargs):
email = kwargs['instance'].email
username = kwargs['instance'].username
if not email: raise ValidationError("email required")
if sender.objects.filter(email=email).exclude(username=username).count(): raise ValidationError("email needs to be unique")
Note that this ensures non-blank email too. However, this doesn't do forms validation as would be appropriated, just raises an exception.
Django has a Full Example on its documentation on how to substitute and use a Custom User Model, so you can add fields and use email as username.
One possible way to do this is to have a pre-save hook on the User object and reject the save of the email already exists in the table.
I think that the correct answer would assure that uniqueness check was placed inside the database (and not on the django side). Because due to timing and race conditions you might end with duplicate emails in the database despite having for example pre_save that does proper checks.
If you really need this badly I guess you might try following approach:
Copy User model to your own app, and change field email to be unique.
Register this user model in the admin app (using admin class from django.contrib.auth.admin)
Create your own authentication backend that uses your model instead of django one.
This method won't make email field unique at the database level, but it's worth trying.
Use a custom validator:
from django.core.exceptions import ValidationError
from django.contrib.auth.models import User
def validate_email_unique(value):
exists = User.objects.filter(email=value)
if exists:
raise ValidationError("Email address %s already exists, must be unique" % value)
Then in forms.py:
from django.contrib.auth.models import User
from django.forms import ModelForm
from main.validators import validate_email_unique
class UserForm(ModelForm):
#....
email = forms.CharField(required=True, validators=[validate_email_unique])
#....
Add the below function in any of the models.py file. Then run makemigrations and migrate. Tested on Django1.7
def set_email_as_unique():
"""
Sets the email field as unique=True in auth.User Model
"""
email_field = dict([(field.name, field) for field in MyUser._meta.fields])["email"]
setattr(email_field, '_unique', True)
#this is called here so that attribute can be set at the application load time
set_email_as_unique()
Since version 1.2 (May 11th, 2015) there has been a way to dynamically import any chosen registration form using the settings option REGISTRATION_FORM.
So, one could use something like this:
REGISTRATION_FORM = 'registration.forms.RegistrationFormUniqueEmail'
This is documented here.
And here's the link to the changelog entry.
Django does not allow direct editing User object but you can add pre_save signal and achieve unique email. for create signals u can follow https://docs.djangoproject.com/en/2.0/ref/signals/. then add the following to your signals.py
#receiver(pre_save, sender=User)
def check_email(sender,instance,**kwargs):
try:
usr = User.objects.get(email=instance.email)
if usr.username == instance.username:
pass
else:
raise Exception('EmailExists')
except User.DoesNotExist:
pass
Add somewhere this:
User._meta.get_field_by_name('email')[0]._unique = True
and then execute SQL similar to this:
ALTER TABLE auth_user ADD UNIQUE (email);
The first answer here is working for me when I'm creating new users, but it fails when I try to edit a user, since I am excluding the username from the view. Is there a simple edit for this that will make the check independent of the username field?
I also tried including the username field as a hidden field (since I don't want people to edit it), but that failed too because django was checking for duplicate usernames in the system.
(sorry this is posted as an answer, but I lack the creds to post it as a comment. Not sure I understand Stackoverflow's logic on that.)
You can use your own custom user model for this purpose. You can use email as username or phone as username , can have more than one attribute.
In your settings.py you need to specify below settings
AUTH_USER_MODEL = 'myapp.MyUser'.
Here is the link that can help you .
https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#auth-custom-user
from an User inherited model, redefine the attribute correctly. It should work, as is it's not usefull to have that in django core because it's simple to do.
I went to \Lib\site-packages\django\contrib\auth\models
and in class AbstractUser(AbstractBaseUser, PermissionsMixin):
I changed email to be:
email = models.EmailField(_('email address'), **unique=True**, blank=True)
With this if you try to register with email address already present in the database you will get message: User with this Email address already exists.