Django 1.5: UserCreationForm & Custom Auth Model - django

I'm using Django 1.5 & Python 3.2.3.
I've got a custom Auth setup, which uses an email address instead of a username. There's no username defined in the model at all. That works fine. Yet, when I build a user creation form, it adds in a username field anyway. So I tried defining exactly which fields I want displayed, but it's still forcing a username field into the form anyway.... even tho it doesn't even exist in the custom auth model. How can I make it stop doing that?
My form for this is defined like so:
class UserCreateForm(UserCreationForm):
class Meta:
model = MyUsr
fields = ('email','fname','surname','password1','password2',
'activation_code','is_active')
At the docs, the Custom Users and Builtin Forms says it "Must be re-written for any custom user model." and I think that's what I'm doing here. Neither this, nor the UserCreationForm documentation say anything more about this though. So I don't know what I'm missing. I didn't find anything via Google either.

Your UserCreationForm should look something like
# forms.py
from .models import CustomUser
class UserCreationForm(forms.ModelForm):
password1 = forms.CharField(label="Password", widget=forms.PasswordInput)
password2 = forms.CharField(label="Password confirmation", widget=forms.PasswordInput)
class Meta:
model = CustomUserModel
# Note - include all *required* CustomUser fields here,
# but don't need to include password1 and password2 as they are
# already included since they are defined above.
fields = ("email",)
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
msg = "Passwords don't match"
raise forms.ValidationError("Password mismatch")
return password2
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
You'll also want a user change form, which won't overrwrite the password field:
class UserChangeForm(forms.ModelForm):
password = ReadOnlyPasswordHashField()
class Meta:
model = CustomUser
def clean_password(self):
# always return the initial value
return self.initial['password']
Define these in your admin like this:
#admin.py
from .forms import UserChangeForm, UserAddForm
class CustomUserAdmin(UserAdmin):
add_form = UserCreationForm
form = UserChangeForm
You'll also need to override list_display, list_filter, search_fields, ordering, filter_horizontal, fieldsets, and add_fieldsets (everything in django.contrib.auth.admin.UserAdmin that mentions username, I think I listed all of it).

You need to create your form from sctratch, it should not extend the UserCreationForm. The UserCreationForm have a username field explicitly defined in it as well as some other fields. You can look at it here.

Related

Validation for model forms

I am trying to do validation for model forms to check if both 'email' and 'confirm_email' have same value. I tried searching online but getting some errors. I am making custom validators in models.py file.
Can you please help me with that. What would be the best way of validating model forms.
Here is my code.
MODELS.PY
from django.db import models
from django.core import validators
from django.core.exceptions import ValidationError
# Create your models here.
def validate_equal(self, email, confirm_email):
if email != confirm_email:
raise ValidationError(
('email does not match'),
params={'email': email, 'confirm_email': confirm_email}
)
class NewSubscriber(models.Model):
first_name = models.CharField(max_length=128)
last_name = models.CharField(max_length=128)
email = models.EmailField(max_length=254,unique=True)
confirm_email = models.EmailField(max_length=254, validators=[validate_equal('self', 'email', 'confirm_email')])
You can't do validation like that, especially when you want to compare fields. All you're doing here is passing the literal strings 'email' and 'confirm_email' (as well as 'self', for some reason) - and you're calling the validation function at define time.
Instead, use a clean method on the form itself.
class NewSubscriberForm(forms.ModelForm):
class Meta:
fields = '__all__'
def clean(self):
if self.cleaned_data['email'] != self.cleaned_data['confirm_email']:
raise forms.ValidationError('email does not match')
return self.cleaned_data

django admin form won't be modify

from django import forms
from .models import SignUp
class forml(forms.ModelForm):
class Meta:
model = SignUp
fields = ['Email', 'Name']
# exclude =['sam']
def clean_email(self):
email = self.cleaned_data.get('Email')
email_base, ext = email.split("#")
exname, domain = ext.split(".")
if not domain == "gov":
raise forms.ValidationError("plz write .gov")
return email
here i'm trying to force the user to sign up with .gov email but for a reason that i can't know it's doing the work !
Your problem is with the uppercase field names.
have you tried calling def clean_Email(self): ???
Also, consider having all your fields lowercase. In python, only class name should be Camelcase.
Hope it helps.

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', )

Django - custom validation for different users

I have a date time field called bk_time. Now, I would like to write custom validation for bk_time based on different users.
For example, I have users Staff_A, Staff_B and Superuser:
Staff_A can only set the time = Mon-Fri 9am-12am
Staff_B can only set the time = Monday only
Superuser no limitation
I have referred Django Doc Validators. But it seems not working for multiple validation
I have tried to write save_formsetDjango Doc Admin.But it seems not able to raise ValidationError
models.py
class Location(models.Model):
name = models.CharField('Location', max_length=100)
class Room(models.Model):
room_label = models.CharField('Room Lebel', max_length=100)
bk_time= models.DateTimeField('Booking Time')
admin.py
class RoomInline(admin.StackedInline):
model = Room
extra = 0
class LocationAdmin(admin.ModelAdmin):
list_display = ['id', 'name']
fields = ('name')
inlines = [RoomInline]
If this is relevant, I'm using Django 1.4.
I think this has to come on the form validation, and not on the field validation. This is because your validation depends on two independent fields.
In particular, this is very similar to an authentication: your validation depends on the user and on another field. Take a look how Django implements its authentication (from django.contrib.auth):
class AuthenticationForm(forms.Form):
[...]
def clean(self):
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
if username and password:
self.user_cache = authenticate(username=username,
password=password)
if self.user_cache is None:
raise forms.ValidationError(
self.error_messages['invalid_login'],
code='invalid_login',
params={'username': self.username_field.verbose_name},
)
elif not self.user_cache.is_active:
raise forms.ValidationError(
self.error_messages['inactive'],
code='inactive',
)
return self.cleaned_data
In your case, you want to raise a ValidationError on a given constraint, and return cleaned_data otherwise.

Custom User Model error

I'm trying to set up my custom user model in Django. The reason is that I want to use email as the username, and remove the username field entirely. I've run into a error, that I just can't figure out.
Manager isn't available; User has been swapped for 'app.MyUser'
Exception Location: .../django/db/models/manager.py in __get__, line 256
Python Version: 2.7.3
Python Path:
[...project specific files,
'/usr/lib/python2.7',
'/usr/lib/python2.7/plat-linux2',
'/usr/lib/python2.7/lib-tk',
'/usr/lib/python2.7/lib-old',
'/usr/lib/python2.7/lib-dynload',
'/usr/local/lib/python2.7/dist-packages',
'/usr/lib/python2.7/dist-packages',
'/usr/lib/python2.7/dist-packages/PIL',
'/usr/lib/python2.7/dist-packages/gtk-2.0',
'/usr/lib/pymodules/python2.7',
'/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode']
I've googled like crazy, but haven't found too many pages about this error message. I have found some pages, with suggestions on how to solve it, but none of the suggestions have worked for me.
My code: I've set the custom user model. I have declared the custom user model AUTH_USER_MODEL = 'app.MyUser' in settings.py. I have also set up a custom UserManager:
class MyUserManager(BaseUserManager):
def create_user(self, email, password=None):
"""
Creates and saves a User with the given email. Note that none of the optional fields gets values in the creation. These fields will have to be filled out later on.
"""
if not email:
raise ValueError('Users must have an email address')
user = self.model(email=MyUserManager.normalize_email(email))
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password=None):
"""
Creates and saves a superuser with the the above mentioned attributes
"""
user = self.create_user(email, password=password)
user.is_admin = True
user.save(using=self._db)
return user
class MyUser(AbstractBaseUser, PermissionsMixin):
"""
Custom made User model. No username, instead email is used as unique field and index
"""
Genders = (('M', 'Man'), ('K', 'Woman'))
FirstName = models.CharField(max_length=30)
LastName = models.CharField(max_length=40)
Gender = models.CharField(max_length=2, choices=Genders, default='K')
email = models.EmailField(verbose_name='email address', max_length=255, unique=True, db_index=True,)
twitter = models.CharField(max_length=30)
is_admin = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
def get_full_name(self):
# The user is identified by their email address
return self.email
def get_short_name(self):
# The user is identified by their email address
return self.email
def __unicode__(self):
return self.email
objects = MyUserManager()
I've tried to declare to different types of UserAdmins, none of which is making any difference,the first one I tried was;
class MyUserAdmin(UserAdmin):
# The forms to add and change user instances
#form = UserChangeForm
#add_form = FrontpageRegistrationForm
list_display = ('email', 'FirstName', 'LastName', 'Gender', 'twitter')
list_filter = ()
add_fieldsets = ((None, {'classes': ('wide',),'fields': ('email', 'password1', 'password2')}),)
search_fields = ('email',)
ordering = ('email',)
filter_horizontal = ()
admin.site.register(MyUser, MyUserAdmin)
I've commented out the two attributes add_form and form because they raised some form errors I wanted to get back to at a later point.
The second UserAdmin was made, after reading about a possible fix here. This didn't help the situation though;
class MyUserAdmin(admin.ModelAdmin):
# The forms to add and change user instances
#form = UserChangeForm
add_form = FrontpageRegistrationForm
add_fieldsets = ((None, {'classes': ('wide',),'fields': ('email', 'password1', 'password2')}),)
def get_fieldsets(self, request, obj=None):
if not obj:
return self.add_fieldsets
return super(MyUserAdmin, self).get_fieldsets(request, obj)
def get_form(self, request, obj=None, **kwargs):
defaults = {}
if obj is None:
defaults.update({'form': self.add_form,'fields': admin.util.flatten_fieldsets(self.add_fieldsets),})
defaults.update(kwargs)
return super(MyUserAdmin, self).get_form(request, obj, **defaults)
I've also tried deleting all tables in the db with no luck.
I would be eternally greatful to anyone who even looks at the problem. And if any one were to solve this, I would try my best to talk my wife into naming our firstborn after the Avatar that gave me a solution so that I could go on living my life.
EDIT:
I tried setting the AUTH_USER_MODELto mainfolder.app.MyUserI'm sure the "mainfolder" is on the pythonpath. init.py in the app should be correct. The new settings.py gave the following server error; auth.user: AUTH_USER_MODEL is not of the form 'app_label.app_name'.admin.logentry: 'user' has a relation with model smartflightsearch.SFSdb.MyUser, which has either not been installed or is abstract.registration.registrationprofile: 'user' has a relation with model, which has either not been installed or is abstract. A new clue I don't know how to interpret..
TL;DR: Use the code from the Solution part at the end of the following answer.
Longer explanation: You see, as of Django 1.5, it's not enough to subclass Django's UserAdmin to be able to interact with swappable user models: you need to override respective forms as well.
If you jump to django.contrib.auth.admin source, you'll see that the UserAdmin's form and add_form have these values:
# django/contrib/auth/admin.py
class UserAdmin(admin.ModelAdmin):
...
form = UserChangeForm
add_form = UserCreationForm
Which point us to forms in django.contrib.auth.forms that do not respect swappable user models:
# django/contrib/auth/forms.py
class UserCreationForm(forms.ModelForm):
...
class Meta:
model = User # non-swappable User model here.
class UserChangeForm(forms.ModelForm):
...
class Meta:
model = User # non-swappable User model here.
Solution: So, you should follow a great already existing answer (don't forget to vote it up!) which boils down to this:
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
class MyUserChangeForm(UserChangeForm):
class Meta:
model = get_user_model()
class MyUserCreationForm(UserCreationForm):
class Meta:
model = get_user_model()
class MyUserAdmin(UserAdmin):
form = MyUserChangeForm
add_form = MyUserCreationForm
admin.site.register(MyUser, MyUserAdmin)
Hopefully, this would be fixed in the future releases of Django (here's the corresponding ticket in the bug tracker).
When you said you set AUTH_USER_MODEL = 'app.MyUser' I'm assuming your app where is located the MyUser class, have a structure, perharps, like this:
inside the app/ dir: init.py and models.py and stuff..
so inside the models.py you have the MyUser and inside the init.py:
from models import MyUser