How do i create a custom django backend for django-registration?

django, django-forms, django-registration, python

Solution

You're trying to use a form as a backend, but that's not what a backend is at all. As the document you link to explains, a backend is a class that implements certain methods, including `registration_allowed`. The form doesn't implement any of those, which is not surprising, because it's meant for user input and validation, not the backend actions.

However, that page does give a hint as to the correct way to implement this. One of the methods that the backend can define is `get_form_class()`, which returns the form class to use. So, it seems that what you need is a custom backend that inherits from `registration.backends.default.DefaultBackend` and overrides just the `get_form_class` method, which simply returns `customRegistrationForm`.

Problem

I've had a read of this http://docs.b-list.org/django-registration/0.8/backend-api.html and i've had a shot at making my own backend. I am doing this because I want to create a backend that disallows having the same email used for registration, and I wanted to change the email-error message. I also wanted to add in my own field! Here is what I came up with: ``` from django import forms from registration.forms import RegistrationForm from django.utils.translation import ugettext_lazy as _ from django.contrib.auth.models import User from registration.forms import attrs_dict class customRegistrationForm(RegistrationForm): email2 = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict, maxlength=75)), label=_("Confirm email")) def clean_email(self): """ Validate that the email is alphanumeric and is not already in use. """ try: email = User.objects.get(email__iexact=self.cleaned_data['email']) except User.DoesNotExist: return self.cleaned_data['email'] raise forms.ValidationError(_("That email already exists - if you have forgotten your password, go to the login screen and then select \"forgot password\"")) def clean(self): """ Verifiy that the values entered into the two email fields match. Note that an error here will end up in ``non_field_errors()`` because it doesn't apply to a single field. """ if 'email' in self.cleaned_data and 'email2' in self.cleaned_data: if self.cleaned_data['email'] != self.cleaned_data['email2']: raise forms.ValidationError(_("The two email fields didn't match.")) return super(RegistrationForm,clean) ``` The above goes in my init.py file (whetever that is) Then, I have in my urls.py code: ``` url(r'^accounts/register/$', register, { 'backend': 'myapp.forms.customRegistrationForm' }, name='registration_register'), ... #other urls here! ``` Now, when i go to the /accounts/register page, I get the following error: AttributeError at /accounts/register/ 'customRegistrationForm' object has no attribute 'registration_allowed' Which is weird. It seems to be telling me I need a "registration_allowed" method added to my subclass. BUT, the subclass is a subclass of the RegistrationForm, which works fine and doesn't have that stuff defined... I know I can add these members in, but it seems to beat the purpose of extension, right? UPDATE Here is the code now that it works! I broke up the varying classes into varying init.py files in different folders - one called "forms" and one called "backends", both of which sit in a folder "djangoRegistration" under my main project. /forms/init.py ``` from django import forms from registration.forms import RegistrationForm from django.utils.translation import ugettext_lazy as _ from django.contrib.auth.models import User from registration.forms import attrs_dict class customRegistrationForm(RegistrationForm): def __init__(self, *args, **kw): super(RegistrationForm, self).__init__(*args, **kw) self.fields.keyOrder = [ 'username', 'email', 'email2', 'password1', 'password2' ] email2 = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict, maxlength=75)), label=_("Confirm email")) def clean_email(self): """ Validate that the email is alphanumeric and is not already in use. """ try: email = User.objects.get(email__iexact=self.cleaned_data['email']) except User.DoesNotExist: return self.cleaned_data['email'] raise forms.ValidationError(_("That email already exists - if you have forgotten your password, go to the login screen and then select \"forgot password\"")) def clean(self): """ Verifiy that the values entered into the two email fields match. Note that an error here will end up in ``non_field_errors()`` because it doesn't apply to a single field. """ if 'email' in self.cleaned_data and 'email2' in self.cleaned_data: if self.cleaned_data['email'] != self.cleaned_data['email2']: raise forms.ValidationError(_("The two email fields didn't match.")) return super(RegistrationForm,clean) ``` /backends/init.py ``` from registration.backends.default import DefaultBackend from dumpstownapp.djangoRegistration.forms import customRegistrationForm class customDefaultBackend(DefaultBackend): def get_form_class(self, request): """ Return the default form class used for user registration. """ return customRegistrationForm ``` and finally, my urls.py just references the new backend: ``` url(r'^accounts/register/$', register, { 'backend': 'myapp.djangoRegistration.backends.customDefaultBackend' }, name='registration_register'), #more urls here! yay! ``` As a final note, I had to add some code to "order" the way the fields were presented, which is what the init method in the customRegistrationForm is doing thanks!

Original source