La restricción FOREIGN KEY falló en el panel de administración de django al crear / editar / eliminar un usuario. (Usando un modelo de usuario personalizado).

Así que estoy usando un modelo de usuario personalizado

from django.db import models from django.contrib.auth.models import AbstractBaseUser, BaseUserManager class UserManager(BaseUserManager): def create_user(self, email, full_name, address, number, password=None): """ Creates and saves a User with the given email and password. """ if not email: raise ValueError('Users must have an email address') if not full_name: raise ValueError('Users must have an email address') if not address: raise ValueError('Users must have an email address') if not number: raise ValueError('Users must have an email address') if not password: raise ValueError('Users must have an email address') user = self.model( email=self.normalize_email(email.lower()), full_name=full_name, address = address, number=number, ) user.set_password(password) user.save(using=self._db) return user def create_staffuser(self, email, full_name, address, number, password): """ Creates and saves a staff user with the given email and password. """ user = self.create_user( email, full_name, address, numbe, password = password, ) user.staff = True user.save(using=self._db) return user def create_superuser(self, email, full_name, address, number, password): """ Creates and saves a superuser with the given email and password. """ user = self.create_user( email, full_name, address, number, password = password, ) user.staff = True user.admin = True user.save(using=self._db) return user class User(AbstractBaseUser): email = models.EmailField(max_length=255, unique=True) full_name = models.CharField(max_length=255, blank = False, null = False) address = models.CharField(max_length=255, blank = False, null = False) number = models.CharField(max_length=255, blank = False, null = False) active = models.BooleanField(default=True) staff = models.BooleanField(default=False) # a admin user; non super-user admin = models.BooleanField(default=False) # a superuser # notice the absence of a "Password field", that's built in. USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['full_name', 'address', 'number'] # Email & Password are required by default. objects = UserManager() def get_full_name(self): # The user is identified by their email address return self.email def get_short_name(self): # The user is identified by their email address return self.email def __str__(self): # __unicode__ on Python 2 return self.email def has_perm(self, perm, obj=None): "Does the user have a specific permission?" # Simplest possible answer: Yes, always return True def has_module_perms(self, app_label): "Does the user have permissions to view the app `app_label`?" # Simplest possible answer: Yes, always return True @property def is_staff(self): "Is the user a member of staff?" return self.staff @property def is_admin(self): "Is the user a admin member?" return self.admin @property def is_active(self): "Is the user active?" return self.active 

Este es mi admin.py para la aplicación

  from django.contrib import admin from django.contrib.auth.models import Group from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from .forms import UserAdminChangeForm, UserAdminCreationForm from .models import User class UserAdmin(BaseUserAdmin): # The forms to add and change user instances form = UserAdminChangeForm add_form = UserAdminCreationForm # The fields to be used in displaying the User model. # These override the definitions on the base UserAdmin # that reference specific fields on auth.User. list_display = ('email', 'admin') list_filter = ('admin',) fieldsets = ( (None, {'fields': ('email', 'password')}), ('Personal info', {'fields': ('full_name', 'address', 'number')}), ('Permissions', {'fields': ('admin', 'active', 'staff')}), ) # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin # overrides get_fieldsets to use this attribute when creating a user. add_fieldsets = ( (None, { 'classes': ('wide',), 'fields': ('email', 'full_name', 'address', 'number', 'password1', 'password2')} ), ) search_fields = ('email',) ordering = ('email',) filter_horizontal = () admin.site.register(User, UserAdmin) admin.site.unregister(Group) 

Y finalmente forms.py

  from django import forms from django.contrib.auth.forms import ReadOnlyPasswordHashField from .models import User class UserAdminCreationForm(forms.ModelForm): """A form for creating new users. Includes all the required fields, plus a repeated password.""" password1 = forms.CharField(label='Password', widget=forms.PasswordInput) password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput) class Meta: model = User fields = ('email', 'full_name', 'address', 'number') def clean_password2(self): # Check that the two password entries match password1 = self.cleaned_data.get("password1") password2 = self.cleaned_data.get("password2") if password1 and password2 and password1 != password2: raise forms.ValidationError("Passwords don't match") return password2 def save(self, commit=True): # Save the provided password in hashed format user = super(UserAdminCreationForm, self).save(commit=False) user.set_password(self.cleaned_data["password1"]) if commit: user.save() return user class UserAdminChangeForm(forms.ModelForm): """A form for updating users. Includes all the fields on the user, but replaces the password field with admin's password hash display field. """ password = ReadOnlyPasswordHashField() class Meta: model = User fields = ('email', 'full_name', 'address', 'number', 'password', 'active', 'admin') def clean_password(self): # Regardless of what the user provides, return the initial value. # This is done here, rather than on the field, because the # field does not have access to the initial value return self.initial["password"] 

Así que funciona muy bien cuando creo un superusuario a través de la consola con manage.py, pero cuando decido editar, eliminar o crear otro usuario en el panel de administración de la interfaz gráfica de usuario, aparece el mensaje “Error de restricción de FOREIGN KEY”. Lo cual no entiendo, ¿podría alguien apuntarme en la dirección correcta?

Creo que he encontrado una solución para esto. El problema podría deberse a problemas de dependencias circulares cuando migra su AUTH_USER_MODEL predeterminado a un modelo personalizado en la mitad del proyecto.

De la documentación de Django.

Cambiar AUTH_USER_MODEL después de haber creado las tablas de la base de datos es mucho más difícil ya que afecta a las claves externas y las relaciones de muchos a muchos, por ejemplo.

Este cambio no se puede hacer automáticamente y requiere la corrección manual de su esquema, el traslado de sus datos de la tabla de usuarios anterior y, posiblemente, la aplicación manual de algunas migraciones. Vea # 25313 para un resumen de los pasos.

Debido a las limitaciones de la función de dependencia dinámica de Django para modelos intercambiables, el modelo al que hace referencia AUTH_USER_MODEL debe crearse en la primera migración de su aplicación (generalmente llamada 0001_initial); De lo contrario, tendrás problemas de dependencia.

Además, puede encontrarse con un CircularDependencyError al ejecutar sus migraciones, ya que Django no podrá romper automáticamente el bucle de dependencia debido a la dependencia dinámica. Si ve este error, debe romper el ciclo moviendo los modelos de los que depende su modelo de usuario a una segunda migración. (Puedes intentar hacer dos modelos normales que tengan una ForeignKey entre sí y ver cómo makemigrations resuelve esa dependencia circular si quieres ver cómo se hace normalmente).

La mejor manera de abordar esto es eliminar la tabla y eliminar todos los archivos de migración y luego volver a ejecutar las migraciones con su modelo personalizado recién creado. Espero que esto funcione.

Puede encontrar más detalles sobre cómo migrar de un modelo incorporado a un nuevo modelo aquí https://code.djangoproject.com/ticket/25313

Pregunté por ahí y el código debería funcionar en versiones anteriores de django. Lamentablemente no funcionará en Django 2.0 o superior. Si alguien quiere una alternativa, creo que esto es perfecto para mi proyecto, la explicación también es simple.