Django admin: Unique email verification fails against self - django

Hi Here's a snippet from my admin.py
#admin.py
class UserForm(forms.ModelForm):
class Meta:
model = User
def clean_email(self):
email = self.cleaned_data['email']
if User.objects.filter(email=email).exists():
raise forms.ValidationError("This email already used")
return email
class UserAdmin(admin.ModelAdmin):
form = UserForm
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
I use this to check that a new user cannot be created with an email address already used. The problem is that when I edit an existing user the validation check fails, because there is a user with that mail address, but that's OK because it's the one I'm editing.
How can I tell the form validation to ignore the match against the current user?

Exclude the current instance from your query:
def clean_email(self):
email = self.cleaned_data['email']
if User.objects.filter(email=email).exclude(pk=self.instance.pk).exists():
raise forms.ValidationError("This email already used")
return email

It's much more better to validate uniqueness using unique on model field.
You can use custom User model with unique email constraint.
Look at this for more info about implementing unique validation on your own https://stackoverflow.com/a/1560617/527064

Related

How to make Django form Field Unique?

I have a Signup form Where I want to make data of email and mobile Number Field to be Unique....
class SignUpForm(UserCreationForm):
email = forms.EmailField(max_length=254, help_text='Required. Inform a valid email address.', unique=True)
mobile_no = forms.CharField(validators=[max_length=17, initial='+91', unique=True)
I am Currently using unique=True but it raise Error as...
TypeError: __init__() got an unexpected keyword argument 'unique'
Easiest and fastest way(both for you and server) is to implement it in your model by setting unique=True.
If you want it in form anyway you need to override clean
Cleaning email:
class SignUpForm(UserCreationForm):
...
def clean_email(self):
email = self.cleaned_data['email']
if User.objects.filter(email=email).exists():
raise ValidationError("Email already exists")
return email
Now form.is_valid() will throw error if an user account with given email already exists.
I think you can figure out how to do same thing for mobile number now.

Check Email Validation Django

Can someone explain how to validate an email adress in Django?
So for Example i want to check if an email is a valid college email adress with the ending .edu .
How can i do that?
from django import forms
from .models import SignUp
class SignUpForm(forms.ModelForm):
class Meta:
model = SignUp
fields = ['full_name','email']
def clean_email(self):
email = self.cleaned_data.get('email')
return email
Assuming that your SignUp.email field is an EmailField, Django will take care of validating that it's a valid email address. All you need to do is check that it ends in .edu, and raise a ValidationError if it doesn't.
class SignUpForm(forms.ModelForm):
class Meta:
model = SignUp
fields = ['full_name','email']
def clean_email(self):
email = self.cleaned_data.get('email')
if not email.endswith('.edu'):
raise forms.ValidationError("Only .edu email addresses allowed")
return email
If might be better to create a validator and add it to your model field. This way, Django will run your validator when you use your SignUpForm and when it does model validation in other places like the Django admin.
from django.core.exceptions import ValidationError
def validate_edu_email_address(value):
if email.endswith('.edu'):
raise forms.ValidationError("Only .edu email addresses allowed")
class SignUp(models.Model):
email = models.EmailField(validators=[validate_edu_email_address])
...
Just create a regular expression for your needs, or even better use some standard one.
import re
EMAIL_REGEX = r"(^[a-zA-Z0-9_.+-]+#[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)"
# EMAIL_REGEX = r'\w+#\.edu' # If you only want to allow edu.
class SignUpForm(forms.ModelForm):
...
def clean_email(self):
email = self.cleaned_data.get('email')
if email and not re.match(EMAIL_REGEX, email):
raise forms.ValidationError('Invalid email format')
return email
In fact, even better approach would be to use EmailField as #Alasdair suggested, that would automatically ensures the same for you (unless you really need to limit the email addresses to your custom format).

DjangoRestFramework ModelSerializer: field-level validation is not working

This is my serializers.py (I want to create a serializer for the built-in User model):
from rest_framework import serializers
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', 'password', 'email', )
def validate_username(self, username):
if not re.search(r'^\w+$', username): #checks if all the characters in username are in the regex. If they aren't, it returns None
raise serializers.ValidationError('Username can only contain alphanumeric characters and the underscore.')
try:
User.objects.get(username=username)
except ObjectDoesNotExist:
return username
raise serializers.ValidationError('Username is already taken.')
The issue is, when I try to create a user using a username which already exists, it returns the following dictionary:
{'username': [u'This field must be unique.']}
rather than saying
{'username': [u'Username is already taken']}
I recreated the validate_username function to this (for testing purposes):
def validate_username(self, username):
raise serializers.ValidationError('Testing to see if an error is raised.')
and it doesn't raise an error. Any idea why DjangoRestFramework is ignoring the validate_username function?
Edit: Note that I am using a ModelSerializer (in the tutorial here: http://www.django-rest-framework.org/api-guide/serializers/#validation it talks about field-level validation only for a Serializer, not a ModelSerializer). Note sure if it makes a difference or not.
Field-level validation is called before serializer-level validation.
So model User having username as unique=True, the field-level validation will raise exception because of username being already present. DRF's UniqueValidator does this work of raising exception when a field is not unique.
As per DRF source code,
class UniqueValidator:
"""
Validator that corresponds to `unique=True` on a model field.
Should be applied to an individual field on the serializer.
"""
message = _('This field must be unique.')
Since these validators run before serializer-level validation, your validate_username is never called.
Try adding the following line in your serializer to do this validator working.
class UserSerializer(serializers.ModelSerializer):
username = serializers.CharField(max_length=32)
class Meta:
model = User
fields = ('username', 'password', 'email', )

Creating a login page for custom Django users? [duplicate]

I want to create a SINGLE form which gives the ability to the admin to create a new user with extended profile. Please note that, I don't want to use admin and registration apps.
I have extended the user with the UserProfile model. I have read all the documents related to extending user profile. But, I really don't know how to save these information.
I coded the following django form for this issue:
class CreateUserForm(forms.Form):
username = forms.CharField(max_length=30)
first_name = forms.CharField()
last_name = forms.CharField()
password1=forms.CharField(max_length=30,widget=forms.PasswordInput()) #render_value=False
password2=forms.CharField(max_length=30,widget=forms.PasswordInput())
email=forms.EmailField(required=False)
title = forms.ChoiceField(choices=TITLE_CHOICES)
def clean_username(self): # check if username dos not exist before
try:
User.objects.get(username=self.cleaned_data['username']) #get user from user model
except User.DoesNotExist :
return self.cleaned_data['username']
raise forms.ValidationError("this user exist already")
def clean(self): # check if password 1 and password2 match each other
if 'password1' in self.cleaned_data and 'password2' in self.cleaned_data:#check if both pass first validation
if self.cleaned_data['password1'] != self.cleaned_data['password2']: # check if they match each other
raise forms.ValidationError("passwords dont match each other")
return self.cleaned_data
def save(self): # create new user
new_user=User.objects.create_user(username=self.cleaned_data['username'],
first_name=self.cleaned_data['first_name'],
last_name=self.cleaned_data['last_name'],
password=self.cleaned_data['password1'],
email=self.cleaned_data['email'],
)
return new_user
Is it OK? however it gives me an error in first_name and last_name. Says django doesn't expect first_name and last_name in save() method.
create_user only supports the username, email and password arguments. First call create_user, then add the extra values to the saved object.
new_user=User.objects.create_user(self.cleaned_data['username'],
self.cleaned_data['email'],
self.cleaned_data['password1'])
new_user.first_name = self.cleaned_data['first_name']
new_user.last_name = self.cleaned_data['last_name']
new_user.save()

Changing user's email address

I am building simple form which allows user to change his basic (first and last name, email) data.
I want to be sure that:
emails still unique across database
user can leave his email untouched
user can change his email
I wanted to use ModelForm for this. I've finished with something like:
class UserDataForm(ModelForm):
class Meta:
model = User
fields = ['first_name', 'last_name', 'email']
def clean_email(self):
cd = self.cleaned_data
email = cd['email']
# Not sure how to check is there is an other account which uses this email EXCEPT this particular user account
I need to show validation error message when there is another account which uses same email AND this account isn't owned by user who is filling the form.
I don't know how to achieve this.
Try this:
class UserDataForm(ModelForm):
class Meta:
model = User
fields = ['first_name', 'last_name', 'email']
def clean_email(self):
cd = self.cleaned_data
email = cd['email']
# object is exists and email is not modified, so don't start validation flow
if self.instance.pk is not None and self.instance.email == email:
return cd
# check email is unique or not
if User.objects.filter(email=value).exists():
raise forms.ValidationError("Email address {} already exists!".format(value))
return cd
Look at this question, I think it will be helpful.
Another way try to check email in clean method:
def clean(self):
cleaned_data = self.cleaned_data
if 'email' in self.changed_data and User.objects.filter(email=value).exists():
raise forms.ValidationError("Email address {} already exists!".format(value))
return cleaned_data