Python Django check if an attribute exists or has been set - python

I have a User object and a UserInfo object which have a one to one relationship. I am just adding the UserInfo object so some users already have User objects but not UserInfo objects. I want to check to see if the User object has a UserInfo object associated with it yet and if not redirect them to a page where I can get some info. I am still new to python and have tried doing an if request.user.user_info: which throws an exception when it doesn't exist so I ended up doing this:
user = request.user
try:
user.user_info.university
except:
print 'redirect to info page'
which works fine, but I feel like exceptions should be for exceptions and not for if statement substitutes. Is there a better way to do this?

I'd say that handling this with exceptions is the pythonic approach. You can do something like this:
try:
# do your thing when user.user_info exists
except AttributeError: # Be explicit with catching exceptions.
# Redirect.
There's a programming concept called it's easier to ask for forgiveness than permission (EAFP) which is used extensively in Python. We assume that attributes, keys and so forth exist for a given object and catch exceptions when they do not.
Here are some references and SO questions about EAFP.
Python glossary
What is the EAFP principle in Python
EAFP and what is really exceptional

Related

Continue after Try has failed

I have a function:
def save_to_models(all_item_tags):
from article.models import Articles
for item in all_item_tags:
newobj = Articles()
try:
newobj.pub_date =item.contents[9].contents[0]
except:
continue
try:
newobj.title =item.contents[1].contents[0]
except:
continue
try:
newobj.description =item.contents[5].contents[0]
except:
continue
try:
newobj.image =get_the_picture(item.contents[7])
except:
continue
newobj.save()
each model has unique=True so I'm using try, except to skip over the error I get when its trying to input a data that's already in the database. How can I condense this code? I feel like its a lot of unnecessary lines of code.
Django is smart: like stated in one of the comments, it's only gonna raise an error when the save() method is called. Until then, Article is a normal Python object. What you should would look more like this :
from psycopg2 import IntegrityError # this is the actual error raised by psycopg2 (the postgres python driver)
from article.models import Articles
for item in all_item_tags:
try:
new_article = Articles(
pub_date=item.contents[9].contents[0],
title=item.contents[1].contents[0],
description=item.contents[5].contents[0],
image=get_the_picture(item.contents[7])
new_article.save() # this is where the actual save happens
except IntegrityError:
# Handle the exception here
Another (more advanced) option is to override the save() method and put your logic there.
That said, you could also use get_or_created to do that. It looks like this:
for item in all_item_tags:
# this methods returns a boolean True of False if the object is already in the DB.
# use your unique identifier here
article, created = Article.objects.get_or_create(unique_id=...)
# You can then do a classic if-else without handling any errors...
if created:
# Great! The object a
else:
# the object exist, do something with it or not...
However, there are a few things I would suggest. My feeling is that you are diving into Django without really knowing Python. Django is a big beast that makes a lot of things really convenient (almost magical) but it's still Python. If you dive too deep and something breaks, it will be very hard for you to know what's going on. I would suggest furthering your knowledge of Python (it's an amazing language so it's gonna be fun) and then go back to Django or maybe start with a smaller framework like Flask which does less magic! For now, here's a link to the official doc on error handling so you can learn a bit more about it. Also, Django has really good doc so I would first look there if a problem arises.
Cheers and happy coding!

What's the most pythonic way to get name of the field when an exception related to IntegrityError is thrown?

I'm developing an API with DRF and I want to generate and return proper custom error messages when an exception related to IntegrityError is thrown.
To do this, I've implemented a custom exception handler. Inside the custom exception handler, I want to get the name of the field that causes the error from the Exception instance and then I'll generate and return proper message in the response.
Currently, I can do this by parsing the message attribute of the Exception instance but I'm not sure this is the best possible solution.
So, is there any pythonic way to get the name of the field from the Exception instance when an exception related to IntegrityError is thrown?
Thanks!
According to source it seems IntergityError is no more than an Exception so the only way is to parsing exception.message or exception.args.
You always can check what gives you print(dir(exception)) but I'm pretty sure only message and args will be helpful.
IMHO "parsing the message" is perfectly pythonic: simple, readable, you can do the same/similar solution in any other similar situation. I've seen something like this before:
# in a custom Serializer's create() method
try:
return super().create(validated_data)
except IntegrityError as e:
raise APIException(detail=e.args[0].split('DETAIL: ')[1])
It simple, it works and getting the extra data in any other way will likely involve something significantly more complicated.

Django exists() versus DoesNotExist

I have some questions about django exists() and DoesNotExist exception.
Example code:
id = 1
# first
if User.objects.get(pk=id).exists():
# my logic
pass
# second
try:
User.objects.get(pk=id)
# my logic
pass
except User.DoesNotExist:
return 0
I often use get() method. Which practice is better? Which code is better? The first or second?
if User.objects.get(pk=id).exists()
This won't work, so the question is pretty easy to answer: This way is inferior to the ways which do work :-)
I guess you actually didn't make a Minimal Complete Verifiable Example and so missed the error when you posted un-verified code.
So instead, I suppose you are asking about the difference between:
QuerySet.exists() when you have a QuerySet (e.g. from a filter operation).
For example:
if User.objects.filter(pk=id).exists():
# ... do the things that need that user to exist
Model.objects.get(…) and catching the Model.DoesNotExist exception type (or, if you want to be more general, the parent type ObjectDoesNotExist).
For example:
try:
user = User.objects.get(pk=id)
except User.DoesNotExist:
# ... handle the case of that user not existing
The difference is:
The QuerySet.exists method is on a queryset, meaning you ask it about a query (“are there any instances matching this query?”), and you're not yet attempting to retrieve any specific instance.
The DoesNotExist exception for a model is raised when you actually attempted to retrieve one instance, and it didn't exist.
Use whichever one correctly expresses your intention.
You can find more info in docs:
about exists(),but exists() works only for QuerySet
Returns True if the QuerySet contains any results, and False if not. This tries to perform the query in the simplest and fastest way possible, but it does execute nearly the same query as a normal QuerySet query.
exists() is useful for searches relating to both object membership in a QuerySet and to the existence of any objects in a QuerySet, particularly in the context of a large QuerySet.
But ObjectDoesNotExist works only with get().
Also you can try another approach:
user = User.objects.filter(id=2)
if user:
# put your logic
pass
Since we are in Django, we'll try to catch the error with Django functionality instead of the common way(which is using Exceptions with Python).
id = 1
def a_query(id):
qs = User.objects.filter(pk=id)
if qs.exists():
return qs.first()
return None
In here, the method exists() helps you catching the error(if there's any).
ref: https://docs.djangoproject.com/en/3.0/ref/models/querysets/#django.db.models.query.QuerySet.exists
in django model,
if you gonna use model.objects.get() if it wasn't exist it raise an error. in that case you can use DoesNotExist along with except:
try:
val = Model.objects.get(pk=val) # if nothing found it will raise an exception
exception:
you can trace an exception without mentioning anything on top.
(or)
exception ObjectDoesNotExist:
# it will come here if exception is DoesNotExist
For Django version 2.0.6, you can do the following, and it will work:
if Model.objects.filter(my_id=objectid).exists():
myobject = get_object_or_404(Model, my_id=objectid)
context = {'myobject': myobject}
return render(request, self.template_name, context)
You can get more info here: https://docs.djangoproject.com/en/2.1/ref/models/querysets/
It's my understanding that you're asking whether to use if statements or try catch on your code. I personally prefer to avoid using try catch, a think it's an ugly syntax, when I do want to raise an exception, I use a python keyword raise, to me, it makes the code cleaner.
Code example:
user = User.objects.filter(id=2)
if not user:
raise ObjectDoesNotExist

Proper use of exceptions in python/django orm

I'm trying to get an object, if it existed ok if not then something else and so on. Is it correct to do the following? I've heared that exceptions are expensive and shuold be avoided, is that correct?
try:
user = User.objects.get(user=id)
except ObjectDoesNotExist:
try:
user = User.objects.get(email=id)
except ObjectDoesNotExist:
try:
# ...
finally:
# do the final thing
They are somewhat expensive, but certainly not too expensive to use when needed. I'd be more concerned about hammering the database with multiple queries You can avoid both problems by getting the results for all possible fields back in one query.
from django.contrib.auth.models import User
from django.db.models import Q
user = User.objects.filter(Q(user=id) | Q(email=id) | [...])[0]
This relies on django's Q-objects, which allow you to create conditions joined together in more complex ways than the usual AND joins that you usually get when building filters. If you aren't concerned about the possibility of getting multiple objects back, you can still use the get() method like you did in your example.
The cost of a try/except is explained here: cost-of-exception-handlers-in-python
I suggest to catch things that should not happen or only happen rarely (real exceptions) with a try/except and more common situations with conditions.
Especially in a case like a Model.objects.get() where the underlying sql returns an empty list that wouldn't raise an exception if called as a filter.
users = User.objects.filter(user=id)[:1]
user = users and users[0]

Exception Value field is blank when throwing custom exceptions in django

I have custom exceptions in my django project that look like this:
class CustomFooError(Exception):
def __init__(self, msg="Something went wrong with Foo."):
self.msg = msg
def __str__(self):
return repr(self.msg)
At various points in my code I will raise exceptions like this:
raise CustomFooError("Things are going badly")
When Django catches these errors, in debug mode I get django's standard pretty stack-trace page. But I never see my error messages -- "Things are going badly" never shows up in the debug error page.
It seems they should show up as the Exception Value on the error page. I walked back through the django source far enough to find out that this is the value field from sys.exc_info() which is consistently tersely documented as "[the exception's] associated value or the second argument to raise, which is always a class instance if the exception type is a class object." Unfortunately, I don't know what to do with this information.
So my question is: How should I be writing and raising my custom exceptions to get more useful data to show up in places like the django error screen?
I would just use super and let the constructor of Exception handle assigning the msg attribute:
class CustomFooError(Exception):
def __init__(self, msg=None):
if msg is None:
msg = 'Something went wrong with Foo.'
super(CustomFooError, self).__init__(msg)
I just tested this from within a Django environment and it correctly displayed the message I passed to the exception constructor or the default one if None was passed.
#AdmiralNemo is right: let the base class do the work.
But to dig into your code a little deeper, the problem is that you don't tie into the Exception implementation at all. Exception(s) stores s in the .message attribute, not .msg. It also stores it as (s,) in the .args attribute. Your code doesn't set either of these attributes, which is probably why Django can't find a message to display.
Also, your __str__ method is odd. It should return self.msg, not repr(self.msg), which would add quotes around the string, and potentially escapes inside the text.

Categories