Search code examples
djangodjango-formsdjango-users

How to add fields to Django User model and show it on registration form with django-registration?


I noticed that the User model that Django provides does not have a Date of Birth Field. I do not want to write my own custom User class just to include this attribute. I am wondering if it is possible in some way to "attach" date of birth to the User model. If not what are the other simple options to make this work. I am also ok to save the contents of User Model to a profile model with additional date of birth information.

But would my form has to save both User and profile model which I am confused as how this work.

EDIT 2: (After implementing frnhr's solution):

Two issues: No activation email is sent out in the development server. I tried default User model with django-registration and I see activation email being sent out. but now, with the custom user model, no activation email is sent out.

when I login as admin, http://127.0.0.1:8000/admin/, I see no User model but only registration profiles and Groups. when I click registration profiles, it does not show all the information about User, but just username and activation key which is shown as already expired. Attached is a screenshot. how to fix this? enter image description here

enter image description here


Solution

  • In newer versions of Django, making your own custom User model is quite painless. See this answer for details: https://stackoverflow.com/a/16125609/236195

    You might run into problems if you are using some older third-party apps which have model relationships hardcoded to auth.models.User. Most of third-party apps which have ongoing development have probably switched already or will do so soon.


    Edit - getting it to work with django-registration

    myapp/models.py:

    from django.contrib.auth.models import AbstractUser
    from django.db import models
    from django.utils.translation import ugettext_lazy as _
    
    class CustomUser(AbstractUser):
        age = models.PositiveIntegerField(_("age"), null=True)  # this field is added to default User model
    

    settings.py:

    INSTALLED_APPS = (
        # ...
    
        'registration_defaults',
        'registration',
    
        'myapp',
    )
    
    # ...
    
    AUTH_USER_MODEL = "myapp.CustomUser"
    ACCOUNT_ACTIVATION_DAYS = 2  # or something
    

    urls.py (add this line):

       url(r'^accounts/', include('registration.backends.default.urls')),
    

    myapp/admin.py (optionally):

    from django.contrib import admin
    from django.contrib.auth.admin import UserAdmin
    from django.utils.translation import ugettext_lazy as _
    
    from .models import CustomUser
    
    
    class CustomUserAdmin(UserAdmin):
        fieldsets = (
            (None, {'fields': ('username', 'password')}),
            (_('Personal info'), {'fields': ('first_name', 'last_name', 'email', 'age')}),  # added "age" field
            (_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser',
                                           'groups', 'user_permissions')}),
            (_('Important dates'), {'fields': ('last_login', 'date_joined')}),
        )
    
    
    admin.site.register(CustomUser, CustomUserAdmin)
    

    django-registration

    Get it from here: https://bitbucket.org/fhrzenjak/django-registration and put it in your project folder.

    Disclaimer: this is my fork of the original django-registration repo, with applied patch for custom user models by mrginglymus: https://bitbucket.org/ubernostrum/django-registration/pull-request/30/django-15-compatibility-with-custom-user/diff

    Django-registration-defaults is a collection of default templates from here: https://github.com/yourcelf/django-registration-defaults It's pip-installable

    EDIT 2 - accept a custom field on registration

    Well, this is a separate question, but ok here is how...

    Create a MyRegistrationFormform:

    from registration.forms import RegistrationForm
    
    class MyRegistrationForm(RegistrationForm):
        age = forms.CharField(required=False)
    

    In your urls.py add a line before django-registration include. That will override whatever is in the include:

    url(r'^accounts/register/$',
            RegistrationView.as_view(form_class=MyRegistrationForm),
            name='registration_register',
        ),
    url(r'^accounts/', include('registration.backends.default.urls')),
    

    Modify your custom user model to respond on user_registered signal and save the additional data:

    class CustomUser(AbstractUser):
        age = models.PositiveIntegerField(_("age"), null=True)
    
        @classmethod
        def user_created(cls, sender, user, request, **kwargs):
            from myapp.forms import MyRegistrationForm
            form = MyRegistrationForm(request.POST)
            user.age = form.data.get('age', None)
            user.save()
            pass
    
    from registration.signals import user_registered
    user_registered.connect(CustomUser.user_created)