In Django, how can I get an exception's message? - python

In a view function, I have something like:
try:
url = request.POST.get('u', '')
if len(url) == 0:
raise ValidationError('Empty URL')
except ValidationError, err:
print err
The output is a string: [u'Empty URL']
When I try to pass the error message to my template (stuffed in a dict, something like { 'error_message': err.value }), the template successfully gets the message (using {{ error_message }}).
The problem is that I get the exact same string as above, [u'Empty URL'], with the [u'...']!
How do I get rid of that?
(Python 2.6.5, Django 1.2.4, Xubuntu 10.04)

ValidationError actually holds multiple error messages.
The output of print err is [u'Empty URL'] because that is the string returned by repr(err.messages) (see ValidationError.__str__ source code).
If you want to print a single readable message out of a ValidationError, you can concatenate the list of error messages, for example:
# Python 2
print '; '.join(err.messages)
# Python 3
print('; '.join(err.messages))

If you are importing ValidationError from django.core.exceptions you can simply use messages method as err.messages.
https://github.com/django/django/blob/main/django/core/exceptions.py#L124
If you are importing ValidationError from rest_framework.serializers , there is no messages property for the ValidationError but there is detail property. so you can use err.detail which will give you a dictionary.
In order to concatenate all the error messages as a string, you can use
"".join(["".join(v) for v in err.detail.values()])
or
"".join([k+" - "+"".join(v) for k,v in err.detail.items()])
https://github.com/encode/django-rest-framework/blob/master/rest_framework/exceptions.py#L143

A more general way to always know where the message is to call the dir function on your err variable to show you all the attribute of this object.
from that list you can infer where the messages are, in my case (with django 3.2) i found out that the message dict is in an attribute called args.
so to use it (with a print for example)
try:
url = request.POST.get('u', '')
if len(url) == 0:
raise ValidationError('Empty URL')
except ValidationError, err:
print(err.args) # HERE THE ERROR DICT MESSAGE IS ACCESSIBLE

I fixed it by changing ValidationError to BaseException.

Related

how test a value in try except block with pytest and coverage in django setting?

I would like to test a Try/Except block, but I try any method Coverage that says that part is not tested. My goal is to test the Error_Message from the user's django settings, and if the user did not define file_validator_error_message in his Django settings, I would like to consider a default value (file} is not valid). Now I want to test the ERROR_MESSAGE value in the area where I test the Attributeerror error but I don't know how? The point is that in line 10 Coverage says: line 10 didn't jump to line 11, because the exception caught by line 10 didn't happen
code :
try:
# Get Error Message From Django Setting
ERROR_MESSAGE = settings.FILE_VALIDATOR_ERROR_MESSAGE
except AttributeError:
ERROR_MESSAGE = "{file} is not valid"
except ImproperlyConfigured:
ERROR_MESSAGE = "{file} is not valid"
I want to test the ERROR_MESSAGE value in the AttributeerRor error section
I corrected my code as follows and the problem was fixed :
try:
# Get Error Message From Django Setting
CUSTOM_ERROR_MESSAGE = settings.FILE_VALIDATOR_ERROR_MESSAGE
except (AttributeError, ImproperlyConfigured):
CUSTOM_ERROR_MESSAGE = "{file} is not valid"

how can i use try and except while getting model objects?

I have to use a try and except block with the following code, as I am trying to get a model class object but in case if database is empty so for that I need to use try and except.
if(txStatus=='SUCCESS'):
order=Order.objects.get(id=id) #NEED TRY ACCEPT BLOCK FOR THIS
URL = payment_collection_webhook_url
request_data ={}
json_data = json.dumps(request_data)
requests.post(url = URL, data = json_data)
return Response(status=status.HTTP_200_OK)
try..except..else..finally block works like this:
try:
order=Order.objects.get(id=id)
except ObjectDoesNotExist(or MultipleObjectsReturned in case it returns more
than 1 queryset or instance):
...Handle the exception, Write any code you want
else:
...This gets called if there is no exception or error, means it will
execute right after try statement if there's no error, so if you want
something more to happen only if the code doesn't throw an error, you can
write it here
finally:
...This gets executed no matter what in any case, means if there's
something you want to execute regardless of whether it throws an
exception or not, you write it here.
It is as simple as this:
try:
order = Order.objects.get(id=order_id)
except Order.DoesNotExist:
# Exception thrown when the .get() function does not find any item.
pass # Handle the exception here.
You can find more information about DoesNotExist exception here.
#Hagyn is correct, but in Django there is another way to do that:
Something like this:
orders = Order.objects.filter(id=order_id)
if orders.exists():
order = orders.last()
else:
# do rest of the things

How do I catch a psycopg2.errors.UniqueViolation error in a Python (Flask) app?

I have a small Python web app (written in Flask) that uses sqlalchemy to persist data to the database. When I try to insert a duplicate row, an exception is raised, something like this:
(psycopg2.errors.UniqueViolation) duplicate key value violates unique constraint "uix_my_column"
I would like to wrap the exception and re-raise my own so I can add my own logging and messaging that is specific to that particular error. This is what I tried (simplified):
from db import DbApi
from my_exceptions import BadRequest
from psycopg2.errors import UniqueViolation # <-- this does not exist!
class MyClass:
def __init__(self):
self.db = DbApi()
def create(self, data: dict) -> MyRecord:
try:
with self.db.session_local(expire_on_commit=False) as session:
my_rec = MyRecord(**data)
session.add(my_rec)
session.commit()
session.refresh(my_rec)
return my_rec
except UniqueViolation as e:
raise BadRequest('A duplicate record already exists')
But this fails to trap the error because psycopg2.errors.UniqueViolation isn't actually a class name (!).
In PHP, this would be as easy as catching copy/pasting the classname of the exception, but in Python, this is much more obfuscated.
There was a similar question here, but it didn't deal with this specific use-case and (importantly), it did not clarify how one can identify the root exception class name.
How does one find out what exception is actually being raised? Why does Python hide this?
The error that you have posted in your question isn't the error that has been raised. The full error message is:
sqlalchemy.exc.IntegrityError: (psycopg2.errors.UniqueViolation) duplicate key value violates unique constraint "model_name_key"
The key part being the SQLAlchemy error which you've chosen to omit for some reason. SQLAlchemy catches the original error, wraps it in it's own error and raises that.
but in Python, this is much more obfuscated... Why does Python hide this?
This isn't obfuscation, nothing is hidden, the behavior is documented, specific to the frameworks that you are using and is not enforced by the Python language. SQLAlchemy is an abstraction library and if it were to raise exceptions specific to the underlying dpapi adapter, it would significantly reduce the portability of code written within it.
From the docs:
SQLAlchemy does not generate these exceptions directly. Instead, they
are intercepted from the database driver and wrapped by the
SQLAlchemy-provided exception DBAPIError, however the messaging within
the exception is generated by the driver, not SQLAlchemy.
Exceptions raised by the dbapi layer are wrapped in a subclass of the sqlalchemy.exc.DBAPIError, where it is noted:
The wrapped exception object is available in the orig attribute.
So it's very straightforward to catch the SQLAlchemy exception and inspect the original exception, which is an instance of psycopg2.errors.UniqueViolation, as you'd expect. However, unless your error handling is very specific to the type raised by the dbapi layer, I'd suggest that inspecting the underlying type might be unnecessary as the SQLAlchemy exception that is raised will provide enough runtime information to do what you have to do.
Here is an example script that raises a sqlalchemy.exc.IntegrityError, catches it, inspects the underlying exception through the orig attribute and raises an alternate, locally-defined exception.
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from psycopg2.errors import UniqueViolation
engine = create_engine("postgresql+psycopg2://some-user:mysecretpassword#localhost:5432/some-user")
Base = declarative_base()
Session = sessionmaker(bind=engine)
class BadRequest(Exception):
pass
class Model(Base):
__tablename__ = "model"
id = Column(Integer, primary_key=True)
name = Column(String, unique=True)
if __name__ == "__main__":
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
s = Session()
s.add(Model(name="a"))
s.commit()
s.add(Model(name="a"))
try:
s.commit()
except IntegrityError as e:
assert isinstance(e.orig, UniqueViolation) # proves the original exception
raise BadRequest from e
And that raises:
sqlalchemy.exc.IntegrityError: (psycopg2.errors.UniqueViolation) duplicate key value violates unique constraint "model_name_key"
DETAIL: Key (name)=(a) already exists.
[SQL: INSERT INTO model (name) VALUES (%(name)s) RETURNING model.id]
[parameters: {'name': 'a'}]
(Background on this error at: http://sqlalche.me/e/gkpj)
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File ".\main.py", line 36, in <module>
raise BadRequest from e
__main__.BadRequest
I have a slightly different answer that avoids looking up the specific numerical error code. Simply import the constant that defines UNIQUE_VIOLATION:
from psycopg2.errorcodes import UNIQUE_VIOLATION
from psycopg2 import errors
Then use the error lookup function:
except errors.lookup(UNIQUE_VIOLATION) as e:
Solved the issue for me. You can import other error code constants as necessary.
According to psycopg2 docs:
In compliance with the DB API 2.0, the module makes informations about errors available through the following exceptions:
exception psycopg2.Error
Exception that is the base class of all other error exceptions. You can use this to catch all errors with one single except statement. Warnings are not considered errors and thus not use this class as base. It is a subclass of the Python StandardError (Exception on Python 3).
Thus, the proper way to catch the exceptions is:
try:
# your stuff here
except psycopg2.Error as e:
# get error code
error = e.pgcode
# then do something.
Yours in particular is error 23505 according to the ErrCodes Table
For a quick reference to how to import the psycopg2 UniqueViolation (or any other error)
with some quick recipes.
1) Import UniqueViolation
import traceback # Used for printing the full traceback | Better for debug.
from psycopg2 import errors
UniqueViolation = errors.lookup('23505') # Correct way to Import the psycopg2 errors
# ...... Code ....
try:
db.commit()
except UniqueViolation as err:
traceback.print_exc()
db.rollback()
# ...... Code ....
2) Import IntegrityError
UniqueViolation base-exception is actually IntegrityError , so for a broader error catch
(for whatever reason, normally is not recommended, but rule are meant to be broken)
import traceback # Used for printing the full traceback | Better for debug.
from psycopg2._psycopg import IntegrityError
# ...... Code ....
try:
db.commit()
except IntegrityError as err:
traceback.print_exc()
db.rollback()
# ...... Code ....
3) Source Code
The psycopg2 errors module is found here --> /psycopg2/errors.py, and is actually like a gateway for the real error code list.
Here you can see the function used to call the correct error by given code:
#
# NOTE: the exceptions are injected into this module by the C extention.
#
def lookup(code):
"""Lookup an error code and return its exception class.
Raise `!KeyError` if the code is not found.
"""
from psycopg2._psycopg import sqlstate_errors # avoid circular import
return sqlstate_errors[code]
But the really juicy stuff are found here ---> \psycopg2\_psycopg\__init__.py
Once here find the variable sqlstate_errors which is a dict containing the codes as value and the actual error as Note, here is a small snippet (is pretty big):
sqlstate_errors = {
'02000': None, # (!) real value is "<class 'psycopg2.errors.NoData'>"
'02001': None, # (!) real value is "<class 'psycopg2.errors.NoAdditionalDynamicResultSetsReturned'>"
'03000': None, # (!) real value is "<class 'psycopg2.errors.SqlStatementNotYetComplete'>"
'08000': None, # (!) real value is "<class 'psycopg2.errors.ConnectionException'>"
'08001': None, # (!) real value is "<class 'psycopg2.errors.SqlclientUnableToEstablishSqlconnection'>"
'08003': None, # (!) real value is "<class 'psycopg2.errors.ConnectionDoesNotExist'>"
'08004': None, # (!) real value is "<class 'psycopg2.errors.SqlserverRejectedEstablishmentOfSqlconnection'>"
'08006': None, # (!) real value is "<class 'psycopg2.errors.ConnectionFailure'>"
'08007': None, # (!) real value is "<class 'psycopg2.errors.TransactionResolutionUnknown'>"
'08P01': None, # (!) real value is "<class 'psycopg2.errors.ProtocolViolation'>"
# -------- Lots of lines ---------- #
'23503': None, # (!) real value is "<class 'psycopg2.errors.ForeignKeyViolation'>"
# There you are!!!
'23505': None, # (!) real value is "<class 'psycopg2.errors.UniqueViolation'>"
# ----------------
'23514': None, # (!) real value is "<class 'psycopg2.errors.CheckViolation'>"
'23P01': None, # (!) real value is "<class 'psycopg2.errors.ExclusionViolation'>"
4) Documentation
psycopg.org | SQLSTATE Exception
postgresq | PostgreSQL Error Codes
from db import DbApi
from my_exceptions import BadRequest
from psycopg2 import errors
class MyClass:
def __init__(self):
self.db = DbApi()
def create(self, data: dict) -> MyRecord:
try:
with self.db.session_local(expire_on_commit=False) as session:
my_rec = MyRecord(**data)
session.add(my_rec)
session.commit()
session.refresh(my_rec)
return my_rec
except errors.lookup("23505"):
raise BadRequest('A duplicate record already exists')
I get a psycopg2.errors.UniqueViolation error. How do I handle it ?
please refer to https://www.psycopg.org/docs/errors.html for more details about how to handle psycopg2 errors
The answers above didn't work for me for some reason, but this did:
from asyncpg.exceptions import UniqueViolationError
...
except exc.IntegrityError as e:
if e.orig.__cause__.__class__ == UniqueViolationError:
# raise some error specific to unique violation errors
I solved this problem in this way:
from asyncpg.exceptions import UniqueViolationError
try:
user.id = await self.database.execute(query)
except UniqueViolationError:
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="User with this credentials already exist")

Parse Json if a certain value exists

I need to parse "order" from below JSON , if only value of "success" = 'true' , else raise an exception.
Tried below, but not sure how to include the 'true' check in try:
{
"success":true,
"order":"123345"
}
below is the code , I am trying , which is not giving any result from print as well.
import json
from pprint import pprint
data = json.load(open('data.json'))
#pprint(data)
try:
check_key = data['success']
except KeyError:
#continue
print(check_key)
#print(data['order'])
You should evaluate data['success'] in a condition, whether it is false, then you raise your exception.
import json
data = json.load(open('data.json'))
if data['success'] is not True:
raise Exception("Success is false")
order = data['order']
print(order)
I need to parse "order" from below JSON , if only value of "success" = 'true' , else raise an exception.
There's no function that will automatically raise an exception if a value is False; you need to write that yourself.
But it's dead easy:
check_key = data.get('success')
if not check_key:
raise MyKindOfError(f'response success was {check_key}')
do_stuff(data['order'])
(You don't actually need to use get there; you could let data['success'] raise a KeyError if it's not present, and then separately check the value for falsiness and raise your own error. But I assume you probably want to handle a missing success the same way as false, and the error you want to raise probably isn't KeyError, in which case this is simpler.)
As a side note, you've already parsed the JSON at this point. What you have is a dict.
The fact that it originally came from parsing JSON doesn't make any difference; it's a plain old Python dict, with all the same methods, etc., as any other dict. So, it really isn't helpful to think of "how do I … with JSON …"; that just misleads you into forgetting about how easy dicts are to use.

How to raise Suds.WebFault from python code?

I am trying to raise a Suds.WebFault from python code. The __init__ method\constructor takes three arguments __init__(self, fault, document). The fault has fault.faultcode and fault.detail members\attributes\properties. I could not find out what class fault belongs to no matte what I tried. How do I raise Suds.WebFault type exception from python code?
Thanks in advance.
Not sure what exactly you are asking but you can throw a web fault using:
import suds
try:
client.service.Method(parameter)
except suds.WebFault, e:
print e
WebFault is defined in suds.__init__.py as:
class WebFault(Exception):
def __init__(self, fault, document):
if hasattr(fault, 'faultstring'):
Exception.__init__(self, u"Server raised fault: '%s'" %
fault.faultstring)
self.fault = fault
self.document = document
Therefore to raise a WebFault with a meaningful message you will need to pass an object as the first param with the message. Document can simply be None unless required.
import suds
class Fault(object):
faultstring = 'my error message'
raise suds.WebFault(Fault(), document=None)
WebFault is only meant to be actually raised when a <Fault> element is returned by the web server. So it's probably a bad idea to raise that yourself.
If you still would like to, I would start looking at the code where the framework raises it: https://github.com/unomena/suds/blob/4e90361e13fb3d74915eafc1f3a481400c798e0e/suds/bindings/binding.py#L182 - and work backwards from there.

Categories