In Django, how can I gracefully add a query filter to a large group or all members of an object?
We currently have a media object model with a publication date that determines when the item appears on our site. We were asked to add the ability to specify date ranges when an item is enabled. If an item has no associated date ranges, the date it was posted determines whether it shows. If it has date ranges, then they must be published and must fall within one of the specified date ranges to be displayed on the site.
Now, the obvious way to do this is to go through and modify each set of queries in our code by adding a filter to exclude items that have date ranges but don't fall into any of them. But we have many such requests, and almost all of them need to be changed. So I was wondering if there is some central place where I could add this filter so that it affects all queries.
Of course, this would have to be done so that some queries (i.e. the list of media items in the admin) would still display all items. So more than what I need is an elegant way to influence a large group of queries with a specific filter without finding every instance of the code. Any ideas?
source to share
You are looking for model managers .
Something like:
class MediaItemManager(models.Manager):
def get_queryset(self):
return super(MediaItemManager, self).get_queryset().filter(...)
You can set this as the default manager in your model like this:
class MediaItem(models.Model):
objects = MediaItemManager()
all_objects = models.Manager()
You can ensure that this does not affect the administrator by specifying get_queryset
in the subclass ModelAdmin
:
class MyModelAdmin(admin.ModelAdmin):
def get_queryset(self, request):
qs = self.model.all_objects.get_queryset()
ordering = self.get_ordering(request)
if ordering:
qs = qs.order_by(*ordering)
return qs
This is a rather unfortunate duplication of the base method get_queryset
, as there is no way to explicitly override the admin. A better alternative would probably be to name your custom manager filtered_objects
and use the default manager as objects
.
source to share
This is what custom model managers are . Define a manager subclass and override with get_query_set
your request. Then you first need to explicitly define the default manager and then call your own objects
and it should just work.
Note, although you can do this, I would suggest doing it. At the very least, call your custom manager something else - for example restricted
- and search and replace the code to replace the object references with a custom version. This allows people reading your code to understand that the request is not standard.
source to share