organizations.OrganizationGroups has no ForeignKey to 'auth.User'

Issue

The following exception has been appearing randomly in my Django admin installation. OrganizationGroups does not have a ForeignKey to auth.User. This exception is also appearing in different apps of the system, and it is always organizations.OrganizationGroups has no ForeignKey to 'app.Model'

I’m currently running:

  • Django Version: 3.2.13
  • Python Version: 3.9.2

OrganizationGroups model:

class OrganizationGroups(models.Model):
    id = models.BigAutoField(primary_key=True)
    organization = models.ForeignKey(
        Organization, models.DO_NOTHING, null=True, blank=True
    )
    group = models.ForeignKey(Group, models.CASCADE, blank=True, null=True)

    class Meta:
        db_table = 'organization_groups'
        unique_together = (('organization', 'group'),)

    def __str__(self) -> str:
        return self.organization.name

UserAdmin ModelAdmin:

from django.contrib.admin import ModelAdmin, register
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User
from django.http import HttpRequest

from apps.organizations.models import OrganizationGroups
from apps.users.forms import UserForm
from apps.users.models import *

@register(User)
class UserAdmin(BaseUserAdmin):
    # FRONTEND SETTINGS

    actions = None
    fieldsets = (('Users', {'fields': ('username',)}),)
    form = UserForm
    list_display = ('username', 'is_active')
    list_filter = ('is_active',)
    list_per_page = 10
    ordering = ('username',)
    readonly_fields = ('username',)

    # OVERRIDEN FUNCTIONS

    def get_fieldsets(self, request: HttpRequest, obj=None):
        if not obj:
            return self.add_fieldsets

        if request.user.is_superuser:
            perm_fields = (
                'is_active',
                'is_superuser',
                'groups',
                'user_permissions'
            )
        else:
            perm_fields = (
                'is_active',
                'groups',
                'user_permissions'
            )

        return [
            (None, {'fields': ('username',)}),
            (('Permisos'), {'fields': perm_fields}),
            (('Fechas importantes'), {'fields': ('last_login', 'date_joined')})
        ]

    def get_form(self, request, obj=None, **kwargs):
        form = super(UserAdmin, self).get_form(request, obj, **kwargs)
        form.user = request.user
        if not request.user.is_superuser:
            if 'groups' in form.base_fields:
                groups = form.base_fields['groups']
                try:
                    user = LegacyUser.objects.get(
                        user_name=request.user.username
                    )
                    og_not_in_users_org = OrganizationGroups.objects.exclude(
                        organization=user.organization
                    )
                    exclude_group_id_list = []
                    for organization_group in og_not_in_users_org:
                        exclude_group_id_list.append(
                            organization_group.group_id
                        )
                    groups.queryset = groups.queryset.exclude(
                        id__in=exclude_group_id_list
                    )
                except LegacyUser.DoesNotExist:
                    pass
            if 'user_permissions' in form.base_fields:
                permissions = form.base_fields['user_permissions']
                permissions.queryset = permissions.queryset.exclude(
                    content_type__app_label__in=[
                        'admin', 'admin_interface', 'auth', 'contenttypes',
                        'organizations', 'sessions'
                    ]
                )
        return form

    def get_queryset(self, request: HttpRequest):
        qs = super().get_queryset(request)
        if request.user.is_superuser:
            return qs
        try:
            user = LegacyUser.objects.get(user_name=request.user.username)
            legacy_user_organization = LegacyUser.objects.filter(
                organization=user.organization
            )
            user_name_list = []
            for user in legacy_user_organization:
                user_name_list.append(user.user_name)
            return qs.filter(username__in=user_name_list)
        except LegacyUser.DoesNotExist:
            # It is assumed user is superuser.
            return qs

    def has_add_permission(self, request: HttpRequest):
        return False

    def has_delete_permission(self, request: HttpRequest, obj=None) -> bool:
        return False

UserForm ModelForm:

from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.forms.models import ModelForm

class UserForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(UserForm, self).__init__(*args, **kwargs)
        try:
            user: User = User.objects.get(id=self.instance.pk)
            if user == self.user:
                if self.user.is_superuser:
                    self.fields['is_superuser'].disabled = True
                self.fields['is_active'].disabled = True
                self.fields['groups'].disabled = True
                self.fields['user_permissions'].disabled = True
                self.fields['last_login'].disabled = True
                self.fields['date_joined'].disabled = True
        except User.DoesNotExist:
            pass

    def clean(self):
        for string, field in self.fields.items():
            if field.disabled is True:
                raise ValidationError(
                    'No puede realizar modificaciones a su propio usuario.',
                    'own_user_is_not_editable'
                )
        return super().clean()
Traceback (most recent call last):
  File “/usr/local/lib/python3.9/dist-packages/django/core/handlers/exception.py”, line 47, in inner
    response = get_response(request)
  File “/usr/local/lib/python3.9/dist-packages/django/core/handlers/base.py”, line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File “/usr/local/lib/python3.9/dist-packages/django/contrib/admin/options.py”, line 616, in wrapper
    return self.admin_site.admin_view(view)(*args, **kwargs)
  File “/usr/local/lib/python3.9/dist-packages/django/utils/decorators.py”, line 130, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File “/usr/local/lib/python3.9/dist-packages/django/views/decorators/cache.py”, line 44, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File “/usr/local/lib/python3.9/dist-packages/django/contrib/admin/sites.py”, line 232, in inner
    return view(request, *args, **kwargs)
  File “/usr/local/lib/python3.9/dist-packages/django/contrib/admin/options.py”, line 1660, in change_view
    return self.changeform_view(request, object_id, form_url, extra_context)
  File “/usr/local/lib/python3.9/dist-packages/django/utils/decorators.py”, line 43, in _wrapper
    return bound_method(*args, **kwargs)
  File “/usr/local/lib/python3.9/dist-packages/django/utils/decorators.py”, line 130, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File “/usr/local/lib/python3.9/dist-packages/django/contrib/admin/options.py”, line 1540, in changeform_view
    return self._changeform_view(request, object_id, form_url, extra_context)
  File “/usr/local/lib/python3.9/dist-packages/django/contrib/admin/options.py”, line 1604, in _changeform_view
    formsets, inline_instances = self._create_formsets(request, obj, change=True)
  File “/usr/local/lib/python3.9/dist-packages/django/contrib/admin/options.py”, line 1961, in _create_formsets
    for FormSet, inline in self.get_formsets_with_inlines(*get_formsets_args):
  File “/usr/local/lib/python3.9/dist-packages/django/contrib/admin/options.py”, line 799, in get_formsets_with_inlines
    yield inline.get_formset(request, obj), inline
  File “/usr/local/lib/python3.9/dist-packages/django/contrib/admin/options.py”, line 2059, in get_formset
    fields = flatten_fieldsets(self.get_fieldsets(request, obj))
  File “/usr/local/lib/python3.9/dist-packages/django/contrib/admin/options.py”, line 337, in get_fieldsets
    return [(None, {‘fields’: self.get_fields(request, obj)})]
  File “/usr/local/lib/python3.9/dist-packages/django/contrib/admin/options.py”, line 328, in get_fields
    form = self._get_form_for_get_fields(request, obj)
  File “/usr/local/lib/python3.9/dist-packages/django/contrib/admin/options.py”, line 2144, in _get_form_for_get_fields
    return self.get_formset(request, obj, fields=None).form
  File “/usr/local/lib/python3.9/dist-packages/django/contrib/admin/options.py”, line 2141, in get_formset
    return inlineformset_factory(self.parent_model, self.model, **defaults)
  File “/usr/local/lib/python3.9/dist-packages/django/forms/models.py”, line 1069, in inlineformset_factory
    fk = _get_foreign_key(parent_model, model, fk_name=fk_name)
  File “/usr/local/lib/python3.9/dist-packages/django/forms/models.py”, line 1038, in _get_foreign_key
    raise ValueError(
Exception Type: ValueError at /auth/user/1/change/
Exception Value: ‘organizations.OrganizationGroups’ has no ForeignKey to ‘auth.User’.

If you need more information feel free to ask it and I will edit my question!

Solution

I have solved the issue, it was caused by the way I was generating the OrganizationGroups inline using get_inlines in GroupAdmin (which is an override to the default GroupAdmin). Before I was using super().get_inlines(request, obj) and when I went to another module, the module was trying to recreate the OrganizationGroups inline, and the module did not have the relationship to OrganizationGroups (because it did not have to), and that is when it broke. Now I am generating the inline creating a completely new list without using super().get_inlines(request, obj) and apparently that solved the issue.

OrganizationInline:

class OrganizationInline(StackedInline):
    model = Organization.groups.through
    autocomplete_fields = ('organization')
    extra = 0
    min_num = 0
    max_num = 1
    verbose_name = _('organización')
    verbose_name_plural = _('organizaciones')

Previous get_inlines code:

def get_inlines(self, request, obj):
        inlines = super().get_inlines(request, obj)
        if request.user.is_superuser:
            inlines.clear()
            inlines.append(OrganizationInline)
        return inlines

New get_inlines code:

def get_inlines(self, request: HttpRequest, obj):
        if request.user.is_superuser:
            return [OrganizationInline]
        return []

Answered By – Santiago Muñoz

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published