How to execute code in Django if an error occurs? - django

One of the views that I call works with two database tables. It attempts to find an object in the first table. If the object isn't found, I get a Server Error (500). I'm not sure what the code would look like, but I want to insert some code into the view that will execute if the Server Error occurs so that I can tell it to try to find the object in the second table.
Current Code:
#csrf_exempt
#login_required
def addEvent(request):
event_id = request.POST['event_id']
user = request.POST['profile']
event = Event.objects.get(event_id = event_id)
if event.DoesNotExist:
event = customEvent.objects.get(event_id = event_id)
user = Profile.objects.get(id = user)
user.eventList.add(event)
return HttpResponse(status = 200)

Most likely you are getting 500 error because you are not finding a record in the first table. To fix that, you just have to catch the DoesNotExist Exception (Mentioned here):
try:
obj = FooModel.objects.get(...)
except FooModel.DoesNotExist:
try:
obj = OtherModel.objects.get(...)
except OtherModel.DoesNotExist:
raise Http404
or you can simplify this by using a shortcut:
try:
obj = FooModel.objects.get(...)
except FooModel.DoesNotExist:
obj = get_object_or_404(OtherModel, ...)

Related

database not updated by a task running in celery beat

Hello I have a celery task that is suppose to run every 1 hour to fetch a key and it runs and even acts like it has updates the database but it does not update in reality
#app.task
def refresh_token():
r = requests.get(AUTH_URL, auth=HTTPBasicAuth(CONSUMER_KEY, CONSUMER_SECRET))
obj = json.loads(r.text)
obj['expires_in'] = int(obj['expires_in'])
try:
mpesa_token = MpesaAccessToken.objects.get(id=1)
mpesa_token.access_token = obj['access_token']
mpesa_token.save()
print(obj)
print(mpesa_token.access_token)
print("saved")
except:
print(obj)
mpesa_token = MpesaAccessToken.objects.create(**obj)
return 1
the last thee prints all shows in the logs but checking the admin panel, the values are not updated however when I use a view and make a request then call the function, the database get updated, could anyone know what is going on
You need to add transaction.atomic() to your code. Like this:
from django.db import transaction
#app.task
def refresh_token():
with transaction.atomic():
r = requests.get(AUTH_URL, auth=HTTPBasicAuth(CONSUMER_KEY, CONSUMER_SECRET))
obj = json.loads(r.text)
obj['expires_in'] = int(obj['expires_in'])
try:
mpesa_token = MpesaAccessToken.objects.get(id=1)
mpesa_token.access_token = obj['access_token']
mpesa_token.save()
print(obj)
print(mpesa_token.access_token)
print("saved")
except:
print(obj)
mpesa_token = MpesaAccessToken.objects.create(**obj)
return 1

POST request uploading to Database but not getting custom response

So as the title suggest, I am able to send a POST request, and see in my postgresql database (through the pgAdmin client) that the data is being submitted and stored, however I run into a number of errors in the response output. It may have to do with the Authorization, as I have never worked with authorizing before. Both errors produce responses with 500 errors, contrary to what I would like.
The POST request is as follows:
def create():
"""
Create Student Function
"""
req_data = request.get_json()
try:
data = student_schema.load(req_data)
except ValidationError as err:
error = err.messages
return custom_response(error, 400)
#check if student exists in db
student_in_db = Student.get_user_by_email(data.get('email'))
if student_in_db:
message = {'error': 'Student already exists, please supply another email address'}
return custom_response(message, 400)
student = Student(data)
student.save()
ser_data = student_schema.dump(student).data
token = Auth.generate_token(ser_data.get('id'))
return custom_response({'jwt_token': token}, 201)
The custom_response is as so:
def custom_response(res, status_code):
"""
Custom Response Function
"""
return Response(
mimetype="application/json",
response=json.dumps(res),
status=status_code
)
Error 1
The new entry is stored, however the response to the server is still a 500 error. The error output is an attribute error pointing towards
ser_data = student_schema.dump(student).data
which is apparently a dict object, thus has no attribute data.
Error 2
Emails are declared unique in my database, thus when trying to create another user with the same email, I get a unique constraint failure, which leads to a 500 response, even though I have the built in function of checking if the student is already in the database.
With flask_sqlalchemy, I expect to see
db.session.add(student)
db.session.commit()
unless you're doing that in
student.save()

How I can Do a transaction rollback and commit at the time of import

The code given below is the program to import data into my django model,
actually what this program does is it read the file and it import each line from the file to the django model, here may be 1000 files will be there,there will be some error in some file and the program will catch the exception if the file have any type of error and log it, But what i need is to rollback the entire transaction from one file which throws an error.I want to do this in postgres
def impSecOrdr_File(self,path):
lines=1
try:
fromFile=open(path)
for eachLine in fromFile:
obj = SecOrdr_File()
if lines!=1:
fieldsInline = eachLine.split(",")
obj.dates = dates
obj.column1 = fieldsInline[0].strip()
obj.column2 = fieldsInline[1].strip()
obj.column3 = float(fieldsInline[2].strip())
obj.column4 = float(fieldsInline[3].strip())
obj.save()
lines+=1
except BaseException as e:
transaction.rollback()
logging.info('\tError in importing %s line %d : %s' % (path, lines, e.__str__()))
else:
try:
logging.info("\tImported %s, %d lines" % (path, lines))
except Exception as e:
logging.info(e)
Is there any way to do that. I went through the django transaction document and I tried Some of these but it is not working.. Can any one help me please
you can save the obj into a list use a try statement if all the transaction passes the save the transactions
lis =[]
try:
#your code
lis.append(obj) #instead of obj.save()
except:
#catch the error and transaction get cancelled
else:
k=len(lis)
for i in range(k):
lis[i].save()
I will suggest you to use postgresSQL instead of sqlite

SQLAlchemy query not retrieving committed data in alternate session

I have a problem where I insert a database item using a SQLAlchemy / Tastypie REST interface, but the item is missing when subsequently get the list of items. It shows up only after I get the list of items a second time.
I am using SQLAlchemy with Tastypie/Django running on Apache via mod_wsgi. I use a singleton Database Manager class to hold my engine and declarative_base, and with Tastypie, a separate class to get the session and make sure I roll-back if there is a problem with the commit. As in the update below, the problem occurs when I don't close my session after inserting. Why is this necessary?
My original code was like this:
Session = scoped_session(sessionmaker(autoflush=True))
# Singleton Database Manager class for managing session
class DatabaseManager():
engine = None
base = None
def ready(self):
host='mysql+mysqldb://etc...'
if self.engine and self.base:
return True
else:
try:
self.engine = create_engine(host, pool_recycle=3600)
self.base = declarative_base(bind=self.engine)
return True
except:
return False
def getSession(self):
if self.ready():
session = Session()
session.configure(bind=self.engine)
return session
else:
return None
DM = DatabaseManager()
# A session class I use with Tastypie to ensure the session is destroyed at the
# end of the transaction, because Tastypie creates singleton Resources used for
# all threads
class MySession:
def __init__(self):
self.s = DM.getSession()
def safeCommit(self):
try:
self.s.commit()
except:
self.s.rollback()
raise
def __del__(self):
try:
self.s.commit()
except:
self.s.rollback()
raise
# ... Then ... when I get requests through Apache/mod_wsgi/Django/Tastypie
# First Request
obj_create():
db = MySession()
print db.s.query(DBClass).count() # returns 4
newItem = DBClass()
db.s.add(newItem)
db.s.safeCommit()
print db.s.query(DBClass).count() # returns 5
# Second Request after First Request returns
obj_get_list():
db = MySession()
print db.s.query(DBClass).count() # returns 4 ... should be 5
# Third Request is okay
obj_get_list():
db = MySession()
print db.s.query(DBClass).count() # returns 5
UPDATE
After further digging, it appears that the problem is my session needed to be closed after creating. Perhaps because Tastypie's object_create() adds the SQLAlchemy object to it's bundle, and I don't know what happens after it leaves the function's scope:
obj_create():
db = MySession()
newItem = DBClass()
db.s.add(newItem)
db.s.safeCommit()
copiedObj = copyObj(newItem) # copy SQLAlchemy record into non-sa object (see below)
db.s.close()
return copiedObj
If someone cares to explain this in an answer, I can close the question. Also, for those who are curious, I copy my object out of SQLAlchemy like this:
class Struct:
def __init__(self, **entries):
self.__dict__.update(entries)
class MyTastypieResource(Resource):
...
def copyObject(self, object):
base = {}
# self._meta is part of my tastypie resource
for p in class_mapper(self._meta.object_class).iterate_properties:
if p.key not in base and p.key not in self._meta.excludes:
base[p.key] = getattr(object,p.key)
return Struct(**base)
The problem was resolved by closing my session. The update in the answer didn't solve the problem fully - I ended up adding a middleware class to close the session at the end of a transaction. This ensured everything was written to the database. The middleware looks a bit like this:
class SQLAlchemySessionMiddleWare(object):
def process_response(self, request, response):
try:
session = MyDatabaseManger.getSession()
session.commit()
session.close()
except Exception, err:
pass
return response
def process_exception(self, request, exception):
try:
session = MyDatabaseManger.getSession()
session.rollback()
session.close()
except Exception, err:
pass

Django: get the first object from a filter query or create

In Django, queryset provides a method called get_or_create that either returns an objects or creates an object.
However, like the get method, get_or_create can throw an exception if the query returns multiple objects.
Is there an method to do this elegantly:
objects = Model.manager.filter(params)
if len(objects) == 0:
obj = Model.objects.create(params)
else:
obj = objects[0]
get_or_create() is just a convenience function so there's nothing wrong with writing your own, like pavid has shown or
result = Model.objects.filter(field__lookup=value)[0]
if not result:
result = Model.objects.create(...)
return result
EDIT
As suggested, changed the [:1] slice (which returns a single-entry list) after the filter to [0] (which returns the actual object). The problem with this is it will raise an exception if there is not match to the query.
This will also raise a simliar exception:
Model.objects.filter(field_lookup=value).latest()
Looking at the question again, I'm not sure whether the original poster is looking to return multiple objects/rows, or just a way to get around raising an exception when retrieving a single object/row.
Here's another option?
results = Model.objects.filter(...)
if results.exists():
return results
else:
return Model.objects.create(...)
and another:
result = None
try:
result = Model.objects.get(...)
except Model.DoesNotExist:
result = Model.objects.create(...)
There's nothing wrong with raising & catching exceptions!
From django 1.6 there is a convenience method first() that returns the first result in a filter query, or None.
obj = Model.manager.filter(params).first()
if obj is None:
obj = Model.objects.create(params)
Here's a manager method that will allow you to extend this function elegantly
class FilterOrCreateManager(models.Manager):
"""Adds filter_or_create method to objects
"""
def filter_or_create(self, **kwargs):
try:
created = False
obj = self.filter(**kwargs).first()
if obj is None:
obj = self.create(**kwargs)
created = True
return (obj,created)
except Exception as e:
print(e)
Then ensure you add the manager to whichever model(s) you want to use this on:
class MyObj(models.Model):
objects = FilterOrCreateManager()
After that, you will be able to use it as you would get_or_create:
obj_instance, created = MyObj.filter_or_create(somearg='some value')
I just came across this question and wanted to contribute because no one has suggested actually catching the IndexError.
try:
obj = Model.objects.filter(params)[0]
except IndexError:
obj = Model.objects.create(params)
Do it in one line:
obj = Model.objects.filter(params).first() or Model.objects.create(params)
You can try this:
result = Model.objects.filter(field__lookup=value)[0]
if not result:
result = Model.objects.create(...)
return result
This works for me:
In your view, call something like this:
obj = Category.objects.get_or_create_category(form.cleaned_data.get('name'))
In your Model manager, create a function like this:
class CategoryManager(models.Manager):
def get_or_create_category(self, query):
try:
result = Category.objects.filter(name = query)[0]
except:
result = Category.objects.create(name = query)
return result
The logic is simple. First, try to retrieve the first Category object who's name matches the query string (which is provided by the form). If the retrieval fails (because it doesn't exist), create a new Category with the string as its name. Return the result for use in your view.