Enforcing lowercase usernames in Django - django

Currently, in django.contrib.auth, there can be two users with the username 'john' and 'John'. How can I prevent this from happening.
The most straightforward approach is add a clean method in contib.auth.models and convert it to a lowercase before saving but i dont want to edit the contrib.auth package.
Thanks.

Listen on pre_save for the Users model and then do your checks there. Least intrusive and most portable way.
Here is an example on how this would look like (adapted from the user profile example):
def username_check(sender, instance, **kwargs):
if User.objects.filter(username=instance.username.lower()).count():
raise ValidationError('Duplicate username')
pre_save.connect(username_check, sender=User)

I have used clean method in the user creation form by extending the class UserCreationForm in my custom CustomerRegistrationForm.
The code for the same is as follows:
class CustomerRegistrationForm(UserCreationForm):
# All your form code comes here like creating custom field, etc..
......
......
......
......
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
......
......
......
......
# This is the main centre of interest
# We create the clean_username field specific cleaner method in order to get and set the required field data
# In this case, the username field
def clean_username(self):
username = self.cleaned_data.get('username') # get the username data
lowercase_username = username.lower() # get the lowercase version of it
return lowercase_username
This approach is best as we'll not have to write extra messy code for pre_save event in database, and also, it comes with all django validator functionalities like duplicate username check, etc.
Every other thing will be automatically handled by django itself.
Do comment if there needs to be any modification in this code. T

There is a better option if you are using Postgres. Postgres has a case-insensitive field type, citext. As of 1.11, Django has this available in django.contrib.postgres.fields.citext. You may also need to deal with case-sensitivity in the url regular expressions.

I'd probably solve this on the model using a custom field for username.
from django.db import models
class LowercaseCharField(models.CharField):
"""
Override CharField to convert to lowercase before saving.
"""
def to_python(self, value):
"""
Convert text to lowercase.
"""
value = super(LowercaseCharField, self).to_python(value)
# Value can be None so check that it's a string before lowercasing.
if isinstance(value, str):
return value.lower()
return value
And then in your model..
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.validators import UnicodeUsernameValidator
from django.utils.translation import gettext_lazy as _
# Assuming you saved the above in the same directory in a file called model_fields.py
from .model_fields import LowercaseCharField
class User(AbstractUser):
username = LowercaseCharField(
# Copying this from AbstractUser code
_('username'),
max_length=150,
unique=True,
help_text=_('Required. 150 characters or fewer. Letters, digits and #/./+/-/_ only.'),
validators=[UnicodeUsernameValidator(),],
error_messages={
'unique': _("A user with that username already exists."),
},
)
# other stuff...
Usernames will all get saved as lowercase "automatically".

Updating the accepted answer to be more current:
from django.db.models.signals import pre_save
from django.db.utils import IntegrityError
from django.dispatch import receiver
#receiver(pre_save, sender=User)
def username_check(instance, sender, **kwargs):
"""Ensure that username unique constraint is case insensitive"""
if sender.objects.filter(username__iexact=instance.username.lower()):
raise IntegrityError("Duplicate username")

Related

How can I use `email` in "django-rest-framework-simplejwt" instead of `username` to generate token?

In django-rest-framework-simplejwt plugin username and password are used by default. But I wanted to use email instead of username. So, I did like below:
In serializer:
class MyTokenObtainSerializer(Serializer):
username_field = User.EMAIL_FIELD
def __init__(self, *args, **kwargs):
super(MyTokenObtainSerializer, self).__init__(*args, **kwargs)
self.fields[self.username_field] = CharField()
self.fields['password'] = PasswordField()
def validate(self, attrs):
# self.user = authenticate(**{
# self.username_field: attrs[self.username_field],
# 'password': attrs['password'],
# })
self.user = User.objects.filter(email=attrs[self.username_field]).first()
print(self.user)
if not self.user:
raise ValidationError('The user is not valid.')
if self.user:
if not self.user.check_password(attrs['password']):
raise ValidationError('Incorrect credentials.')
print(self.user)
# Prior to Django 1.10, inactive users could be authenticated with the
# default `ModelBackend`. As of Django 1.10, the `ModelBackend`
# prevents inactive users from authenticating. App designers can still
# allow inactive users to authenticate by opting for the new
# `AllowAllUsersModelBackend`. However, we explicitly prevent inactive
# users from authenticating to enforce a reasonable policy and provide
# sensible backwards compatibility with older Django versions.
if self.user is None or not self.user.is_active:
raise ValidationError('No active account found with the given credentials')
return {}
#classmethod
def get_token(cls, user):
raise NotImplemented(
'Must implement `get_token` method for `MyTokenObtainSerializer` subclasses')
class MyTokenObtainPairSerializer(MyTokenObtainSerializer):
#classmethod
def get_token(cls, user):
return RefreshToken.for_user(user)
def validate(self, attrs):
data = super(MyTokenObtainPairSerializer, self).validate(attrs)
refresh = self.get_token(self.user)
data['refresh'] = text_type(refresh)
data['access'] = text_type(refresh.access_token)
return data
In view:
class MyTokenObtainPairView(TokenObtainPairView):
"""
Takes a set of user credentials and returns an access and refresh JSON web
token pair to prove the authentication of those credentials.
"""
serializer_class = MyTokenObtainPairSerializer
And it works!!
Now my question is, how can I do it more efficiently? Can anyone give suggestion on this? Thanks in advance.
This answer is for future readers and therefore contains extra information.
In order to simplify the authentication backend, you have multiple classes to hook into. I would suggest to do option 1 (and optionally option 3, a simplified version of yours) below. Couple of notes before you read on:
Note 1: django does not enforce email as required or being unique on user creation (you can override this, but it's off-topic)! Option 3 (your implementation) might therefore give you issues with duplicate emails.
Note 1b: use User.objects.filter(email__iexact=...) to match the emails in a case insensitive way.
Note 1c: use get_user_model() in case you replace the default user model in future, this really is a life-saver for beginners!
Note 2: avoid printing the user to console. You might be printing sensitive data.
As for the 3 options:
Adjust django authentication backend with f.e. class EmailModelBackend(ModelBackend) and replace authenticate function.
Does not adjust token claims
Not dependent on JWT class/middleware (SimpleJWT, JWT or others)
Also adjusts other authentication types (Session/Cookie/non-API auth, etc.)
The required input parameter is still username, example below. Adjust if you dont like it, but do so with care. (Might break your imports/plugins and is not required!)
Replace django authenticate(username=, password=, **kwarg) from django.contrib.auth
Does not adjust token claims
You need to replace token backend as well, since it should use a different authentication, as you did above.
Does not adjust other apps using authenticate(...), only replaces JWT auth (if you set it up as such)
parameters is not required and therefore this option is less adviced).
Implement MyTokenObtainPairSerializer with email as claim.
Now email is sent back as JWT data (and not id).
Together with option 1, your app authentication has become username agnostic.
Option 1 (note that this also allows username!!):
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q
class EmailorUsernameModelBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
UserModel = get_user_model()
try:
user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username))
except UserModel.DoesNotExist:
return None
else:
if user.check_password(password):
return user
return None
Option 2:
Skipped, left to reader and not adviced.
Option 3:
You seem to have this covered above.
Note: you dont have to define MyTokenObtainPairView, you can use TokenObtainPairView(serializer_class=MyTokenObtainPairSerializer).as_view() in your urls.py. Small simplification which overrides the used token serializer.
Note 2: You can specify the identifying claim and the added data in your settings.py (or settings file) to use email as well. This will make your frontend app use the email for the claim as well (instead of default user.id)
SIMPLE_JWT = {
'USER_ID_FIELD': 'id', # model property to attempt claims for
'USER_ID_CLAIM': 'user_id', # actual keyword in token data
}
However, heed the uniqueness warnings given by the creators:
For example, specifying a "username" or "email" field would be a poor choice since an account's username or email might change depending on how account management in a given service is designed.
If you can guarantee uniqueness, you are all set.
Why did you copy and paste so much instead of subclassing? I got it to work with:
# serializers.py
from rest_framework_simplejwt.serializers import TokenObtainSerializer
class EmailTokenObtainSerializer(TokenObtainSerializer):
username_field = User.EMAIL_FIELD
class CustomTokenObtainPairSerializer(EmailTokenObtainSerializer):
#classmethod
def get_token(cls, user):
return RefreshToken.for_user(user)
def validate(self, attrs):
data = super().validate(attrs)
refresh = self.get_token(self.user)
data["refresh"] = str(refresh)
data["access"] = str(refresh.access_token)
return data
And
# views.py
from rest_framework_simplejwt.views import TokenObtainPairView
class EmailTokenObtainPairView(TokenObtainPairView):
serializer_class = CustomTokenObtainPairSerializer
And of course
#urls.py
from rest_framework_simplejwt.views import TokenRefreshView
from .views import EmailTokenObtainPairView
url("token/", EmailTokenObtainPairView.as_view(), name="token_obtain_pair"),
url("refresh/", TokenRefreshView.as_view(), name="token_refresh"),
The question has been a while but, I add +1 for #Mic's answer. By the way, wasn't it sufficient to update to TokenObtainPairSerializer only as following?:
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework_simplejwt.serializers import (
TokenObtainPairSerializer, User
)
class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
username_field = User.EMAIL_FIELD
class EmailTokenObtainPairView(TokenObtainPairView):
serializer_class = CustomTokenObtainPairSerializer
Let summarize the above solutions:
1- Create two app by Django command. One for the new token and the other for the user:
python manage.py startapp m_token # modified token
python manage.py startapp m_user # modified user
2- In the m_token, create the serializers.py and override the serializer to replace username with email field:
# serializers.py
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer, User
class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
username_field = User.EMAIL_FIELD
3- In the m_token, override the view to replace the serializer with the new one:
# views.py
from rest_framework_simplejwt.views import TokenObtainPairView
from .serializer import CustomTokenObtainPairSerializer
class EmailTokenObtainPairView(TokenObtainPairView):
serializer_class = CustomTokenObtainPairSerializer
4- In the m_token, create the urls.py and give the paths as follows:
# urls.py
from django.urls import path
from .views import TokenObtainPairView
from rest_framework_simplejwt.views import TokenRefreshView, TokenVerifyView
urlpatterns = [
path(r'token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path(r'token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
path(r'token/verify/', TokenVerifyView.as_view(), name='token_verify'),
]
5- In the m_user, override the user model as follows:
# models.py
from django.contrib.auth.models import AbstractUser
class MUser(AbstractUser):
USERNAME_FIELD = 'email'
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = ['username']
6- In the django project root, add AUTH_USER_MODEL = 'm_user.MUser' to setting.py.
I tested it in my project and it worked perfectly. I hope I did not miss anything. This way the swagger also shows "email" instead of "username" in the token parameters:
And in addition to #Mic's answer, remember to set USERNAME_FIELD = 'email' and may be REQUIRED_FIELDS = ['username'] in the User model.
For those using a custom User model, you simply can add those lines:
class User(AbstractUser):
...
email = models.EmailField(verbose_name='email address', max_length=255, unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
Then, in urls.py:
from rest_framework_simplejwt.views import TokenObtainPairView
urlpatterns = [
...
path('api/login/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
Using this code you can allow users to login using either username or email in the username field. You can add some lines to validate the email.
class TokenPairSerializer(TokenObtainPairSerializer):
def validate(self, attrs):
raw_username = attrs["username"]
users = User.objects.filter(email=raw_username)
if(users):
attrs['username'] = users.first().username
# else:
# raise serializers.ValidationError("Only email is allowed!")
data = super(TokenPairSerializer, self).validate(attrs)
return data

Django Rest Framework - how to create custom error messages for all ModelSerializer fields?

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', )
I'm aware that Django Rest Framework has it's own field validators, because when I try to create a user using a username which already exists, it raises an error saying:
{'username': [u'This field must be unique.']}
I want to customize the error message and make it say "This username is already taken. Please try again" rather than saying "This field must be unique".
It also has a built-in regex validator, because when I create a username with an exclamation mark, it says:
{'username': [u'Enter a valid username. This value may contain only letters, numbers and #/./+/-/_ characters.']}
I want to customize the regex validator so that it just says "Invalid username".
How do I customize all of the error messages which each field has?
Note: according to this post: Custom error messages in Django Rest Framework serializer I can do:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
def __init__(self, *args, **kwargs):
super(UserSerializer, self).__init__(*args, **kwargs)
self.fields['username'].error_messages['required'] = u'My custom required msg'
But what do I do for the 'unique' and 'regex' validators? I tried doing
self.fields['username'].error_messages['regex'] = u'My custom required msg'
and
self.fields['username'].error_messages['validators'] = u'My custom required msg'
but neither worked.
In order to replace unique or regex error messages you should change message member of corresponding validator object. This could be done using separate mixin class:
from django.core.validators import RegexValidator
from rest_framework.validators import UniqueValidator
from django.utils.translation import ugettext_lazy as _
class SetCustomErrorMessagesMixin:
"""
Replaces built-in validator messages with messages, defined in Meta class.
This mixin should be inherited before the actual Serializer class in order to call __init__ method.
Example of Meta class:
>>> class Meta:
>>> model = User
>>> fields = ('url', 'username', 'email', 'groups')
>>> custom_error_messages_for_validators = {
>>> 'username': {
>>> UniqueValidator: _('This username is already taken. Please, try again'),
>>> RegexValidator: _('Invalid username')
>>> }
>>> }
"""
def __init__(self, *args, **kwargs):
# noinspection PyArgumentList
super(SetCustomErrorMessagesMixin, self).__init__(*args, **kwargs)
self.replace_validators_messages()
def replace_validators_messages(self):
for field_name, validators_lookup in self.custom_error_messages_for_validators.items():
# noinspection PyUnresolvedReferences
for validator in self.fields[field_name].validators:
if type(validator) in validators_lookup:
validator.message = validators_lookup[type(validator)]
#property
def custom_error_messages_for_validators(self):
meta = getattr(self, 'Meta', None)
return getattr(meta, 'custom_error_messages_for_validators', {})
Then you could just inherit this mixin and update Meta class:
class UserSerializer(SetCustomErrorMessagesMixin, serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'email', 'groups')
custom_error_messages_for_validators = {
'username': {
UniqueValidator: _('This username is already taken. Please, try again'),
RegexValidator: _('Invalid username')
}
}
You also can do that just customizing your language translation, look here:
https://docs.djangoproject.com/en/3.2/topics/i18n/translation/#message-files
All you have to do is to create message files, change translation in there and compile your message into .mo files.
This method is not so much unacceptable as i might seems. Look here:
https://www.django-rest-framework.org/topics/internationalization/
DRF docs say that its common practice customizing error messages (your own and default ones) via translation.
My tips for those who`d want to try path above:
Make default django.po file. Most of default errors will be there.
If you are using DRF, then open this file https://raw.githubusercontent.com/encode/django-rest-framework/master/rest_framework/locale/en_US/LC_MESSAGES/django.po , chose error that you want to customize and copy only that error into your django.po flle above.
Customize it.
Compile it.
(!) NOTE Make sure that your translation file named exacly as django.po, not mylovelydjango.po, not myproject.po but EXACTLY django.po, or nothing will work.

Django-registration: How to make account creation ask for first/last name

I'm using the Django-Registration package to have users create accounts, authenticate them and log in to my webapp.
However, the form/view for account creation doesn't ask the user for a firstname/last name (those fields are part of the model BTW). It only asks for their email address, login ID and password (twice). I would like it to ask for the user's first name/last name (these fields can be optional... but it should still ask). I cannot find the form and view files to modify such that it asks for this info. I have modified the template file. But without the form and view modifications that's useless. How is this done?
In your forms.py, extend the DjangoRegistration form like:
class MyExtendedForm(RegistrationForm):
first_name = forms.CharField(widget=forms.TextInput(label="first_name"))
last_name = forms.CharField(widget=forms.TextInput(label="last_name"))
In the urls.py, tell django-registration to use this extended form:
# using my registration form to override the default
(r'^register/$',
register,
{'backend': 'registration.backends.default.DefaultBackend',
'form_class': MyExtendedForm}),
Define user_created to save the extra information:
def user_created(sender, user, request, **kwargs):
"""
Called via signals when user registers. Creates different profiles and
associations
"""
form = MyExtendedForm(request.Post)
# Update first and last name for user
user.first_name=form.data['first_name']
user.last_name=form.data['last_name']
user.save()
Then, register for signals from django-registration to call your function after any registration is processed:
from registration.signals import user_registered
user_registered.connect(user_created)
Once you have everything set up like shown here use following CustomUserForm:
class CustomUserForm(RegistrationForm):
class Meta(RegistrationForm.Meta):
model = CustomUser
fields = ['first_name','last_name','username','email','password1','password2']
To add the fields first_name and last_name from the default Django User model provide your own formclass:
Prepend the two fields to Meta.fields of the default RegistrationForm:
from django_registration.forms import RegistrationForm
class RegistrationWithNameForm(RegistrationForm):
class Meta(RegistrationForm.Meta):
fields = ["first_name", "last_name"] + RegistrationForm.Meta.fields
Override the default RegistrationView by adding a path to urls.py:
from django_registration.backends.activation.views import RegistrationView
from yourapp.forms import RegistrationWithNameForm
path('accounts/register/',
RegistrationView.as_view(form_class=RegistrationWithNameForm),
name='django_registration_register',
),
path("accounts/", include("django_registration.backends.activation.urls")),
Testcase:
from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse
class RegistrationTestCase(TestCase):
registration_url = reverse("django_registration_register")
test_username = "testuser"
post_data = {
"first_name": "TestFirstName",
"last_name": "TestLastName",
"username": test_username,
"email": "testuser#example.com",
"password1": "mypass",
"password2": "mypass"
}
def test_register(self):
response = self.client.post(self.registration_url, self.post_data)
self.assertRedirects(response, reverse("django_registration_complete"))
user = get_user_model().objects.get(username=self.test_username)
# Assert all fields are present on the newly registered user
for field in ["username", "first_name", "last_name", "email"]:
self.assertEqual(self.post_data[field], getattr(user, field))

Django - how to add email to required things

I create registration form:
#urls.py
from django.conf.urls import patterns, url
from django.views.generic import TemplateView
from account.views import Register
urlpatterns = patterns('',
url(r'^register/$', Register.as_view(template_name='account/register.html')),
)
#views.py
from django.views.generic import CreateView
from django.contrib.auth.models import User
class Register(CreateView):
model = User
success_url = '/account/'
And i have question: how I can add that email be require (now I must only enter username, password and 2 times time).
#edit
And how "repair" password? When i create user (in this form) and then go to admin panel, in user i see "Invalid password format or unknown hashing algorithm.". How i can repair this?
The reason that email is not required is because you're using a ModelForm, which takes a lot of cues from the underlying User model. Specifically, the required=True attribute is not present on the email field of the model.
One solution is to create your own form with the necessary attributes, perhaps by using a ModelForm and adding a required email field.
Another solution, and probably the better one, is to use something like django-registration as mentioned by Aamir Adnan in the comments to your question. It'll simplify things a lot for you.
As far as your repair password goes, you can't set the password to a raw string value as you're doing with your CreateView. To set a password, you have to call user.set_password(raw_string) which will take care of hashing and salting for you. Look how the built in UserCreationForm works, and try to mimic it if you decide to build the form yourself, rather than using a library (you shouldn't).
To solve these two problems you can use form like:
class UserCreationForm(forms.ModelForm):
class Meta:
model = User
def __init__(self, *args, **kwargs):
super(UserCreationForm).__init__(*args, **kwargs)
self.fields['email'].required = True
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password"])
if commit:
user.save()
return user
class Register(CreateView):
model = User
form_class = UserCreationForm
success_url = '/account/'
But there'll be other problems like duplication of username.

error overiding save method while extending Django-registration app

I am using django-registration app. and have following code in forms.py
from django.contrib.auth.forms import UserCreationForm
from registration.forms import RegistrationFormUniqueEmail
from django import forms
from django.contrib.auth.models import User
from accounts.models import UserProfile
from pprint import pprint
class UserRegistrationForm(RegistrationFormUniqueEmail):
#email = forms.EmailField(label = "Email")
fullname = forms.CharField(label = "Full name")
class Meta:
model = User
fields = ("fullname", "email", )
def __init__(self, *args, **kwargs):
super(UserRegistrationForm, self).__init__(*args, **kwargs)
del self.fields['username']
def save(self, commit=True):
user = super(UserRegistrationForm, self).save(commit=False)
user.userprofile.full_name = self.cleaned_data["fullname"]
user.email = self.cleaned_data["email"]
if commit:
user.save()
return user
I inherited from Django-registraion app's class RegistrationFormUniqueEmail so I when called save method at user = super(UserRegistrationForm, self).save(commit=False) it says, that save attribute doesnot exist. I actually wrote this code with inheritance from UserCreationForm .
I have just read the comment for SuperClass of RegistrationFormUniqueEmail that is :
"""
Form for registering a new user account.
Validates that the requested username is not already in use, and
requires the password to be entered twice to catch typos.
Subclasses should feel free to add any additional validation they
need, but should avoid defining a ``save()`` method -- the actual
saving of collected user data is delegated to the active
registration backend.
"""
These comments ask to not define another save method but I need to. So is there way that I can do define save method and call parent save method too to define additional fields? Following is the code of django-registration apps's forms.py:
"""
Forms and validation code for user registration.
"""
from django.contrib.auth.models import User
from django import forms
from django.utils.translation import ugettext_lazy as _
# I put this on all required fields, because it's easier to pick up
# on them with CSS or JavaScript if they have a class of "required"
# in the HTML. Your mileage may vary. If/when Django ticket #3515
# lands in trunk, this will no longer be necessary.
attrs_dict = {'class': 'required'}
class RegistrationForm(forms.Form):
"""
Form for registering a new user account.
Validates that the requested username is not already in use, and
requires the password to be entered twice to catch typos.
Subclasses should feel free to add any additional validation they
need, but should avoid defining a ``save()`` method -- the actual
saving of collected user data is delegated to the active
registration backend.
"""
username = forms.RegexField(regex=r'^[\w.#+-]+$',
max_length=30,
widget=forms.TextInput(attrs=attrs_dict),
label=_("Username"),
error_messages={'invalid': _("This value may contain only letters, numbers and #/./+/-/_ characters.")})
email = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict,
maxlength=75)),
label=_("E-mail"))
password1 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False),
label=_("Password"))
password2 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False),
label=_("Password (again)"))
def clean_username(self):
"""
Validate that the username is alphanumeric and is not already
in use.
"""
existing = User.objects.filter(username__iexact=self.cleaned_data['username'])
if existing.exists():
raise forms.ValidationError(_("A user with that username already exists."))
else:
return self.cleaned_data['username']
def clean(self):
"""
Verifiy that the values entered into the two password fields
match. Note that an error here will end up in
``non_field_errors()`` because it doesn't apply to a single
field.
"""
if 'password1' in self.cleaned_data and 'password2' in self.cleaned_data:
if self.cleaned_data['password1'] != self.cleaned_data['password2']:
raise forms.ValidationError(_("The two password fields didn't match."))
return self.cleaned_data
class RegistrationFormTermsOfService(RegistrationForm):
"""
Subclass of ``RegistrationForm`` which adds a required checkbox
for agreeing to a site's Terms of Service.
"""
tos = forms.BooleanField(widget=forms.CheckboxInput(attrs=attrs_dict),
label=_(u'I have read and agree to the Terms of Service'),
error_messages={'required': _("You must agree to the terms to register")})
class RegistrationFormUniqueEmail(RegistrationForm):
"""
Subclass of ``RegistrationForm`` which enforces uniqueness of
email addresses.
"""
def clean_email(self):
"""
Validate that the supplied email address is unique for the
site.
"""
if User.objects.filter(email__iexact=self.cleaned_data['email']):
raise forms.ValidationError(_("This email address is already in use. Please supply a different email address."))
return self.cleaned_data['email']
class RegistrationFormNoFreeEmail(RegistrationForm):
"""
Subclass of ``RegistrationForm`` which disallows registration with
email addresses from popular free webmail services; moderately
useful for preventing automated spam registrations.
To change the list of banned domains, subclass this form and
override the attribute ``bad_domains``.
"""
bad_domains = ['aim.com', 'aol.com', 'email.com', 'gmail.com',
'googlemail.com', 'hotmail.com', 'hushmail.com',
'msn.com', 'mail.ru', 'mailinator.com', 'live.com',
'yahoo.com']
def clean_email(self):
"""
Check the supplied email address against a list of known free
webmail domains.
"""
email_domain = self.cleaned_data['email'].split('#')[1]
if email_domain in self.bad_domains:
raise forms.ValidationError(_("Registration using free email addresses is prohibited. Please supply a different email address."))
return self.cleaned_data['email']
I just want to know that how can I override save() method or else how can I create new additional fields?
I have found solution of my own posted problem:
I have removed that django-registration app's RegistrationFormUniqueEmail, and instead I am inheriting from UserCreationForm and added the required method into my own UserRegistrationForm so I am able to override save method and able to do the things that I wanted to .