While working on a project, we use a particular pattern in Django for getting a single object. We first filter the queryset by the id or slug and then check if the queryset exists. If so, then we return the object otherwise we return none.
Here is an example of how this looks in Python:
models = MyModel.objects.filter(id=24) if len(models) > 0: return models else: return 'could not find model'
Which brings us to the question of get versus filter, which one should we use? Should we lean towards checking the number of objects from the queryset filtering or rely on the get and handle/catch any exception thrown when no object is found?
According to James Bennet, Django release manager and author of django-registration, this isn’t necessary,
get()is provided specifically for this case. Use it.
Option 2 is almost precisely how the
get()method is actually implemented in Django, so there should be no “performance” difference (and the fact that you’re thinking about it indicates you’re violating one of the cardinal rules of programming, namely trying to optimize code before it’s even been written and profiled — until you have the code and can run it, you don’t know how it will perform, and trying to optimize before then is a path of pain).
The reason we avoid it is because the
get() method requires that we use a try/except block and I rarely see exceptions being caught in our code. Jeff Knupp suggests that writing Python with exception handling leads to cleaner code.
Let’s take a look at that example. If we use the get() method to retrieve a model instance and use exceptions to catch the case where the object could not be found
try: model = MyModel.get(id=24) return model except MyModel.DoesNotExist: raise
You’ll notice how we’re using the bare “raise” statement in the example in line 5. This will let you propagate the traceback and original exception up the chain. This means you do not have to invent new types of exceptions.
In his article, Jeff Knupp walks through the performance characteristics of using exceptions and finds that the trade-off in speed is acceptable, something on the order of thousandths of a millisecond. So there’s no way to use speed as an excuse to avoid exceptions!
Start using exceptions in Python and use the get() function of querysets whenever you want to find exactly one model instance.
UPDATE: Some good books on Django design patterns and coding style: