bgarcial
bgarcial

Reputation: 3193

Customizing the Django User model - username as a Primary Key attribute

How to can I create my own user model in Django, in which I want that my P.K be the username field and not the id field autogenerated by Django?

I am trying the following:

I've create the userprofiles application in where I will create my customize django user model

INSTALLED_APPS = [
       ...
    'userprofiles.apps.UserprofilesConfig',
       ...
]

AUTH_USER_MODEL = 'userprofiles.User'

In my userprofiles/models.py file I have defined the following:

from __future__ import unicode_literals
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, UserManager, PermissionsMixin

# Create your models here.
class User(AbstractBaseUser, PermissionsMixin):

GENDER_MALE = 'M'
GENDER_FEMALE = 'F'
GENDER_OTHER = 'O'
GENDER_CHOICES = (
    (GENDER_MALE, u'Male'),
    (GENDER_FEMALE, u'Female'),
)

username = models.CharField(max_length=15, unique=True, db_index=True, primary_key=True)

first_name=models.CharField(max_length=50, blank=False,)
last_name=models.CharField(max_length=50, blank=False,)
photo = models.ImageField(upload_to='avatars', blank=True)
email = models.EmailField(max_length=254, unique=True)
is_staff = models.BooleanField(
    default=True,
    help_text='Designates whether the user can log into this admin site.')
is_active = models.BooleanField(default=True)
#date_joined = models.DateTimeField(default=None)
#date_joined = models.DateTimeField()

gender = models.CharField(
    max_length=1,
    choices=GENDER_CHOICES,
    blank=False, )

is_player = models.BooleanField(default=False)
is_coach = models.BooleanField(default=False)
is_viewer = models.BooleanField(default=False)

USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email',]
objects = UserManager()

class Meta:
    db_table = 'auth_user'

def get_full_name(self):
    """
    Returns the first_name plus the last_name, with a space in between.
    """
    full_name = '%s %s' % (self.first_name, self.last_name)
    return full_name.strip()

def get_short_name(self):
    "Returns the short name for the user."
    return self.first_name

I am using the AbstractBaseUser class due to:

AbstractBaseUser provides the core implementation of a User model, including hashed passwords and tokenized password resets. You must then provide some key implementation details: ...

and also I considered this explanation in which say that is necessary add manually the fields that I needed.

Then, when I perform the migrations the fields that I've defined in my User model (that inherit from AbstractBaseUser) are created in my database:

(fuupbol) ➜  fuupbol_project git:(master) ✗ python manage.py makemigrations
    Migrations for 'userprofiles':
    0013_auto_20160418_2252.py:
    - Add field groups to user
    - Add field user_permissions to user
    - Alter field is_superuser on user
    (fuupbol) ➜  fuupbol_project git:(master) ✗ python manage.py migrate
    Operations to perform:
    Apply all migrations: sessions, userprofiles, admin, contenttypes, auth
    Running migrations:
    Rendering model states... DONE
     Applying userprofiles.0013_auto_20160418_2252... OK
    (fuupbol) ➜  fuupbol_project git:(master) ✗

enter image description here

Then I've created a superuser via command line interface of this way:

(fuupbol) ➜  fuupbol_project git:(master) ✗ python manage.py createsuperuser
Username: cdiaz   
Email: [email protected]
Password: 
Password (again): 
Superuser created successfully.
(fuupbol) ➜  fuupbol_project git:(master) ✗ 

Like Django use special forms to create and edit forms in the admin relative to Users model, due to these was thinked only for the django original user model.

Here we are using a custom django user model with AbstractBaseUser, then we use the following class:

In my userprofiles/forms.py file

from django import forms
from django.contrib.auth.forms import (UserChangeForm,UserCreationForm)

from .models import User

class CustomUserChangeForm(UserChangeForm):
    class Meta(UserChangeForm.Meta):
        model = User

class CustomUserCreationForm(UserCreationForm):
    class Meta(UserCreationForm.Meta):
        model = User

In my userprofiles/admin.py file create an CustomUserAdmin class customized with the fields defined before in my custom User model

from django.contrib import admin                                                                                                                                                       
from django.contrib.auth.admin import UserAdmin                                                                                                                                        

from .models import User                                                                                                                                                               
from .forms import CustomUserChangeForm, CustomUserCreationForm                                                                                                                        

# Register your models here.                                                                                                                                                           

class CustomUserAdmin(UserAdmin):                                                                                                                                                      
    form = CustomUserChangeForm                                                                                                                                                        
    add_form = CustomUserCreationForm                                                                                                                                                  
    fieldsets = UserAdmin.fieldsets + (                                                                                                                                                
        (                                                                                                                                                                              
            None, {                                                                                                                                                                    
                'fields':(                                                                                                                                                             
                    'username',                                                   
                    'password',                                                                                                                                                        
                    'first_name',                                                                                                                                                      
                    'last_name',                                                                                                                                                       
                    'gender',                                                                                                                                                          
                    'email',                                                                                                                                                           
                    'photo',                                                                                                                                                           
                    'is_staff',                                                                                                                                                        
                    'is_active',                                                                                                                                                       
                    'is_superuser',                                                                                                                                                    
                    'is_player',                                                                                                                                                       
                    'is_coach',                                                                                                                                                        
                    'is_viewer',                                                                                                                                                       
                    'last_login',                                                                                                                                                                                                                                                                                                                           
                )                                                                                                                                                                      
            }                                                                                                                                                                          
        ),                                                                                                                                                                             
    )                                                                                                                                                                                  


#Change our UserAdmin class to inherit of our CustomUserAdmin created above (do not inherit of model.ModelAdmin)                                                                       
@admin.register(User)                                                                                                                                                                  
class UserAdmin(CustomUserAdmin):                                                                                                                                                                                            
    list_display = ('username','password','first_name','last_name','gender','email','photo','is_staff','is_active','is_superuser','is_player','is_coach','is_viewer','last_login',)  

Then when I go to the Django admin and I see the options to edit some user already created, I get this message:

enter image description here

In this moment I do not have the date_joined attribute in my User model, and if I add the date_joined attribute to my User model, and perform the migrations, I get this messages:

(fuupbol) ➜  fuupbol_project git:(master) ✗ python manage.py makemigrations userprofiles
Migrations for 'userprofiles':
  0022_user_date_joined.py:
    - Add field date_joined to user
(fuupbol) ➜  fuupbol_project git:(master) ✗ 

And when I execute the migrate command

(fuupbol) ➜  fuupbol_project git:(master) ✗ python manage.py migrate userprofiles       
    Operations to perform:
      Apply all migrations: userprofiles
    Running migrations:
      Rendering model states... DONE
      Applying userprofiles.0015_user_date_joined...Traceback (most recent call last):
      File "/home/bgarcial/.virtualenvs/fuupbol/lib/python3.4/site-packages/django/db/backends/utils.py", line 64, in execute
        return self.cursor.execute(sql, params)
    psycopg2.IntegrityError: column "date_joined" contains null values


    The above exception was the direct cause of the following exception:

    Traceback (most recent call last):
      File "manage.py", line 10, in <module>
        execute_from_command_line(sys.argv)
      File "/home/bgarcial/.virtualenvs/fuupbol/lib/python3.4/site-packages/django/core/management/__init__.py", line 353, in execute_from_command_line
        utility.execute()
      File "/home/bgarcial/.virtualenvs/fuupbol/lib/python3.4/site-packages/django/core/management/__init__.py", line 345, in execute
        self.fetch_command(subcommand).run_from_argv(self.argv)
      File "/home/bgarcial/.virtualenvs/fuupbol/lib/python3.4/site-packages/django/core/management/base.py", line 348, in run_from_argv
        self.execute(*args, **cmd_options)
      File "/home/bgarcial/.virtualenvs/fuupbol/lib/python3.4/site-packages/django/core/management/base.py", line 399, in execute
        output = self.handle(*args, **options)
      File "/home/bgarcial/.virtualenvs/fuupbol/lib/python3.4/site-packages/django/core/management/commands/migrate.py", line 200, in handle
        executor.migrate(targets, plan, fake=fake, fake_initial=fake_initial)
      File "/home/bgarcial/.virtualenvs/fuupbol/lib/python3.4/site-packages/django/db/migrations/executor.py", line 92, in migrate
        self._migrate_all_forwards(plan, full_plan, fake=fake, fake_initial=fake_initial)
      File "/home/bgarcial/.virtualenvs/fuupbol/lib/python3.4/site-packages/django/db/migrations/executor.py", line 121, in _migrate_all_forwards
        state = self.apply_migration(state, migration, fake=fake, fake_initial=fake_initial)
      File "/home/bgarcial/.virtualenvs/fuupbol/lib/python3.4/site-packages/django/db/migrations/executor.py", line 198, in apply_migration
        state = migration.apply(state, schema_editor)
      File "/home/bgarcial/.virtualenvs/fuupbol/lib/python3.4/site-packages/django/db/migrations/migration.py", line 123, in apply
        operation.database_forwards(self.app_label, schema_editor, old_state, project_state)
      File "/home/bgarcial/.virtualenvs/fuupbol/lib/python3.4/site-packages/django/db/migrations/operations/fields.py", line 62, in database_forwards
        field,
      File "/home/bgarcial/.virtualenvs/fuupbol/lib/python3.4/site-packages/django/db/backends/base/schema.py", line 396, in add_field
        self.execute(sql, params)
      File "/home/bgarcial/.virtualenvs/fuupbol/lib/python3.4/site-packages/django/db/backends/base/schema.py", line 110, in execute
        cursor.execute(sql, params)
      File "/home/bgarcial/.virtualenvs/fuupbol/lib/python3.4/site-packages/django/db/backends/utils.py", line 79, in execute
        return super(CursorDebugWrapper, self).execute(sql, params)
      File "/home/bgarcial/.virtualenvs/fuupbol/lib/python3.4/site-packages/django/db/backends/utils.py", line 64, in execute
        return self.cursor.execute(sql, params)
      File "/home/bgarcial/.virtualenvs/fuupbol/lib/python3.4/site-packages/django/db/utils.py", line 95, in __exit__
        six.reraise(dj_exc_type, dj_exc_value, traceback)
      File "/home/bgarcial/.virtualenvs/fuupbol/lib/python3.4/site-packages/django/utils/six.py", line 685, in reraise
        raise value.with_traceback(tb)
      File "/home/bgarcial/.virtualenvs/fuupbol/lib/python3.4/site-packages/django/db/backends/utils.py", line 64, in execute
        return self.cursor.execute(sql, params)
    django.db.utils.IntegrityError: column "date_joined" contains null values

    (fuupbol) ➜  fuupbol_project git:(master) ✗ 

I cannot understand the reason of the error message django.db.utils.IntegrityError: column "date_joined" contains null values due to in this moment I do not have this attribute date_joined

Any orientation would be appreciated. Thanks

Upvotes: 3

Views: 2371

Answers (1)

Jared Mackey
Jared Mackey

Reputation: 4158

I am assuming you don't have data for date_joined on any of the instances and one of the migrations requires it to not be be null. You can probably either go into the database and add it manual to get around it temporarily.

Upvotes: 1

Related Questions