Django-Registration & Django-Profile, using your own custom form
Asked Answered
W

3

31

I am making use of django-registration and django-profile to handle registration and profiles. I would like to create a profile for the user at the time of registration. I have created a custom registration form, and added that to the urls.py using the tutorial on:

http://dewful.com/?p=70

The basic idea in the tutorial is to override the default registration form to create the profile at the same time.

forms.py - In my profiles app

from django import forms
from registration.forms import RegistrationForm
from django.utils.translation import ugettext_lazy as _
from profiles.models import UserProfile
from registration.models import RegistrationProfile

attrs_dict = { 'class': 'required' }

class UserRegistrationForm(RegistrationForm):
    city = forms.CharField(widget=forms.TextInput(attrs=attrs_dict))

    def save(self, profile_callback=None):
        new_user = RegistrationProfile.objects.create_inactive_user(username=self.cleaned_data['username'],
        password=self.cleaned_data['password1'],
        email=self.cleaned_data['email'])
        new_profile = UserProfile(user=new_user, city=self.cleaned_data['city'])
        new_profile.save()
        return new_user

In urls.py

from profiles.forms import UserRegistrationForm

and

url(r'^register/$',
                           register,
                           {'backend': 'registration.backends.default.DefaultBackend', 'form_class' : UserRegistrationForm},
                           name='registration_register'),

The form is displayed, and i can enter in City, however it does not save or create the entry in the DB.

Waxbill answered 8/4, 2010 at 16:5 Comment(1)
Solution with signals - here I wrote how to use signals to save additional dataBogor
Y
29

You're halfway there - you've successfully built a custom form that replaces the default form. But you're attempting to do your custom processing with a save() method on your model form. That was possible in older versions of django-registration, but I can see from the fact that you specified a backend in your URL conf that you're using v0.8.

The upgrade guide says:

Previously, the form used to collect data during registration was expected to implement a save() method which would create the new user account. This is no longer the case; creating the account is handled by the backend, and so any custom logic should be moved into a custom backend, or by connecting listeners to the signals sent during the registration process.

In other words, the save() method on the form is being ignored now that you're on version 0.8. You need to do your custom processing either with a custom backend or with a signal. I chose to create a custom back-end (if anyone has gotten this working with signals, please post code - I wasn't able to get it working that way). You should be able to modify this to save to your custom profile.

  1. Create a regbackend.py in your app.
  2. Copy the register() method from DefaultBackend into it.
  3. At the end of the method, do a query to get the corresponding User instance.
  4. Save the additional form fields into that instance.
  5. Modify the URL conf so that it points to BOTH the custom form AND the custom back-end

So the URL conf is:

url(r'^accounts/register/$',
    register,
    {'backend': 'accounts.regbackend.RegBackend','form_class':MM_RegistrationForm},        
    name='registration_register'
    ),

regbackend.py has the necessary imports and is basically a copy of DefaultBackend with just the register() method, and the addition of:

    u = User.objects.get(username=new_user.username)
    u.first_name = kwargs['first_name']
    u.last_name = kwargs['last_name']
    u.save() 
Yeta answered 22/4, 2010 at 0:3 Comment(4)
thanks for the comment. Decided to do a redirect to create profile upon registration, that way you dont have a profile for an unactivated user. Will mark this question as answered however.Waxbill
I have the same problem with the new version of djando registration. Shacker, but where exactly is : "Copy the register() method from DefaultBackend into it." DefaultBackend??, where is? ThanksGirondist
Asinox - You need to get it from the django-registration source code. Look in registration/backends/default/__init__.pyYeta
This is great. I did it, but still my user won't get the first_name and last_name from the form.Evoke
S
12

As described in my comment on Django Trac ticket I made a metaclass and mixin to allow multiple inheritance for ModelForm Django forms. With this you can simply make a form which allows registration with fields from user and profile models at the same time without hard-coding fields or repeating yourself. By using my metaclass and mixin (and also fieldset mixin) you can do:

class UserRegistrationForm(metaforms.FieldsetFormMixin, metaforms.ParentsIncludedModelFormMixin, UserCreationForm, UserProfileChangeForm):
    error_css_class = 'error'
    required_css_class = 'required'
    fieldset = UserCreationForm.fieldset + [(
    utils_text.capfirst(UserProfileChangeForm.Meta.model._meta.verbose_name), {
      'fields': UserProfileChangeForm.base_fields.keys(),
    })]

    def save(self, commit=True):
        # We disable save method as registration backend module should take care of user and user
        # profile objects creation and we do not use this form for changing data
        assert False
        return None

    __metaclass__ = metaforms.ParentsIncludedModelFormMetaclass

Where UserCreationForm can be for example django.contrib.auth.forms.UserCreationForm form and UserProfileChangeForm a simple ModelForm for your profile model. (Do not forget to set editable to False in your foreign key to User model.)

With django-registration backend having such register method:

def register(self, request, **kwargs):
    user = super(ProfileBackend, self).register(request, **kwargs)
    profile, created = utils.get_profile_model().objects.get_or_create(user=user)

    # lambda-object to the rescue
    form = lambda: None
    form.cleaned_data = kwargs

    # First name, last name and e-mail address are stored in user object
    forms_models.construct_instance(form, user)
    user.save()

    # Other fields are stored in user profile object
    forms_models.construct_instance(form, profile)
    profile.save()

    return user

Be careful that registration signal is send at the beginning of this method (in method in superclass) and not at the end.

In the same manner you can make a change form for both user and profile information. Example for this you can find in my comment on Django Trac ticket mentioned above.

Szczecin answered 21/7, 2010 at 11:1 Comment(2)
I've recently implemented the same thing with signals, but this is just amazing! I definitely need to learn how to use metaclass and mixin techniques!Webbed
You can see here an example of it in use.Szczecin
T
1

With registration 0.8 and later:

Create a subclass of registration.backends.default.views.RegistrationView in your views.py or equivalent:

from registration.backends.default.views import RegistrationView

class MyRegistrationView(RegistrationView):

    form_class= MyCustomRegistrationForm

    def register(self, request, **cleaned_data):
        new_user= super(MyRegistrationView, self).register(request, **cleaned_data)
        # here create your new UserProfile object
        return new_user
Tafoya answered 21/5, 2014 at 11:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.