I'd like to allow my users to change their emails. Is there a plugin to allow me to do this? I tried "django-change-email 0.1.2", but it doesn't appear to work. I've fixed some outdated errors, and it asked to update the database after that. I did so, but the database didn't appear to show any new tables to change the email.
Basically, I'd like for users to update their email address themselves. The server will then send a confirmation email containing a unique hash. Clicking on this will validate the change and save the email. Is this possible with some other plugin? Thank you!
The best way to allow users to change their email address is to create a separate UserProfile models which can be used to store email address. Example code is shown below.
class UserProfileForm(ModelForm):
def __init__(self, *args, **kwargs):
super(UserProfileForm, self).__init__(*args, **kwargs)
try:
self.fields['email'].initial = self.instance.user.email
except User.DoesNotExist:
pass
email = forms.EmailField(label="Primary email")
class Meta:
model = Parent
def save(self, *args, **kwargs):
"""
Update the primary email address on the related User object as well.
"""
u = self.instance.user
u.email = self.cleaned_data['email']
u.save()
profile = super(UserProfileForm, self).save(*args,**kwargs)
return profile
This way, you can ensure that the new email address remains inactive until the user has clicked on verify email address link which you will be sending to the user. Hope I answered your question.
Related
I have a feature on my website where a user can share content with another user registered on the site. They do this by entering in an email belonging to another user. This is then posted, setting the desired user to as a shared owner of content in the model.
What is the best way to check that the email address belongs to a registered user of the site?
Thanks!
I think the efficient way is to search for the user with the given mail. Django User already has a mail field that is unique.
if you want to write from basic:
from django.core.validators import validate_email
class SampleForm(forms.Form):
mail = forms.CharField(max_length=50)
def clean(self):
cleaned_data = super(SampleForm, self).clean()
mail = cleaned_data.get('mail')
# validate the structure of the mail address
try:
validate_email(mail)
except validate_email.ValidationError:
raise forms.ValidationError('email is not valid')
# now find if mail has registered
try:
User.objects.get(email=mail)
except User.DoesNotExist:
raise forms.ValidationError('This mail address is not registered')
return cleaned_data
I have successfully setup django-allauth along with a custom user model which let's users sign in directly using email and password or through Facebook, in which case email is taken from Facebook and saved in the Email field of the custom user model. I have also created a mobile field which stays empty as of now.
I want to allow users to log in using their Facebook, Email or MOBILE. Unfortunately that field is not unique=True in the model. I have thought of capturing the mobile and fetching the associated email address and then use that along with the password to log in any user.
However, I don't know how to extend the SIGN IN form that comes with django-allauth or the block of code that signs a user in where I can change it to meet my need.
As of now I don't find any of my current code relevant to this problem, but if it helps I am willing to provide it upon mention.
The following solution worked for me. I put the code in the forms.py file.
from allauth.account.forms import LoginForm
from auth_project import settings
class CustomLoginForm(LoginForm):
def __init__(self, *args, **kwargs):
super(LoginForm, self).__init__(*args, **kwargs)
if settings.ACCOUNT_AUTHENTICATION_METHOD == "email":
login_widget = forms.TextInput(attrs={'type': 'text',
'placeholder':
('Mobile Number'),
'autofocus': 'autofocus'})
login_field = forms.CharField(label=("Mobile"),
widget=login_widget)
self.fields["login"] = login_field
set_form_field_order(self, ["login", "password", "remember"])
def user_credentials(self):
credentials = {}
mobile = self.cleaned_data["login"]
login = CustomUser.objects.filter(mobile=mobile).values('email')[0]['email']
if settings.ACCOUNT_AUTHENTICATION_METHOD == "email":
credentials["email"] = login
credentials["password"] = self.cleaned_data["password"]
return credentials
# this is to set the form field in order
# careful with the indentation
def set_form_field_order(form, fields_order):
if hasattr(form.fields, 'keyOrder'):
form.fields.keyOrder = fields_order
else:
# Python 2.7+
from collections import OrderedDict
assert isinstance(form.fields, OrderedDict)
form.fields = OrderedDict((f, form.fields[f])
for f in fields_order)
Thanks.
I've created a User model for my django app
class User(Model):
"""
The Authentication model. This contains the user type. Both Customer and
Business models refer back to this model.
"""
email = EmailField(unique=True)
name = CharField(max_length=50)
passwd = CharField(max_length=76)
user_type = CharField(max_length=10, choices=USER_TYPES)
created_on = DateTimeField(auto_now_add=True)
last_login = DateTimeField(auto_now=True)
def __unicode__(self):
return self.email
def save(self, *args, **kw):
# If this is a new account then encrypt the password.
# Lets not re-encrypt it everytime we save.
if not self.created_on:
self.passwd = sha256_crypt.encrypt(self.passwd)
super(User, self).save(*args, **kw)
I've also created an authentication middleware to use this model.
from accounts.models import User
from passlib.hash import sha256_crypt
class WaitformeAuthBackend(object):
"""
Authentication backend fo waitforme
"""
def authenticate(self, email=None, password=None):
print 'authenticating : ', email
try:
user = User.objects.get(email=email)
except User.DoesNotExist:
user = None
if user and sha256_crypt.verify(password, user.passwd):
return user
else:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
I have ammended the settings.py file correctly and if I add some print statements to this backend I can see the user details print out. I don't recall reading that I need to implement is_authenticated in the django docs. Am I missing something silly?
I'm not quite sure why you have created a new User model instead of using Django's built-in one and adding a linked UserProfile, which is the recommended thing to do (until 1.5 is released, when pluggable user models will be available). However, yes you need to define an is_authenticated method, which always returns True: this is exactly what the built-in model does. The reason is that if you have an actual User, it will always be authenticated: otherwise, you will have an AnonymousUser object, whose is_authenticated method always returns False.
you dont have to reinvent the wheel. Just use Djangos build in authentication system and save yourself a lot of trouble. You can also extend it to your needs or use different authentication backends. Have a read here. HTH.
How to setup email confirmation in Pinax?, Do I need to setup SMTP server or something like that?, I tried to find documentation about that but failed. Can anyone redirect me to documentation or any related article explaning about this?, Pinax uses emailconfirmation app. I browsed the code of emailconfirmation, But It doesn't include any settings about host or server.
The integration of emailconfirmation is pretty straightforward. You don't have to set up any mailserver, if you have an existing mailserver that you can use to send your mails. You just have to fill in the data used for sending the mails in your standard Django settings, and emailconfirmation is going to use that:
# e-mail settings
# XXXXXXXXXXXXXXXXXXXXXXX THESE ARE NOT YET PRODUCTIONREADY!
EMAIL_HOST='mail.your_mailserver.com'
EMAIL_PORT=1025
EMAIL_HOST_USER='your_username'
EMAIL_HOST_PASSWORD='your_password'
To summarize what to do next: You have to create a form for entering an email-adress (The reason why such a form doesn't come with emailconfiguration is somewhat obscure). This can look something like this:
# email form using emailconfirmation
class AddEmailForm(forms.Form):
def __init__(self, *args, **kwargs):
try:
self.user = kwargs.pop('user')
except KeyError:
pass
super(AddEmailForm, self).__init__(*args, **kwargs)
email = forms.EmailField(label="Email",required=True, widget=forms.TextInput())
def clean_email(self):
try:
EmailAddress.objects.get(user=self.user, email=self.cleaned_data["email"])
except EmailAddress.DoesNotExist:
try:
User.objects.get(email = self.cleaned_data['email'])
except User.DoesNotExist:
return self.cleaned_data["email"]
raise forms.ValidationError(u"email address associated with another account.")
def save(self):
try:
self.user.message_set.create(message="Confirmation email sent to %s" % self.cleaned_data["email"])
except AttributeError:
pass
return EmailAddress.objects.add_email(self.user, self.cleaned_data["email"])
This will allow a user to enter an email address, check if the email-adress allready exists and is in use by another account. After that it will add the emailadress to the unconfirmed emailadresses and send an email with a link to the user. The user then can confirm the emailadress by clicking on the link.
That's all there is to it. Let's hope the Pinax guys will do a quality offensive on their docs soon ;)
I have run into the following error trying to create a user in django:
>>> email = 'verylongemail#verylongemail.com'
>>> user_object = User.objects.create_user(username=email, email=email, password='password')
Data truncated for column 'username' at row 1
It seems Django has a limit on the number of chars allowed in a username. How would I get around this?
I've had to modify the auth_user table by hand to make the field longer and then convert emails into a username by removing the # symbol and the period (maybe other characters too, it's really not a great solution). Then, you have to write a custom auth backend that authenticates a user based on their email, not the username, since you just need to store the username to appease django.
In other words, don't use the username field for auth anymore, use the email field and just store the username as a version of the email to make Django happy.
Their official response on this topic is that many sites prefer usernames for auth. It really depends if you are making a social site or just a private site for users.
If you override the form for Django users you can actually pull this off pretty gracefully.
class CustomUserCreationForm(UserCreationForm):
"""
The form that handles our custom user creation
Currently this is only used by the admin, but it
would make sense to allow users to register on their own later
"""
email = forms.EmailField(required=True)
first_name = forms.CharField(required=True)
last_name = forms.CharField(required=True)
class Meta:
model = User
fields = ('first_name','last_name','email')
and then in your backends.py you could put
class EmailAsUsernameBackend(ModelBackend):
"""
Try to log the user in treating given username as email.
We do not want superusers here as well
"""
def authenticate(self, username, password):
try:
user = User.objects.get(email=username)
if user.check_password(password):
if user.is_superuser():
pass
else: return user
except User.DoesNotExist: return None
then in your admin.py you could override with
class UserCreationForm(CustomUserCreationForm):
"""
This overrides django's requirements on creating a user
We only need email, first_name, last_name
We're going to email the password
"""
def __init__(self, *args, **kwargs):
super(UserCreationForm, self).__init__(*args, **kwargs)
# let's require these fields
self.fields['email'].required = True
self.fields['first_name'].required = True
self.fields['last_name'].required = True
# let's not require these since we're going to send a reset email to start their account
self.fields['username'].required = False
self.fields['password1'].required = False
self.fields['password2'].required = False
Mine has a few other modifications, but this should get you on the right track.
You have to modify the username length field so that syncdb will create the proper length varchar and you also have to modify the AuthenticationForm to allow greater values as well or else your users won't be able to log in.
from django.contrib.auth.forms import AuthenticationForm
AuthenticationForm.base_fields['username'].max_length = 150
AuthenticationForm.base_fields['username'].widget.attrs['maxlength'] = 150
AuthenticationForm.base_fields['username'].validators[0].limit_value = 150