IntegrityError at /admin/api/user/6/change/ FOREIGN KEY constraint failed - python

I am developing a website on django. When I am trying to delete a user via admin panel i get an error. I can change e.g. staff status (while still getting an error, but changes are getting apllied) The code is below:
models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
emailSpam = models.BooleanField(default=True)
email = models.EmailField('email', unique=True)
first_name = None
last_name = None
confirmedEmail = models.BooleanField(default=False)
REQUIRED_FIELDS = ["emailSpam"]
forms.py
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import User
class CustomUserCreationForm(UserCreationForm):
class Meta:
model = User
fields = ('email',)
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = User
fields = ('email',)
admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .forms import CustomUserCreationForm, CustomUserChangeForm
from .models import User
class Admin(UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = User
list_display = ('email', 'is_staff', 'is_active',)
list_filter = ('email', 'is_staff', 'is_active',)
fieldsets = (
(None, {'fields': ('email', 'password')}),
('Permissions', {'fields': ('is_staff', 'is_active')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'password1', 'password2', 'is_staff', 'is_active')}
),
)
search_fields = ('email',)
ordering = ('email',)
admin.site.register(User, Admin)

Possible solutions
There are three things that might be causing the issue, at least as far as I can tell. The first you already discounted. I hope it's the second solution, since that will be easier, but I fear it might be the third, which would be hardest to get around.
Cause One
As my comment stated, perhaps there is another model with a field that has User as a ForeignKey with on_delete = models.CASCADE. When you try to delete the User, all instances of this class that has that ForeignKey will need to be deleted as well (because of on_delete=models.CASCADE), and that's what's causing the issue. You have already stated you have no such models, so let's move on to solution 2.
Cause Two
I hope it's this one, since it might be easier to fix. I noticed you have email = models.EmailField('email', unique=True) as one of your fields for your User model, but AbstractUser should already have an email field. Try removing that field, makemigrations and migrate, and see if the issue is resolved.
Cause Three
Did you change from the default User model to the custom user you are now using in mid-project? In other words, did you, in this project, ever run makemigrations before you switched to a custom user model? That can be a big problem. However, there are two solutions for this, not so easy or desirable, but doable.
Solution A: If this is a new project with no valuable data yet, you can simpy delete your database, delete all migrations in all folders, as well as all __pycache__ folders as described here. Then re do python manage.py makemigrations and python manage.py migrate. Of course this will erase all your tables, so you wouldn't want to do this mid-project.
Solution B: There is a way to handle this mid-project without losing data. The steps are out in this django ticket 25313, or these, more readable instructions.

Related

Add fields from another model to the admin site

My Profile model has a OneToOne relation with Django's built-in User model.
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
verified = models.BooleanField(default=False)
If I want to change user's password or properties like Active or Superuser I have to do it in one Change User page, and to edit verified property I have to go to another.
Is there any way to merge this:
And this:
Into one form so I can edit everything about a user in one page?
Edit 1:
As you guys suggested the StackedInline approach, let's see how it turns out.
Please first look at Django's default Admin site (first screenshot above):
Everything is grouped in sections and sections have titles.
Look at how the password information is displayed.
There's a link to change the password.
Now I implement the StackedInline solution.
Please note that this is in the admin.py of my myapp:
from django.contrib import admin
from .models import Profile
from django.contrib.auth.models import User
# Register your models here.
class ProfileInline(admin.StackedInline):
model = Profile
class UserAdmin(admin.ModelAdmin):
inlines = (ProfileInline, )
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
Now let's look at Admin site:
Everything is scattered. Sections and their titles are gone (Personal info, Permissions, etc).
Password field shows the hashed password. All other information is gone.
There's no link to change the password.
Edit 2:
To solve the problem of Edit 1 I look at the source code of Django (https://github.com/django/django/blob/main/django/contrib/auth/admin.py) and add update my code as below:
class UserAdmin(admin.ModelAdmin):
inlines = (ProfileInline, )
fieldsets = (
(None, {"fields": ("username", "password")}),
(("Personal info"), {"fields": ("first_name", "last_name", "email")}),
(
("Permissions"),
{
"fields": (
"is_active",
"is_staff",
"is_superuser",
"groups",
"user_permissions",
),
},
),
(("Important dates"), {"fields": ("last_login", "date_joined")}),
)
add_fieldsets = (
(
None,
{
"classes": ("wide",),
"fields": ("username", "password1", "password2"),
},
),
)
filter_horizontal = (
"groups",
"user_permissions",
)
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
Now I have two sections in the Admin site:
The section on the top shows almost everything (except that the password field is still different and there's no link to change the password and also the verified field is not there) but sections and titles are back.
Then there's this additional and completely unnecessary part:
As you can see:
All fields of information about the user is repeated
Look at the password field
Information is not grouped in sections with titles
verified filed appears.
OP can use one of the InlineModelAdmin objects such as StackedInline. This allows one to create inline forms providing one the ability to edit models on the same page as a parent model.
Adapting for OP's case, it would be something like
from django.contrib import admin
class ProfileInline(admin.StackedInline):
model = Profile
class UserAdmin(admin.ModelAdmin):
inlines = [
ProfileInline,
]
admin.site.register(User, UserAdmin)
Now OP's admin site is set up to edit Profile objects inline from the User detail page.
User model Extension vs Inheritance.
Your profile model only add some elements to user. In this case Model inheritance can be better.
# models.py
class Profile(user):
verified = models.BooleanField(default=False)
after that you can achieve all fields for user and for profile:
# admin.py
class ProfileAdmin(ModelAdmin):
fields = '__all__'
if you don't want to switch on Model inheritance.
You can use InlineModel in UserAdmin to change related model.
# admin.py
class ProfileInline(StackedInline):
model=Profile
class UserAdmin(ModelAdmin):
inlines = (ProfileInline, )
you can use override AbstractUser from model Django so you can merge user in one place like this:
from django.contrib.auth.models import AbstractUser
from django.db import models
class CustomUser(AbstractUser):
verified = models.BooleanField(default=False)
and you need to add this line to Django settings.py file so that Django knows to use the new User class:
AUTH_USER_MODEL = 'users.CustomUser'
then make sure to migrate :
(env)$ python manage.py makemigrations
(env)$ python manage.py migrate

Import-export module works not with User model for me in Django

I am trying to import new users to original User model form the Django admin site. I used to add the users one-by-one manually to the model but now I need to import lots of users and I need a solution to bulk import. I tried the code below, but I have an error:
django.contrib.admin.sites.NotRegistered: The model User is not registered
Please help me to solve this.
admin.py
from django.contrib import admin
from django.contrib.auth.models import User
from import_export.admin import ImportExportModelAdmin
from import_export import resources
class UserResource(resources.ModelResource):
class Meta:
model = User
fields = ('id','username','first_name', 'last_name', 'email' 'password1', 'password2')
class UserAdmin(ImportExportModelAdmin):
list_display = ('id','username','first_name', 'last_name', 'email')
resource_class = UserResource
pass
admin.site.unregister(User)
admin.site.register(User, UserAdmin)

Django - Admin Interfaces - to allow create users for non-superuser

I have the situation of permits the access to admin interfaces to three types of users:
- Admin
- Supervisor
- Agent
It's a situation hierarchycal, the admin is one (the superuser) and it's creates the supervisors, and the supervisors create the agents.
All them can login to django admin with distincts authorizations.
The login has managed by 'django.contrib.auth' with the default model auth_user (.
from django.contrib.auth.models import User
class Supervisor(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
e_mail = models.EmailField(max_length=60, db_column='E-Mail',blank=True)
...other fields....
class Admin(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
e_mail = models.EmailField(max_length=60, db_column='E-Mail',blank=True)
...other fields...
But, there is a problem. If I allow the supervisor to create an agent implies that I must add the authorization to ADD and CHANGE the table USER. And this is dangerous, any supervisors could become a superuser, deleting users, etc etc....
How can I resolve this problem?? Is it possible to permit the supervisor to create an Agent without that he can be dangerous??
Thanks
EDIT
I have a doubt... in models.py during a definition of class I written this method:
def save(self):
self.user.is_staff = True
self.user.save()
super(Agent, self).save()
In Java, the method of EJB it was transactional/atomic (the commit is automatic)... In django, I have to call the method save().
It is a question different from the previous.....
Don't know if I got to understand your problem. But, if I did, you could edit the user creation form.
#admin.register(User)
class UserAdmin(BaseUserAdmin):
view_on_site = False
list_display = ('username', 'first_name', 'last_name', 'is_active', 'is_staff', 'date_joined', 'last_login')
list_filter = ('is_staff', )
fieldsets = (
('Data', {'fields': ('username', 'email', 'first_name', 'last_name', 'password')}),
('Activation/Deactivation', {'fields': ('is_active', )}),
('Permissions', {'fields': ('is_staff', 'groups', )})
)
As superuseris not an option anymore, your supervisors can't became superusers.
Let me know if this helps. Maybe we can get closer to the solution.

Custom User model fields (AbstractUser) not showing in django admin

I have extended User model for django, using AbstractUser method. The problem is, my custom fields do not show in django admin panel.
My models.py:
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
is_bot_flag = models.BooleanField(default=False)
My admin.py:
from django.contrib.auth.admin import UserAdmin
from .models import User
admin.site.register(User, UserAdmin)
Thanks
If all you want to do is add new fields to the standard edit form (not creation), there's a simpler solution than the one presented above.
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User
class CustomUserAdmin(UserAdmin):
fieldsets = (
*UserAdmin.fieldsets, # original form fieldsets, expanded
( # new fieldset added on to the bottom
'Custom Field Heading', # group heading of your choice; set to None for a blank space instead of a header
{
'fields': (
'is_bot_flag',
),
},
),
)
admin.site.register(User, CustomUserAdmin)
This takes the base fieldsets, expands them, and adds the new one to the bottom of the form. You can also use the new CustomUserAdmin class to alter other properties of the model admin, like list_display, list_filter, or filter_horizontal. The same expand-append method applies.
You have to override UserAdmin as well, if you want to see your custom fields. There is an example here in the documentation.
You have to create the form for creating (and also changing) user data and override UserAdmin. Form for creating user would be:
class UserCreationForm(forms.ModelForm):
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
class Meta:
model = User
fields = '__all__'
def clean_password2(self):
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):
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
You override UserAdmin with:
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
class UserAdmin(BaseUserAdmin):
add_form = UserCreationForm
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'first_name', 'last_name', 'is_bot_flag', 'password1', 'password2')}
),
)
and then you register:
admin.site.register(User, UserAdmin)
I pretty much copy/pasted this from documentation and deleted some code to make it shorter. Go to the documentation to see the full example, including example code for changing user data.
The quickest way to show your extra fields in the Django Admin panel for an AbstractUser model is to unpack the UserAdmin.fieldsets tuple to a list in your admin.py, then edit to insert your field/s in the relevant section and repack as a tuple (see below).
Add this code in admin.py of your Django app
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User
fields = list(UserAdmin.fieldsets)
fields[0] = (None, {'fields': ('username', 'password', 'is_bot_flag')})
UserAdmin.fieldsets = tuple(fields)
admin.site.register(User, UserAdmin)
Note:
list(UserAdmin.fieldsets) gives the following list:
[ (None, {'fields': ('username', 'password')}),
('Personal info', {'fields': ('first_name', 'last_name', 'email')}),
('Permissions', {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups',
'user_permissions')}),
('Important dates', {'fields': ('last_login', 'date_joined')})
]
These fields are by default in Django user models, and here we are modifying the first index of the list to add our custom fields.
Try this...
models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class CustomUser(AbstractUser):
phone_number = models.CharField(max_length=12)
settings.py : Add below line of code in settings.py
AUTH_USER_MODEL = 'users.CustomUser'
forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm
from .models import CustomUser
class CustomUserCreationForm(UserCreationForm):
class Meta:
model = CustomUser
fields = '__all__'
admin.py
from django.contrib import admin
from .models import CustomUser
from .forms import CustomUserCreationForm
from django.contrib.auth.admin import UserAdmin
# Register your models here.
class CustomUserAdmin(UserAdmin):
model = CustomUser
add_form = CustomUserCreationForm
fieldsets = (
*UserAdmin.fieldsets,
(
'Other Personal info',
{
'fields': (
'phone_number',
)
}
)
)
admin.site.register(CustomUser, CustomUserAdmin)
After all are done then run below command in terminal
python manage.py makemigrations
python manage.py migrate
python manage.py runserver

Django Admin Inlines With OneToOne Field

Here in my model, you can see that I have a SpecializedProfile with a OneToOne relationship with a UserProfile, with a OneToOne relationship with the django user model.
I want to create an admin for the SpecializedProfile containing inlines for the UserProfile and the django User model, so that I can create a SpecializedProfile all at once, without needing to go to the UserProfile page and the User page.
Here is my model:
class UserProfile(models.Model):
user_auth = models.OneToOneField(User, related_name="profile", primary_key=True)
# more fields...
class SpecializedProfile(models.Model):
profile = models.OneToOneField(UserProfile, related_name="specialized_profile", primary_key=True)
# More fields...
and here is my attempt at creating the admin:
class UserInline(admin.TabularInline):
model = User
fk_name = 'profile__specialized_profile'
class ProfileInline(admin.TabularInline):
model = UserProfile
fk_name = 'specialized_profile'
class SpecializedProfileAdmin(admin.ModelAdmin):
model = SpecializedProfile
inlines = [
UserInline, ProfileInline
]
admin.site.register(SpecializedProfile, SpecializedProfileAdmin)
The admin isn't working, and I am getting this error:
<class 'profiles.admin.ProfileInline'>: (admin.E202) 'profiles.UserProfile' has no field named 'trainer'.
<class 'profiles.admin.UserInline'>: (admin.E202) 'auth.User' has no ForeignKey to 'profiles.SpecializedProfile'.
It seems like django wants the inlines to be on the models where the OneToOne fields are defined, and won't accept reverse relationships. I'd rather not have to go restructuring my models to make this work... is there anything I can do to make the inlines work with my model as-is?
I fixed that error by making reverse side inline, not from Profile to User but from overridden User to Profile:
class ProfileInline(admin.StackedInline):
model = Profile
class IspUserAdmin(UserAdmin):
list_display = ('username', 'first_name', 'last_name', 'email', 'is_staff', 'is_superuser', 'is_active')
list_filter = ('date_joined', 'last_login', 'is_staff', 'is_superuser', 'is_active',)
inlines = (ProfileInline,)
admin.site.unregister(User)
admin.site.register(User, IspUserAdmin)
Then I also tweaked Profile admin (removed Add action and changed some field links to custom ones).
There's a django module on github that will do this for you, without you having to reverse the relationships: django_reverse_admin.
Once installed, your admin would look like:
# admin.py
from django_reverse_admin import ReverseModelAdmin
class SpecializedProfileAdmin(ReverseModelAdmin):
model = SpecializedProfile
inline_reverse = ['profile']
inline_type = 'tabular' # could also be 'stacked'
admin.site.register(SpecializedProfile, SpecializedProfileAdmin)
Unfortunately, I don't think it can do nested inlines (Django can't either), so that would only solve part of your problem. I know you didn't want to change your database structure, but SpecializedProfile really seems more like a subclass of UserProfile. If you rewrote your model like so:
class SpecializedProfile(UserProfile):
# More fields...
Then you could have the admin like this:
# admin.py
from django_reverse_admin import ReverseModelAdmin
class UserProfileAdmin(ReverseModelAdmin):
model = UserProfile
inline_reverse = ['user_auth']
inline_type = 'tabular'
class SpecializedProfileAdmin(ReverseModelAdmin):
model = SpecializedProfile
inline_reverse = ['user_auth']
inline_type = 'tabular'
admin.site.register(SpecializedProfile, SpecializedProfileAdmin)
admin.site.register(UserProfile, UserProfileAdmin)
This way, you can view everything inline for both UserProfile and SpecializedProfile.

Categories