Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save maddrum/8fceeedcee09bd7c872f1c5010a90dde to your computer and use it in GitHub Desktop.
Save maddrum/8fceeedcee09bd7c872f1c5010a90dde to your computer and use it in GitHub Desktop.
Django admin Inline models with custom model manager - wrong querysetfix
# bug described here - https://code.djangoproject.com/ticket/33813
----
# Definitions
# models
class ReviewsModelManager(models.Manager):
def get_queryset(self):
qs = super().get_queryset()
qs = qs.filter(published=True)
return qs
def get_full_queryset(self):
qs = super().get_queryset()
return qs
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=100)
objects = BaseModelManager()
class BookReview(models.Model):
book = models.ForeignKey(Book, on_delete=models.CASCADE, related_name='reviews')
content = models.CharField(max_length=255)
published = models.BooleanField(default=False)
objects = ReviewsModelManager()
-----
# admin
class ReviewTabularInline(admin.TabularInline):
form = ReviewTabularInlineForm
def get_queryset(self, request):
qs = self.model._default_manager.get_full_queryset()
ordering = self.get_ordering(request)
if ordering:
qs = qs.order_by(*ordering)
return qs
class BookAdmin(admin.ModelAdmin):
inlines = [ReviewTabularInline]
def get_queryset(self, request):
qs = self.model._default_manager.get_full_queryset()
ordering = self.get_ordering(request)
if ordering:
qs = qs.order_by(*ordering)
return qs
----
# CUSTOM FORM WORKARAUND - SOLUTION
class ReviewTabularInlineForm(forms.ModelForm)
def clean(self):
"""
This is needed because the default validation fails.
InlineForm is always sending an 'id' field which is a ModelChoiceField.
That field gets the default qs not the modified one.
Therefore, objects that are not 'is_active' are not presented in the ModelChoiceField.
So when you try to updated from non-active to active those is not in the qs and the whole form
drops.
So this method is hijacking the original and revalidate the 'id' fields.
"""
if self.instance._meta.pk.name not in self.errors:
return super().clean()
if self.instance._meta.pk.name not in self.fields:
return super().clean()
cleaned_data = super().clean()
cleaned_data[self.instance._meta.pk.name] = self.instance
del self.errors[self.instance._meta.pk.name]
self.cleaned_data = cleaned_data
return cleaned_data
@maddrum
Copy link
Author

maddrum commented Jun 29, 2022

Filed that as Django bug - https://code.djangoproject.com/ticket/33813
Turns out to be the solution for that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment