Last active
January 4, 2023 08:54
-
-
Save maddrum/8fceeedcee09bd7c872f1c5010a90dde to your computer and use it in GitHub Desktop.
Django admin Inline models with custom model manager - wrong querysetfix
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Filed that as Django bug - https://code.djangoproject.com/ticket/33813
Turns out to be the solution for that.