Search code examples
djangopython-3.xdjango-users

Python Django - Associating a ForeignKey on users. IntegrityError NOT NULL constraint failed


I'm making a website (obviously) with Django and I have a problem with when I create users. I want to associate my users with a model, called a Class, and the Class contains multiple users. And I assume I need to use models.OneToOneField() to associate one class with multiple users. But when I, for example, create a superuser in the terminal, I instantly get an IntegrityError when I have typed the last bit of required information and pressed enter. Basically, I just want a model called a Class to have multiple users associated with it, and every Class have different Keys/authentication (as shown in models.py) to do different things on my website.

Full traceback (after trying to create superuser):

Traceback (most recent call last):
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\db\backends\utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\db\backends\sqlite3\base.py", line 383, in execute
    return Database.Cursor.execute(self, query, params)
sqlite3.IntegrityError: NOT NULL constraint failed: users_user.parent_class_id

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

Traceback (most recent call last):
  File "manage.py", line 21, in <module>
    main()
  File "manage.py", line 17, in main
    execute_from_command_line(sys.argv)
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\core\management\__init__.py", line 381, in execute_from_command_line
    utility.execute()
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\core\management\__init__.py", line 375, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\core\management\base.py", line 323, in run_from_argv
    self.execute(*args, **cmd_options)
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\contrib\auth\management\commands\createsuperuser.py", line 61, in execute
    return super().execute(*args, **options)
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\core\management\base.py", line 364, in execute
    output = self.handle(*args, **options)
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\contrib\auth\management\commands\createsuperuser.py", line 156, in handle
    self.UserModel._default_manager.db_manager(database).create_superuser(**user_data)
  File "C:\Users\lll20\Documents\PYJA\pyja\users\models.py", line 37, in create_superuser
    return self._create_user(email, password, **extra_fields)
  File "C:\Users\lll20\Documents\PYJA\pyja\users\models.py", line 18, in _create_user
    user.save(using=self._db)
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\contrib\auth\base_user.py", line 66, in save
    super().save(*args, **kwargs)
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\db\models\base.py", line 741, in save
    force_update=force_update, update_fields=update_fields)
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\db\models\base.py", line 779, in save_base
    force_update, using, update_fields,
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\db\models\base.py", line 870, in _save_table
    result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\db\models\base.py", line 908, in _do_insert
    using=using, raw=raw)
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\db\models\manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\db\models\query.py", line 1186, in _insert
    return query.get_compiler(using=using).execute_sql(return_id)
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\db\models\sql\compiler.py", line 1335, in execute_sql
    cursor.execute(sql, params)
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\db\backends\utils.py", line 99, in execute
    return super().execute(sql, params)
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\db\backends\utils.py", line 67, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\db\backends\utils.py", line 76, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\db\backends\utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\db\utils.py", line 89, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\db\backends\utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "C:\Users\lll20\Documents\PYJA\pyja\p_env\lib\site-packages\django\db\backends\sqlite3\base.py", line 383, in execute
    return Database.Cursor.execute(self, query, params)
django.db.utils.IntegrityError: NOT NULL constraint failed: users_user.parent_class_id

The Code

project/users/models.py:

from django.contrib.auth.models import AbstractUser, BaseUserManager
from django.db import models
from django.utils.translation import ugettext_lazy as _


class UserManager(BaseUserManager):
    """Define a model manager for User model with no username field."""

    use_in_migrations = True

    def _create_user(self, email, password, **extra_fields):
        """Create and save a User with the given email and password."""
        if not email:
            raise ValueError('The given email must be set')
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_user(self, email, password=None, **extra_fields):
        """Create and save a regular User with the given email and password."""
        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(email, password, **extra_fields)

    def create_superuser(self, email, password, **extra_fields):
        """Create and save a SuperUser with the given email and password."""
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self._create_user(email, password, **extra_fields)


class Keys(models.Model):
    """A Model to represent any kind of auth key."""
    key = models.CharField(max_length=250, blank=False, default="")

    kind_of_authorisation = models.CharField(max_length=100, blank=False, default="")

    class Meta:
        verbose_name_plural = "Keys"


class Class(models.Model):
    """A model to represent all kinds of classes."""
    name = models.CharField(max_length=100, blank=False, default="", unique=True)
    class_id = models.CharField(max_length=200, blank=False, default="", unique=True)

    authorisations = models.ForeignKey(Keys, on_delete=models.CASCADE, blank=False)

    class Meta:
        verbose_name_plural = "Classes"


class User(AbstractUser):
    """User model."""
    username = models.CharField(max_length=75, unique=True)
    email = models.EmailField(_('email address'), unique=True)
    first_name = models.CharField(max_length=100, blank=False, default="")
    last_name = models.CharField(max_length=100, blank=False, default="")

    # It's this field that I don't know about.
    parent_class = models.ForeignKey(Class, on_delete=models.CASCADE,
                                     null=True)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['username', 'first_name', 'last_name']

    objects = UserManager()

project/users/admin.py:

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin
from django.utils.translation import ugettext_lazy as _

from .models import User, Class, Keys


@admin.register(User)
class UserAdmin(DjangoUserAdmin):
    """Define admin model for custom User model with no email field."""

    fieldsets = (
        (None, {'fields': ('email', 'password')}),
        (_('Personal info'), {'fields': ('first_name', 'last_name')}),
        (_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser',
                                       'groups', 'user_permissions')}),
        (_('Important dates'), {'fields': ('last_login', 'date_joined')}),
    )
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('email', 'password1', 'password2'),
        }),
    )
    list_display = ('email', 'first_name', 'last_name', 'is_staff')
    search_fields = ('email', 'first_name', 'last_name')
    ordering = ('email',)


admin.site.register(Class)
admin.site.register(Keys)

project/users/forms.py:

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

from .models import User, Class, Keys
from explore_database import functions as func


class CreateUserForm(UserCreationForm):
    # Require the email field.
    email = forms.EmailField(required=True)

    class Meta:
        model = User
        fields = ("email", "username", "password1",
                  "password2")

    def save(self, commit=True):
        user = super(UserCreationForm, self).save(commit=False)
        # Get all the relevant data, and save it.
        user.email = self.cleaned_data["email"]
        user.username = self.cleaned_data["username"]

        # Save the newly created user.
        if commit:
            user.save()
        return user


class EditProfileForm(UserChangeForm):
    password = None

    class Meta:
        model = User
        fields = ("email", "username")


class CreateClass(forms.ModelForm):

    class Meta:
        model = Class
        fields = ("name", "class_id", "authorisations")


class CreateKey(forms.ModelForm):

    class Meta:
        model = Keys
        fields = ("key", "kind_of_authorisation")

Any help is highly appreciated. Thanks!

UPDATE: Okay, I got to a point where I have a superuser. I set null=True and was able to create the superuser successfully. BUT, now I can't see where I should set the class in admin for my superuser. The only way, I could associate a class instance with the superuser was, to go into python manage.py shell and then manually add the class instance, I created in admin, to the superuser.


Solution

  • django.db.utils.IntegrityError: NOT NULL constraint failed: users_user.parent_class_id
    

    You have one or multiple user objects saved to your database already. By adding this OneToOneField, those existing records will have a value of Null, meaning they are not currently pointing at a parent_class object.

    You have two choices:

    One: Edit your Django model so that Null is an acceptable value.

    parent_class = models.OneToOneField(Class, on_delete=models.CASCADE, null=True)

    Two: Create a new migration where you provide Django a default value for this field (ex. None).