How to exclude results with get_object_or_404?
In Django you can use the exclude to create SQL similar to not equal
. An example could be.
Model.objects.exclude(status='deleted')
开发者_如何学Go
Now this works great and exclude is very flexible. Since I'm a bit lazy, I would like to get that functionality when using get_object_or_404
, but I haven't found a way to do this, since you cannot use exclude on get_object_or_404
.
What I want is to do something like this:
model = get_object_or_404(pk=id, status__exclude='deleted')
But unfortunately this doesn't work as there isn't an exclude query filter or similar. The best I've come up with so far is doing something like this:
object = get_object_or_404(pk=id)
if object.status == 'deleted':
return HttpResponseNotfound('text')
Doing something like that, really defeats the point of using get_object_or_404
, since it no longer is a handy one-liner.
Alternatively I could do:
object = get_object_or_404(pk=id, status__in=['list', 'of', 'items'])
But that wouldn't be very maintainable, as I would need to keep the list up to date.
I'm wondering if I'm missing some trick or feature in django to use get_object_or_404
to get the desired result?
Use django.db.models.Q
:
from django.db.models import Q
model = get_object_or_404(MyModel, ~Q(status='deleted'), pk=id)
The Q objects lets you do NOT (with ~
operator) and OR (with |
operator) in addition to AND.
Note that the Q object must come before pk=id
, because keyword arguments must come last in Python.
The most common use case is to pass a Model. However, you can also pass a QuerySet instance:
queryset = Model.objects.exclude(status='deleted')
get_object_or_404(queryset, pk=1)
Django docs example: https://docs.djangoproject.com/en/1.10/topics/http/shortcuts/#id2
There's another way instead of using Q objects. Instead of passing the model to get_object_or_404
just pass the QuerySet to the function instead:
model = get_object_or_404(MyModel.objects.filter(pk=id).exclude(status='deleted'))
One side effect of this, however, is that it will raise a MultipleObjectsReturned
exception if the QuerySet returns multiple results.
get_object_or_404
utilizes the get_queryset
method of the object manager. If you override the get_queryset
method to only return items that aren't "deleted" then get_object_or_404
will automatically behave as you want. However, overriding get_queryset
like this will likely have issues elsewhere (perhaps in the admin pages), but you could add an alternate manager for when you need to access the soft deleted items.
from django.db import models
class ModelManger(models.Manger):
def get_queryset(self):
return super(ModelManger, self).get_queryset().exclude(status='deleted')
class Model(models.Model):
# ... model properties here ...
objects = ModelManager()
all_objects = models.Manager()
So if you need only non-deleted items you can do get_object_or_404(Models, id=id)
but if you need all items you can do get_object_or_404(Models.all_objects, id=id)
.
精彩评论