Can we use queryset.first() instead of catching exception? - django

I normally use MyModel.objects.filter(pk=1).first() instead of MyModel.objects.get(pk=1) when I am not entirely sure whether there is any object with pk=1 or not.
Is it a bad practice doing so for this scenario or I need to
try:
MyModel.objects.get(pk=1)
except MyModel.DoesNotExist:
print('not found')
I normally do MyModel.objects.filter(pk=1).first() for the less code.

Nothing wrong with that. You get None if the object does not exist. It saves hitting the database with more than one query, compared to using .exists()
I would call the following self-documenting without a comment, but if you think not, then comment it
obj = MyModel.objects.filter(pk=1).first()
if obj is None:
# no such object exists...
...
else
# process obj
...
Has just occurred to me, this is a use for the "walrus" operator if you are using an up to date Python
if obj := MyModel.objects.filter(pk=1).first() is None:
but not sure I prefer it!

You can use django built-in method get_object_or_404 this will return 404 page if object does not exits
from django.shortcuts import render, get_object_or_404
obj = get_object_or_404(MyModel, id=pk)

Related

Django Rest Framework how to check an object is exists or not?

I'm trying to check if an object is exists or not and this is how I do:
try:
control = Card.objects.filter(cc_num = cc_number)[0]
exists = True
except (IndexError):
exists = False
It works but I wonder if there is a more practical way to do?
(The reason I use except(IndexError) is I'm finding the object with typing [0] to end of model.objects.filter().)
Note: cc_num is unique.
you can do something like this:
if model.objects.filter(email = email).exists():
# at least one object satisfying query exists
else:
# no object satisfying query exists
Check this:
https://docs.djangoproject.com/en/stable/ref/models/querysets/#django.db.models.query.QuerySet.exists
try this:
use Card.objects.get() since cc_num is unique, and only one object will be retrieved if it exists
try:
controls = Card.objects.get(cc_num == cc_number)
#do something
except DoesNotExist:
#do something in case not
https://www.codegrepper.com/code-examples/python/check+if+a+value+exist+in+a+model+Django
you can try like this,import this
from django.core.exceptions import ObjectDoesNotExist
try:
controls = Card.objects.get(cc_num == cc_number)
#do something
except ObjectDoesNotExist:
#do something in case not

How do I import the Django DoesNotExist exception?

I'm trying to create a UnitTest to verify that an object has been deleted.
from django.utils import unittest
def test_z_Kallie_can_delete_discussion_response(self):
...snip...
self._driver.get("http://localhost:8000/questions/3/want-a-discussion")
self.assertRaises(Answer.DoesNotExist, Answer.objects.get(body__exact = '<p>User can reply to discussion.</p>'))
I keep getting the error:
DoesNotExist: Answer matching query does not exist.
You can also import ObjectDoesNotExist from django.core.exceptions, if you want a generic, model-independent way to catch the exception:
from django.core.exceptions import ObjectDoesNotExist
try:
SomeModel.objects.get(pk=1)
except ObjectDoesNotExist:
print 'Does Not Exist!'
You don't need to import it - as you've already correctly written, DoesNotExist is a property of the model itself, in this case Answer.
Your problem is that you are calling the get method - which raises the exception - before it is passed to assertRaises. You need to separate the arguments from the callable, as described in the unittest documentation:
self.assertRaises(Answer.DoesNotExist, Answer.objects.get, body__exact='<p>User can reply to discussion.</p>')
or better:
with self.assertRaises(Answer.DoesNotExist):
Answer.objects.get(body__exact='<p>User can reply to discussion.</p>')
DoesNotExist is always a property of the model that does not exist. In this case it would be Answer.DoesNotExist.
One thing to watch out for is that the second parameter to assertRaises needs to be a callable - not just a property. For instance, I had difficulties with this statement:
self.assertRaises(AP.DoesNotExist, self.fma.ap)
but this worked fine:
self.assertRaises(AP.DoesNotExist, lambda: self.fma.ap)
self.assertFalse(Answer.objects.filter(body__exact='<p>User...discussion.</p>').exists())
This is how I do such a test.
from foo.models import Answer
def test_z_Kallie_can_delete_discussion_response(self):
...snip...
self._driver.get("http://localhost:8000/questions/3/want-a-discussion")
try:
answer = Answer.objects.get(body__exact = '<p>User can reply to discussion.</p>'))
self.fail("Should not have reached here! Expected no Answer object. Found %s" % answer
except Answer.DoesNotExist:
pass # all is as expected

Why is swig not handling AttributeError correctly when using __getattribute__?

I have a class that is getting exported from C++ to Python in SWIG. Everything works fine. Now I want to define getattribute to handle virtualize access to variables and functions that are defined in a scripting language built into the C++ code. However when I define the getattribute function using %pythoncode it is not working as expected. If I cannot find the variables or functions I am supposed to raise and exception called AttributeError. However the SWIG function getattr chokes on this.
%pythoncode %{
def __getattribute__(self, attribute):
raise AttributeError(attribute)
%}
Now if I do this it is okay:
%pythoncode %{
def __getattribute__(self, attribute):
return object.__getattribute__(self, attribute)
%}
So, the behaviour of the SWIG produced getattr does not work properly when I raise an AttributeError like I am supposed to do when and attribute is not found. So, for my purposes I will be using the second example and inserting my own code before the routine to determine if a virtualized function exists. If not I will let the default object getattribute function handle it.
Is there a better way to approach this?
Now that I look at this i am finding it does not work in regular Python 2.7 either:
class testmethods(object):
def __init__(self):
self.nofunc1 = None
self.nofunc2 = "nofunc2"
def func1(self):
print "func1"
def func2(self):
print "func2"
def __getattribute__(self, attribute):
print "Attribute:",attribute
raise AttributeError(attribute)
This raises the exception, but does not switch responsibility to "getattr" function. So how is one supposed to handle this?
Okay, strike that. If getattr is present in the object raising the exception does work. So swig behaviour is not correct.
Now, I think I figured this out:
def __getattribute__(self, attribute):
try:
defattrib = object.__getattribute__(self, attribute)
except AttributeError,e:
defattrib = None
if defattrib is not None:
return defattrib
# find attributes in user defined functions
...
# if an attribute cannot be found
raise AttributeError(attribute)
This seems to work fine. swig getattr seems to handle the exception properly. So it is just my code that was not working.

django order_by FieldError exception can not be catched

from django.core.exceptions import FieldError
#This is a method of a class
def _order_item_list(self, item_list, order_items_by, previous_order_by):
if order_items_by == previous_order_by:
order_items_by = '-' + order_items_by
try:
result = item_list.order_by(order_items_by)
except FieldError:
result = item_list
return result, order_items_by
Now when I order by valid fields following the generated link,everything works perfect. When I edit a link and add some dummy fieldnames for ordering, it should be catched by this exception and the original list should be returned. But it is not happening, instead I always get a FieldError from django.
FieldError at ...
Cannot resolve keyword u'fgsdffds' into field. Choices are: ...
The reason the exception is not caught is because the QuerySet has not been evaluated yet.
To validate an arbitrary (user-specified) value used for a model field or order_by value, simply check to see if that model has a field by that name.
For example, say you have a model called Ticket and an arbitrary GET parameter called field_name. Here's how you might handle creating a valid QuerySet in views.py:
from django.db.models import FieldDoesNotExist
from myapp.models import Ticket
def index(request):
default_field = 'id'
field_name = request.GET.get('field_name', default_field)
try:
Ticket._meta.get_field_by_name(field_name)
except FieldDoesNotExist:
field_name = default_field
tickets = Ticket.objects.all().order_by(field_name)
return ...
This means there's a typo, or the exception happens elsewhere. Insert a debug line:
import pdb; pdb.set_trace()
before the try-except and see how the code is executed. Try PUDB or IPDB debuggers instead of the standard one. Many questions disappear when you have a debugger and can see exactly what goes wrong.
I faced the same problem and surely it was because the exception is later. In order to raise exception in try-catch block I modified the code in following manner:
try:
result = item_list.order_by(order_items_by)
**result = list(result)**
except FieldError:
result = item_list
This worked for me.

Django; is it possible to have a default value for queryset

employee = Employee.objects.filter('age' = 99)
We assume this queryset is empty.
If I use employee[0], that will return index out of range error, so is it possible to have a None as default value here?
`employee[0] or None`? # This won't work, but this is what I mean.
If you just want to get a single instance, use get, not filter:
employee = Employee.objects.get(age = 99)
If that doesn't exist, you'll get a Employee.DoesNotExist exception, which you'll need to catch. If there's more than one 99 year-old employee, you'll get a Employee.MultipleObjectsReturned exception, which you may want to catch.
There's always django-annoying's get_object_or_None if you're feeling lazy!
from annoying.functions import get_object_or_None
obj = get_object_or_None(Employee, age=99)
If you don't want to use all of django-annoying, you can always add get_object_or_None somewhere, it just looks like this:
def get_object_or_None(klass, *args, **kwargs):
"""
Uses get() to return an object or None if the object does not exist.
klass may be a Model, Manager, or QuerySet object. All other passed
arguments and keyword arguments are used in the get() query.
Note: Like with get(), a MultipleObjectsReturned will be raised if
more than one object is found.
"""
queryset = _get_queryset(klass)
try:
return queryset.get(*args, **kwargs)
except queryset.model.DoesNotExist:
return None
On Django >= 1.6 there's the first method, so you can just do:
employee = Employee.objects.filter(age=99).first()
Note, however, that this only makes sense if you know the QuerySet is guaranteed to return either 1 or 0 results.
This should work:
employee[0] if employee else None
first_employee = employee[0] if employee.count() > 0 else None