Django Admin error 500 when trying to delete default Django Users

54 Views Asked by At

I am currently using Django Admin as a management site for my PostgreSQL DB, every action in the Admin works properly except the delete action on the default Django User (With this I mean I directly used django.auth.user as an entity for my DB). Everything is deployed as microservices in Kubernetes just in case you need this information.

The error it's basically an Error 500 that I can't figure it out, also is worth mentioning that this is a Production DB, though it has so little data that it wouldn't suppose big deal to reinitialise it if really needed it.

Error 500 (Server Error)

I can confirm I get the delete confirmation prompted correctly where we can visualize what other related entities will be also delete with the CASCADE option on the 'models.py'.

Delete confirmation prompted correctly

Maybe it could be a bad migration on the DB since the start of the project where I tried several packages & libraries that I am not using anymore, though I don't think it's related with that since I am receiving a Server Error after all.

I will share below the related Code:

requirements.txt

asgiref==3.5.2
Django==3.2
python-decouple==3.7
django-cors-headers==3.13.0
djangorestframework==3.14.0
djangorestframework-simplejwt==4.8.0
djoser==2.1.0
psycopg2==2.9.4
psycopg2-binary==2.9.4
pytz==2022.4
sqlparse==0.4.3
influxdb-client==1.33.0
paho-mqtt==1.6.1
whitenoise==6.4.0

models.py

from enum import unique
from django.db import models
from django.contrib.auth.models import User
from django.core.validators import MaxValueValidator, MinValueValidator

class Gestora(models.Model):
    name = models.CharField(max_length=30, null=None)
    cif = models.CharField(max_length=9, null=None)
    adress = models.CharField(max_length=50, null=None)
    contact = models.EmailField(max_length=45, unique=True)

    def __str__(self):
        return self.name

    class Meta:
        managed = False
        db_table = 'demo01_gestora'


class Usuario(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    gestora = models.ForeignKey(Gestora, related_name='usuarios', on_delete=models.CASCADE)

    def __str__(self):
        return self.user.username

    class Meta:
        managed = False
        db_table = 'demo01_usuario'


class Casa(models.Model):
    name = models.CharField(max_length=50, null=None)
    adress = models.CharField(max_length=50, null=None)
    latitude = models.DecimalField(max_digits=9, decimal_places=6, validators=[
                                  MinValueValidator(-90), MaxValueValidator(90)],null=True)
    longitude = models.DecimalField(max_digits=9, decimal_places=6, validators=[
                                   MinValueValidator(-180), MaxValueValidator(180)],null=True)
    owner = models.CharField(max_length=50, null=None)
    gestora = models.ForeignKey(Gestora, related_name='casas', on_delete=models.CASCADE)

    def __str__(self):
        return self.name + ', ' + self.adress

    class Meta:
        managed = False
        db_table = 'demo01_casa'


class Device(models.Model):
    id = models.AutoField(primary_key=True, unique=True)
    deviceId = models.CharField(max_length=12, null=None, blank=False)
    roomName = models.CharField(max_length=30, null=None, blank=False, default='-')
    online = models.BooleanField(null=True, blank=True)
    versions = models.CharField(max_length=30, blank=True)
    casa = models.ForeignKey(Casa, related_name='devices', null=True, blank=True, on_delete=models.CASCADE)

    def __str__(self):
        if (self.versions == ''):
            return (str(self.deviceId))
        else:
            return (str(self.deviceId) + ', ' + self.versions)

    class Meta:
        managed = False
        db_table = 'demo01_device'

admin.py

from django.contrib import admin
from .models import Gestora, Usuario, Casa, Device
from django.contrib.admin.models import LogEntry
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import ugettext as _
from django.utils.html import format_html
from django.urls import reverse


### NAMING OVERRIDE FOR ADMIN TOOL ###

admin.site.site_header = 'Nelium Eco Control Administration'
admin.site.index_title = 'Entity Features'


### USER CREATE RESTRICTION DEPENDING ON WHAT CURRENT USER ROLE IS ###

class UserRestrictedFormAdmin(UserAdmin):
    class Meta:
        model = User

    ### CUSTOM FIELDSET FOR ENTITY 'USER' ###
    def get_fieldsets(self, request, obj=None):
        if not obj:
            return self.add_fieldsets

        if request.user.is_superuser:
            perm_fields = ('is_active', 'is_staff', 'is_superuser',
                           'groups', 'user_permissions')
        elif request.user.is_staff and not request.user.is_superuser:
            perm_fields = ('is_active', 'is_staff', 'groups')

        return [(None, {'fields': ('username', 'password')}),
                (_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
                (_('Permissions'), {'fields': perm_fields}),
                (_('Important dates'), {'fields': ('last_login', 'date_joined')})]
    
admin.site.unregister(User)
admin.site.register(User, UserRestrictedFormAdmin)
    

### USUARIO CRUD RESTRICTION DEPENDING ON WHAT 'GESTORA' THE USER BELONGS TO ###

class CustomUsuarioAdmin(admin.ModelAdmin):
    class Meta:
        model = Usuario

    ### LIST, SEARCH & FILTERS FOR ENTITY 'USUARIO' ###
    search_fields = ['user__username', 'gestora__name']


    ### CUSTOM CHANGELIST_VIEW METHOD FOR STAFF USERS ###
    def changelist_view(self, request, extra_context=None):
        if request.user.is_superuser:
            self.list_display = ('get_user_username', 'get_user_firstlastname', 'get_gestora_name',)
        elif request.user.is_staff and not request.user.is_superuser:
            self.list_display = ('get_user_username', 'get_gestora_name',)
        return super(CustomUsuarioAdmin, self).changelist_view(request, extra_context)


    ### DEFINITION OF FOREIGN KEY GET METHODS ###
    @admin.display(ordering='gestora__name', description='Gestora')
    def get_gestora_name(self, obj):
        related_obj = obj.gestora
        url = reverse('admin:entityManagement_gestora_change', args=[related_obj.id])
        return format_html('<a href="{}">{}</a>', url, related_obj.name)
    
    @admin.display(ordering='user__first_name', description='Nombre Completo')
    def get_user_firstlastname(self, obj):
        related_obj = obj.user
        url = reverse('admin:auth_user_change', args=[related_obj.id])
        return format_html('<a href="{}">{}</a>', url, related_obj.first_name + ' ' + related_obj.last_name)
    
    @admin.display(ordering='user__username', description='Usuario')
    def get_user_username(self, obj):
        related_obj = obj.user
        url = reverse('admin:entityManagement_usuario_change', args=[obj.id])
        return format_html('<a href="{}">{}</a>', url, related_obj.username)


    ### CUSTOM QUERYSET FOR ENTITY 'GESTORA' ###
    def get_queryset(self, request):
        if request.user.is_superuser:
            return super().get_queryset(request)
        currentUsuario = Usuario.objects.get(user=request.user)
        return super().get_queryset(request).filter(gestora_id=currentUsuario.gestora_id)
    
admin.site.register(Usuario, CustomUsuarioAdmin)


### GESTORA CRUD RESTRICTION DEPENDING ON WHAT 'GESTORA' THE USER BELONGS TO ###

class CustomGestoraAdmin(admin.ModelAdmin):
    class Meta:
        model = Gestora

    ### LIST, SEARCH & FILTERS FOR ENTITY 'GESTORA' ###
    list_display = ('name', 'cif', 'adress',)
    search_fields = ['name', 'cif', 'adress', 'contact']


    ### CUSTOM QUERYSET FOR ENTITY 'GESTORA' ###
    def get_queryset(self, request):
        if request.user.is_superuser:
            return super().get_queryset(request)
        currentUsuario = Usuario.objects.get(user=request.user)
        return super().get_queryset(request).filter(id=currentUsuario.gestora_id)

admin.site.register(Gestora, CustomGestoraAdmin)


### CASA CRUD RESTRICTION DEPENDING ON WHAT 'GESTORA' THE USER BELONGS TO ###

class CustomCasaAdmin(admin.ModelAdmin):
    class Meta:
        model = Casa
    
    ### LIST, SEARCH & FILTERS FOR ENTITY 'CASA' ###
    list_display = ('name', 'adress', 'owner', 'get_gestora_name',)
    search_fields = ['name', 'adress', 'owner', 'gestora__name']


    ### DEFINITION OF FOREIGN KEY GET METHODS ###
    @admin.display(ordering='gestora__name', description='Gestora')
    def get_gestora_name(self, obj):
        related_obj = obj.gestora
        url = reverse('admin:entityManagement_gestora_change', args=[related_obj.id])
        return format_html('<a href="{}">{}</a>', url, related_obj.name)
    

    ### CUSTOM QUERYSET FOR ENTITY 'CASA' ###
    def get_queryset(self, request):
        if request.user.is_superuser:
            return super().get_queryset(request)
        currentUsuario = Usuario.objects.get(user=request.user)
        return super().get_queryset(request).filter(gestora_id=currentUsuario.gestora_id)
    

    ### CUSTOM FILTERED FOREIGN KEY FIELD FOR ENTITY 'CASA' ###
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if request.user.is_staff and not request.user.is_superuser:
            if db_field.name == 'gestora':
                currentUsuario = Usuario.objects.get(user=request.user)
                kwargs['queryset'] = Gestora.objects.filter(id=currentUsuario.gestora_id)
        return super().formfield_for_foreignkey(db_field, request, **kwargs)

admin.site.register(Casa, CustomCasaAdmin)


### DEVICE CRUD RESTRICTION DEPENDING ON WHAT 'GESTORA' THE USER BELONGS TO ###

class CustomDeviceAdmin(admin.ModelAdmin):
    class Meta:
        model = Device

    ### LIST, SEARCH & FILTERS FOR ENTITY 'DEVICE' ###
    list_display = ('deviceId', 'roomName', 'get_casa_name',)
    list_filter = ('online', 'versions',)
    search_fields = ['deviceId', 'casa__name']


    ### DEFINITION OF FOREIGN KEY GET METHODS ###
    @admin.display(ordering='casa__name', description='Casa')
    def get_casa_name(self, obj):
        related_obj = obj.casa
        url = reverse('admin:entityManagement_casa_change', args=[related_obj.id])
        return format_html('<a href="{}">{}</a>', url, related_obj.name)


    ### CUSTOM QUERYSET FOR ENTITY 'DEVICE' ###
    def get_queryset(self, request):
        if request.user.is_superuser:
            return super().get_queryset(request)
        currentUsuario = Usuario.objects.get(user=request.user)
        listIdsCasas = Casa.objects.filter(gestora_id=currentUsuario.gestora_id).values_list('id', flat=True)
        print(listIdsCasas)
        listDevicesPerGestora = Device.objects.filter(casa_id__in=listIdsCasas)
        print(listDevicesPerGestora)
        return listDevicesPerGestora
    

    ### CUSTOM FILTERED FOREIGN KEY FIELD FOR ENTITY 'DEVICE' ###
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        print('self', self)
        print('db_field', db_field)
        print('build_absolute_uri() => ', request.build_absolute_uri())
        print('kwargs => ', kwargs)

        if request.user.is_staff and not request.user.is_superuser:
            print('authorization if')
            if db_field.name == 'casa':
                print('db_field if')
                print('kwargs => ', kwargs)

                currentUsuario = Usuario.objects.get(user=request.user)
                kwargs['queryset'] = Casa.objects.filter(gestora_id=currentUsuario.gestora_id)

        return super().formfield_for_foreignkey(db_field, request, **kwargs)

admin.site.register(Device, CustomDeviceAdmin)

Is worth mentioning that I did not include any custom operation for the deafult Django User on the 'admin.py' file since everything should work properly with base Django entities.

In case you need to see anything else of the code please request it on the comments.

I tried to gather more information about the error but I wasn't able to achieve it since nor the browser nor the container log on Kubernetes shows anything else apart from the 'Error 500'.

I also tried to get more info by using POST request through Postman but I couldn't make it since the delete action has a confirmation prompt that I don't know how to surpass on Postman.

What I am trying to achieve is a completely functional management site where delete operation for default Django user works properly and avoiding manually deleting Users on the DB container with PSQL commands.

If you have read this far, thank you in advance for at least trying to give me a hand, hope someone with more knowledge of Django can bring some light for this issue I am having. Once again thanks ^^

0

There are 0 best solutions below