#33813 closed Bug (duplicate)
Incorrect queryset for the 'id' field in the InlineModels' forms for django.cotrib.admin when using custom model managers
Reported by: | Hristo Trendafilov | Owned by: | nobody |
---|---|---|---|
Component: | contrib.admin | Version: | |
Severity: | Normal | Keywords: | admin, inlines, custom model manager |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Hello.
I have run into the following that is applicable to all Django versions.
Let's have two models and one is using a custom model manager like so
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()
The concept is that we are having one book that could have multiple reviews. A review could be published or not. We then apply a model manager to show only published book reviews. We keep the original queryset to use in the admin later in the get_full_queryset
Then we add a model admin in admin.py like so:
class ReviewTabularInline(admin.TabularInline): 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
Default querysets are hijacked in both admin and inline in order to include all reviews - both published and not.
If you go to the book admin
, the inline will display all reviews as it should.
If you try to mark a review from published
to not published
in the inline this will also work.
But if you try to mark not-published
to published
that is failing with an unspecified error.
The reason for this is that the inline form does not validate.
Validation fails because the inline form receives an extra field /called id
by default or whatever the PK name for the model is/
That field is a ModelChoiceField
.
But the queryset for that ModelChoiceField
is done with the get_queryset
of the custom model manager - ReviewsModelManager
that will not include items that are not marked as not-published
. Since you are trying to update non-published
to published
, that object is going to be missing in ModelChoiceField
queryset which causes the field not to validate and that drops the whole form.
This inconsistency. ModelChoiceField
id
field should also get the queryset that is specified in the inline's get_queryset
Change History (2)
comment:1 by , 2 years ago
Summary: | Incorrect querysets for InlineModels for django.cotrib.admin → Incorrect queryset for the 'id' field in the InlineModels' forms for django.cotrib.admin when using custom model managers |
---|
comment:2 by , 2 years ago
Resolution: | → duplicate |
---|---|
Status: | new → closed |
This is a duplicate of #33375 — in BaseModelFormSet.add_fields() we add the
id
ModelChoiceField
using the default manager.You'll need to provide the inline's `form` option to provide the base form class declaring the
id
ModelChoiceField
with the correct (for your case) QuerySet.