I'm writing a challenge in Python and I would like to know what is the right approach to return None case my function throws an Exception.
def get(email):
try:
customer = get_customer(email)
return customer
except TypeError as e:
logging.error(f'Customer {email} not found', e)
Should I do something like customer = None and return customer after except block?
Related
I have no idea to implement to handle the class variable exception. I am trying to create python module which has dependency with mongodb.
class HrmLookUps(DBMixin):
db_handle = DBMixin.get_db_handle(DBMixin, MONGO_STORE_DICT.get("DB_NAME"),
MONGO_STORE_DICT.get("DB_HOST"),
MONGO_STORE_DICT.get("DB_PORT"),
MONGO_STORE_DICT.get("USERNAME"),
MONGO_STORE_DICT.get("PASSWORD"))
#classmethod
def get_gender(cls):
collection_name = COLLECTION_NAME_DICT.get("COLLECTION_GENDER")
# collection_name = "departments"
gender_record = cls.get_from_db(cls, cls.db_handle, collection_name)
if gender_record[collection_name]:
return gender_record
else:
raise KeyError("Collection Name Invalid!")
I have multiple get method like above get_gender(). I am handling each method raised Keyerror if its data empty.
My question is,
Is this proper way to handle exception of methods?
If my class variable has some issues ex.database credential wrong how can I handle that?
Currently your else clause isn't really doing anything. Your if statement would already throw an error if collection_name wasn't a key in gender_record. So unless you want to raise KeyError in the else clause when collection_name is in gender_record but its value happens to be 0 or False or None.
I think what you are trying to do is something closer to these examples:
#classmethod
def get_gender(cls):
collection_name = COLLECTION_NAME_DICT.get("COLLECTION_GENDER")
# collection_name = "departments"
gender_record = cls.get_from_db(cls, cls.db_handle, collection_name)
if collection_name in gender_record:
return gender_record
else:
raise KeyError("Collection Name Invalid!")
or this maybe
#classmethod
def get_gender(cls):
collection_name = COLLECTION_NAME_DICT.get("COLLECTION_GENDER")
# collection_name = "departments"
gender_record = cls.get_from_db(cls, cls.db_handle, collection_name)
try:
gender_record[collection_name]
return gender_record
else KeyError as err:
raise KeyError("Collection Name Invalid!") from err
Handling exceptions is pretty much the same in any situation. so for the credentials it would be something like this:
try:
db_handle = DBMixin.get_db_handle(DBMixin, MONGO_STORE_DICT.get("DB_NAME"),
MONGO_STORE_DICT.get("DB_HOST"),
MONGO_STORE_DICT.get("DB_PORT"),
MONGO_STORE_DICT.get("USERNAME"),
MONGO_STORE_DICT.get("PASSWORD"))
except <SomeCredentialErrorHere>:
# whatever you put here is what gets executed if the error is raised.
I have a view that takes 2 arguments :course_slug and chapter_slug and i want to check if the given course and chapter exist in the database so what's the best way to do that :
def Chapter_Detail(request,course_slug,chapter_slug):
try:
course = Course.objects.get(slug=course_slug)
except Course.DoesNotExist:
raise Http404("course does not exist")
try:
chapter = Chapter.objects.get(slug=chapter_slug)
except Chapter.DoesNotExist:
raise Http404("chapter does not exist")
'''
continue view logic
'''
context = {
'chapter':chapter,
'course' :course,
}
return render(request,'courses/chapter-detail.html',context)
or:
def Chapter_Detail(request,course_slug,chapter_slug):
try:
course = Course.objects.get(slug=course_slug)
chapter = Chapter.objects.get(slug=chapter_slug)
'''
continue view logic
'''
except Course.DoesNotExist:
raise Http404("course does not exist")
except Chapter.DoesNotExist:
raise Http404("chapter does not exist")
context = {
'chapter':chapter,
'course' :course,
}
return render(request,'courses/chapter-detail.html',context)
or:
def Chapter_Detail(request,course_slug,chapter_slug):
try:
course = Course.objects.get(slug=course_slug)
chapter = Chapter.objects.get(slug=chapter_slug)
except Course.DoesNotExist:
raise Http404("course does not exist")
except Chapter.DoesNotExist:
raise Http404("chapter does not exist")
else :
'''
continue view logic
'''
context = {
'chapter':chapter,
'course' :course,
}
return render(request,'courses/chapter-detail.html',context)
or there is a better way
continue view logic means that we are going to work with the course and chapter objects
As mentioned by Aiden, you can use the shortcut specifically made for this.
from django.shortcuts import get_object_or_404
def Chapter_Detail(request,course_slug,chapter_slug):
course = get_object_or_404(Course, slug=course_slug)
chapter = get_object_or_404(Chapter, slug=chapter_slug)
'''
continue view logic
'''
context = {
'chapter':chapter,
'course' :course,
}
return render(request,'courses/chapter-detail.html',context)
Your three variants will also work of course. The else-block in the third example is strictly not necessary, since either error will get raised or code will proceed.
Need some help with the if statements and Vaidation errors.
Right now I have this function:
def validate(self, validated_data):
if self.partial:
validated_data = self.fill_data(self.instance, validated_data)
if not validated_data['brand'].sunny_account.first():
raise ValidationError('This brand not Sunny')
validated_data['calculate'] = Account.NEW
return validated_data
Need to add another if statement:
if not validated_data['brand'].moon_account.first():
raise ValidationError('This brand not Moon')
If I add another if not statement in this function it's not going to the second one if not and raising the first Validation error.
I would like that this function checking all if's and raising Validation error for the each case.
From what I understand, you want both the Moon and Sunny errors to be raised. However, this cannot happen: if the first is raised, then the second will never be reached. If the first is not raised, only then can the second be raised. But both can't be raised at the same time.
One solution, do it in a loop:
def validate(self, validated_data):
if self.partial:
validated_data = self.fill_data(self.instance, validated_data)
validations = [
(
validated_data['brand'].sunny_account.first(),
'This brand does not have Sunny enabled'
),
(
validated_data['brand'].moon_account.first(),
'This brand does not have Moon enabled'
),
]
# Validate
err_msg = ""
for cond, msg in validations:
if not cond:
# Has error, add the error message
err_msg = err_msg + msg
if err_msg:
# Error message is not empty --> there is an error
raise ValidationError(err_msg)
validated_data['calculate'] = Account.NEW
return validated_data
It is unusual to want to do this, and handling your exceptions elsewhere might be tricky, but you could raise an Exception of Exceptions something like this:
def my_test(thing):
errors = []
if thing != 1:
errors.append(ValidationError('thing 1'))
if thing != 2:
errors.append(ValidationError('thing 2'))
if errors:
Raise(ValidationError(errors))
You can't raise two exceptions at once, but you can define your own Exception subclass that incorporates arbitrary data. For example, you could store a list of multiple error messages:
class ValidationError(Exception):
def __init__(self):
super().__init__()
self._why: list[str] = []
def __bool__(self) -> bool:
return bool(self._why)
def __str__(self) -> str:
return "\n".join(self._why)
def add(self, why: str) -> None:
self._why.append(why)
and then accumulate multiple messages before deciding to raise:
err = ValidationError()
if not validated_data['brand'].sunny_account.first():
err.add('This brand does not have Sunny enabled')
if not validated_data['brand'].moon_account.first():
err.add('This brand does not have Moon enabled')
if err:
raise err
I have a feeling that this sequence might be written shorter:
dim = Dimension.objects.get(pk=rows['pk'])
try:
dim.name = rows['name']
except KeyError:
pass
try:
dim.external_flg = rows['external_flg']
except:
pass
try:
dim.ext_owner = rows['ext_owner']
except KeyError:
pass
try:
dim.ext_table_name = rows['ext_table_name']
except KeyError:
pass
try:
dim.ext_start_date_column_name = rows['ext_start_date_column_name']
except KeyError:
pass
try:
dim.ext_end_date_column_name = rows['ext_end_date_column_name']
except KeyError:
pass
I've never had any experience in Python code optimization and working with exceptions but I'd be glad to have an alternative examples how it could be shortened.
Thank you!
Use the dict.get() method:
dim.name = rows.get('name', dim.name)
Or, for more DRY solution, put all assignments into the loop:
for field_name in ('name', 'external_flg', 'ext_owner', 'ext_table_name',
'ext_start_date_column_name', 'ext_end_date_column_name'):
if field_name in rows:
setattr(dim, field_name, rows[field_name])
To handle the list data you can make the similar loop:
for i, field_name in enumerate(('name', 'external_flg', 'ext_owner',
'ext_table_name', 'ext_start_date_column_name',
'ext_end_date_column_name')):
if i < len[rows]:
setattr(dim, field_name, rows[i])
Something like this should work — this answer includes try/catch block to catch KeyError :-)
attributes = ['name', 'external_flg', 'ext_owner', 'ext_table_name',
'ext_start_date_column_name', 'ext_end_date_column_name']
dim = Dimension.objects.get(pk=rows['pk'])
for attr in attributes:
try:
setattr(dim, attr, rows[attr])
except KeyError:
pass
I have this long list of try except statement:
try:
uri = entry_obj['media$group']['media$content'][0]['url']
except (KeyError, IndexError):
uri = None
try:
position = entry_obj['yt$position']['$t']
except KeyError:
position = None
try:
description = entry_obj['content']['$t']
except KeyError:
description = None
try:
seconds = entry_obj['media$group']['yt$duration']['seconds']
except KeyError:
seconds = None
try:
thumbnails = entry_obj['media$group']['media$thumbnail']
except KeyError:
thumbnails = None
Is there a more concise way to write this?
If you tire of figuring out what to use for default values in get() calls, just write a helper function:
def resolve(root, *keys):
for key in keys:
try:
root = root[key]
except (KeyError, IndexError):
return None
return root
Then you just write, e.g.:
uri = resolve(entry_obj, 'media$group', 'media$content', 0, 'url')
To simplify the calls a little, you might beef up the helper function to take a single string for the keys and split on spaces; that way you don't have to type so many quotes, and we can also add a default value argument:
def resolve(root, keys, default=None):
for key in keys.split():
try:
root = root[key]
except (TypeError, KeyError):
try:
root = root[int(key)]
except (IndexError, ValueError, KeyError):
return default
uri = resolve(entry_obj, 'media$group media$content 0 url', '')
I thought of another good way to do this, not sure how it compares to kindall's method. We first define a method property:
def res(self, property):
try:
return property()
except (KeyError, IndexError):
return None
Then replace the try-except statements with:
url = res(lambda: entry_obj['media$group']['media$content'][0]['url'])
position = res(lambda: entry_obj['yt$position']['$t'])
description = res(lambda: entry_obj['content']['$t'])
duration = res(lambda: entry_obj['media$group']['yt$duration']['seconds'])
thumbnails = res(lambda: entry_obj['media$group']['media$thumbnail'])
Use the get method of dictionaries instead:
position = entry_object.get('yt$position').get('$t')
get will handle the case of a key not existing for you, and give you a (changable) fallback value instead in that case. You'll still need to handle the first IndexError manually, but all the ones that are just except KeyError: will disappear.