My main ask: Is there any way to change the behavior of a related lookup such as MyModel.objects.filter(relationship__field="value")
?
Consider this setup. I've got a one-to-many relationship with a custom Manager that filters out Books with active=False
from django.db import models
class ActiveOnly(models.Manager):
use_for_related_fields = True
def get_queryset(self):
return super(ActiveOnly, self).get_queryset().filter(active=True)
class Author(models.Model):
name = models.TextField()
class Book(models.Model):
active = models.BooleanField()
author = models.ForeignKey(Author, related_name="books")
title = models.TextField()
objects = ActiveOnly()
And let's create some data:
jim = Author.objects.create(name="Jim")
ulysses = Book.objects.create(title="Ulysses", author=jim, active=True)
finnegans = Book.objects.create(title="Finnegan's Wake", author=jim, active=False)
bill = Author.objects.create(name="Bill")
hamlet = Book.objects.create(title="Hamlet", author=bill, active=False)
Essentially, I never want to have to deal with inactive Books. Here are some queries to test various scenarios.
>>> Book.objects.all().count() # expecting the 1 active book: good
1
>>> jim.books.all() # also expecting only 1: good
1
>>> Author.objects.filter(books__title="Hamlet").first().name
u'Bill'
# ^ this is what I don't want to see, because bill's only book has active=False.
# I want the queryset to return no results.
Is there any way to change the behavior of the books__*
lookup to include the additional filter on active
?
In Django 1.10 the Manager.use_for_related_fields
is deprecated in favor of setting Meta.base_manager_name
on the model. See the updated documentation for details:
Model._base_manager
Using managers for related object access
By default, Django uses an instance of the
Model._base_manager
manager class when accessing related objects (i.e.choice.poll
), not the_default_manager
on the related object. This is because Django needs to be able to retrieve the related object, even if it would otherwise be filtered out (and hence be inaccessible) by the default manager.If the normal base manager class (
django.db.models.Manager
) isn’t appropriate for your circumstances, you can tell Django which class to use by settingMeta.base_manager_name
.
The warning, not do exclude objects in the BaseManager still stands!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With