I want to check if an object exists - if it doesn't exist I want to still continue the function and not return a 404 error. How can I achieve this?
def check(request):
if request.is_ajax():
# print('Working') #prints
id = request.POST.get('id')
post = Post.objects.get(hash=id)
obj = get_object_or_404(Post, post=post)
if obj:
# do stuff
else:
#do something else
The above code returns:
Not Found: /check/
[22/Jul/2018 01:15:03] "POST /check/ HTTP/1.1" 404 1729
Simply catch the Post.DoesNotExist exception and do something else in the exception handler:
def check(request):
if request.is_ajax():
id = request.POST.get('id')
try:
post = Post.objects.get(hash=id)
# do stuff
except Post.DoesNotExist:
# do something else
If you use this often then you could make it as function and save it in helpers.py
def get_or_none(model, *args, **kwargs):
try:
return model.objects.get(*args, **kwargs)
except model.DoesNotExist:
return None
and use it as
post = get_or_none(Post, hash=id)
if post:
#do something
else:
#do other thing
Related
I am getting a ValueError that the class below didn't return any httpresponse when i try to redirect to a template. the redirect is supposed to go to the stripe payment view.
here is an entire class that has the redirect call
class CheckoutView(View):
def get(self, *args, **kwargs):
form = forms.CheckoutForm()
context = {
'form': form
}
return render(self.request, "checkout.html", context)
def post(self, *args, **kwargs):
form = forms.CheckoutForm(self.request.POST or None)
try:
equipment_order = models.EquipmentOrder.objects.get(user=self.request.user, ordered=False)
if form.is_valid():
street_address = form.cleaned_data.get('street_address')
apartment_address = form.cleaned_data.get('apartment_address')
country = form.cleaned_data.get('country')
zip = form.cleaned_data.get('zip')
'''
TODO: add functionality to these fields
same_shipping_address = form.cleaned_data.get('same_shipping_address')
save_info = form.cleaned_data.get('save_info')
'''
payment_option = form.cleaned_data.get('payment_option')
billing_address = models.BillingAddress(
user=self.request.user,
street_address=street_address,
apartment_address=apartment_address,
country=country,
zip=zip
)
billing_address.save()
equipment_order.billing_address = billing_address
equipment_order.save()
if payment_option == 'S':
return redirect('create:payment', payment_option='stripe')
elif payment_option == 'P':
return redirect('create:payment', payment_option='paypal')
else:
messages.warning(self.request, "Invalid payment option")
return redirect('create:checkout')
except ObjectDoesNotExist:
messages.error(self.request, "You do not have an active order")
return redirect("create:order_summary")
1) Remove the try/except i think its better
2) I think you have a problem on 'payement_option' , maybe it doesnt give any value of expected , try to print it first to see what does it give
3) remove the ' or None ' from CheckoutForm
4) you can avoid using 'self' by importing form in that way :
from .forms import CheckoutForm
...
form = CheckoutForm(request.POST)
The above answer may work fine but as I tried your code it throws the same error as you described whenever you leave the form field empty or no payment method is selected.
After trying your code the best possible solution I figure out is this. I know this is not a perfect solution but it worked 😅
Suggestion: Try to move your else statement under if instead of nesting it after elif statement. And change your else to given below.
Old:
else:
messages.warning(self.request, "Invalid payment option select")
return redirect('core:checkout')
New:
else :
messages = 'Invalid payment option select'
return HttpResponse(messages)
Proof: Invalid payment option select
I am writing a generic wrapper around a REST API. I have several functions like the one below, responsible for retrieving a user from its email address. The part of interest is how the response is processed, based on a list of expected status codes (besides HTTP 200) and callbacks associated to each expected status code:
import requests
def get_user_from_email(email):
response = requests.get('http://example.com/api/v1/users/email:%s' % email)
# define callbacks
def return_as_json(response):
print('Found user with email [%s].' % email)
return response.json()
def user_with_email_does_not_exist(response):
print('Could not find any user with email [%s]. Returning `None`.' % email),
return None
expected_status_codes_and_callbacks = {
requests.codes.ok: return_as_json, # HTTP 200 == success
404: user_with_email_does_not_exist,
}
if response.status_code in expected_status_codes_and_callbacks:
callback = expected_status_codes_and_callbacks[response.status_code]
return callback(response)
else:
response.raise_for_status()
john_doe = get_user_from_email('john.doe#company.com')
print(john_doe is not None) # True
unregistered_user = get_user_from_email('unregistered.user#company.com')
print(unregistered_user is None) # True
The code above works well so I want to refactor and generalize the response processing part. I would love to end up with the following code:
#process_response({requests.codes.ok: return_as_json, 404: user_with_email_does_not_exist})
def get_user_from_email(email):
# define callbacks
def return_as_json(response):
print('Found user with email [%s].' % email)
return response.json()
def user_with_email_does_not_exist(response):
print('Could not find any user with email [%s]. Returning `None`.' % email),
return None
return requests.get('https://example.com/api/v1/users/email:%s' % email)
with the process_response decorator defined as:
import functools
def process_response(extra_response_codes_and_callbacks=None):
def actual_decorator(f):
#functools.wraps(f)
def wrapper(*args, **kwargs):
response = f(*args, **kwargs)
if response.status_code in expected_status_codes_and_callbacks:
action_to_perform = expected_status_codes_and_callbacks[response.status_code]
return action_to_perform(response)
else:
response.raise_for_status() # raise exception on unexpected status code
return wrapper
return actual_decorator
My problem is the decorator complains about not having access to return_as_json and user_with_email_does_not_exist because these callbacks are defined inside the wrapped function.
If I decide to move the callbacks outside of the wrapped function, for example at the same level as the decorator itself, then the callbacks have no access to the response and email variables inside the wrapped function.
# does not work either, as response and email are not visible from the callbacks
def return_as_json(response):
print('Found user with email [%s].' % email)
return response.json()
def user_with_email_does_not_exist(response):
print('Could not find any user with email [%s]. Returning `None`.' % email),
return None
#process_response({requests.codes.ok: return_as_json, 404: user_with_email_does_not_exist})
def get_user_from_email(email):
return requests.get('https://example.com/api/v1/users/email:%s' % email)
What is the right approach here? I find the decorator syntax very clean but I cannot figure out how to pass the required parts to it (either the callbacks themselves or their input arguments like response and email).
You could convert the decorator keys into strings, and then pull the inner functions from the outer function passed to the decorator via f.func_code.co_consts. Don't do it this way.
import functools, new
from types import CodeType
def decorator(callback_dict=None):
def actual_decorator(f):
code_dict = {c.co_name: c for c in f.func_code.co_consts if type(c) is CodeType}
#functools.wraps(f)
def wrapper(*args, **kwargs):
main_return = f(*args, **kwargs)
if main_return['callback'] in callback_dict:
callback_string = callback_dict[main_return['callback']]
callback = new.function(code_dict[callback_string], {})
return callback(main_return)
return wrapper
return actual_decorator
#decorator({'key_a': 'function_a'})
def main_function(callback):
def function_a(callback_object):
for k, v in callback_object.items():
if k != 'callback':
print '{}: {}'.format(k, v)
return {'callback': callback, 'key_1': 'value_1', 'key_2': 'value_2'}
main_function('key_a')
# key_1: value_1
# key_2: value_2
Can you use classes? The solution is immediate if you can use a class.
As mentioned in the comments for my other answer, here is an answer that uses classes and decorators. It's a bit counter-intuitive because get_user_from_email is declared as a class, but ends up as a function after decorating. It does have the desired syntax however, so that's a plus. Maybe this could be a starting point for a cleaner solution.
# dummy response object
from collections import namedtuple
Response = namedtuple('Response', 'data status_code error')
def callback_mapper(callback_map):
def actual_function(cls):
def wrapper(*args, **kwargs):
request = getattr(cls, 'request')
response = request(*args, **kwargs)
callback_name = callback_map.get(response.status_code)
if callback_name is not None:
callback_function = getattr(cls, callback_name)
return callback_function(response)
else:
return response.error
return wrapper
return actual_function
#callback_mapper({'200': 'json', '404': 'does_not_exist'})
class get_user_from_email:
#staticmethod
def json(response):
return 'json response: {}'.format(response.data)
#staticmethod
def does_not_exist(response):
return 'does not exist'
#staticmethod
def request(email):
response = Response('response data', '200', 'exception')
return response
print get_user_from_email('blah')
# json response: response data
Here's an approach that uses function member data on class methods in order to map the response function to the appropriate callback. This seems like the cleanest syntax to me, but still has a class turning into a function (which could be easily avoided if desired).
# dummy response object
from collections import namedtuple
Response = namedtuple('Response', 'data status_code error')
def callback(status_code):
def method(f):
f.status_code = status_code
return staticmethod(f)
return method
def request(f):
f.request = True
return staticmethod(f)
def callback_redirect(cls):
__callback_map = {}
for attribute_name in dir(cls):
attribute = getattr(cls, attribute_name)
status_code = getattr(attribute, 'status_code', '')
if status_code:
__callback_map[status_code] = attribute
if getattr(attribute, 'request', False):
__request = attribute
def call_wrapper(*args, **kwargs):
response = __request(*args, **kwargs)
callback = __callback_map.get(response.status_code)
if callback is not None:
return callback(response)
else:
return response.error
return call_wrapper
#callback_redirect
class get_user_from_email:
#callback('200')
def json(response):
return 'json response: {}'.format(response.data)
#callback('404')
def does_not_exist(response):
return 'does not exist'
#request
def request(email):
response = Response(email, '200', 'exception')
return response
print get_user_from_email('generic#email.com')
# json response: generic#email.com
You could pass the function parameters of the outer function to the handlers:
def return_as_json(response, email=None): # email param
print('Found user with email [%s].' % email)
return response.json()
#process_response({requests.codes.ok: return_as_json, 404: ...})
def get_user_from_email(email):
return requests.get('...: %s' % email)
# in decorator
# email param will be passed to return_as_json
return action_to_perform(response, *args, **kwargs)
Here's the view function:
def bar(request):
...
record = get_record_from_model(model, **kwargs)
...
return JsonResponse(data_to_response)
and below there is the function used in view function:
def get_record_from_model(model, **kwargs):
try:
return model.objects.get(**kwargs)
except model.DoesNotExist:
error_data = copy.copy(settings.ERROR["NOT_EXIST_ERR"])
return JsonResponse(error_data)
Can I return JsonResponse(error_data) to the client in get_record_from_model function when exception occur?
Something like raise Http404
The short answer is that you can't do it there directly because the calling function still has to do something with the return value from get_record_for_model. That said, I would recommend that you do something like the below, which sends data as well as a found/not found boolean back to the calling function:
def get_record_from_model(model, **kwargs):
try:
return model.objects.get(**kwargs), True
except model.DoesNotExist:
error_data = copy.copy(settings.ERROR["NOT_EXIST_ERR"])
return error_data, False
...
def bar(request):
...
data, found = get_record_from_model(model, **kwargs)
if not found:
return JsonResponse(data, status=404)
...
return JsonResponse(response_data)
Use django's built-in shortcut get_object_or_404
from django.shortcuts import get_object_or_404
def bar(request):
...
record = get_object_or_404(model, **kwargs)
...
return JsonResponse(data_to_response)
I'm facing this exception error and I'm puzzled by it, as this method worked in similar system, appreciate any help or pointers. Many Thanks!
Exception Value: The view Project.qna.views.add_vote didn't return an HttpResponse object.
def add_vote(request):
if request.method == "POST":
q_id = request.POST['vote_form_q_id']
a_id = request.POST['vote_form_a_id']
vote_value = request.POST['vote_form_value']
ok = False
vote_num = None
name = None
if q_id:
try:
question = Question.objects.get(id=q_id)
question.num_vote += int(vote_value)
question.save()
vote_num = question.num_vote
name = 'Question_'+str(q_id)
ok = True
except Question.DoesNotExist:
pass
elif a_id:
try:
answer = Answer.objects.get(id=a_id)
answer.num_vote += int(vote_value)
answer.save()
vote_num = answer.num_vote
name = 'Answer_'+str(a_id)
ok = True
except Answer.DoesNotExist:
pass
if ok and request.is_ajax:
result = simplejson.dumps({
"vote_num": vote_num,
}, cls=LazyEncoder)
response = HttpResponse(result, mimetype='application/javascript')
response.set_cookie(name, datetime.now)
return response
Fix your indention please, also you seem to have a lot of workarounds that could be simplified.
Every django view should return a HttpResponse object, you seem to have a lot of places where this would not be the case. To narrow down your problem change every pass to a print statement to see where your code actually fails. It would be quite helpful if you could present your POST data.
Well it's hard to tell without seeing what kind of request you are making to the view. But are you sending a POST request? Because you don't handle GET requests in any way. Also the indentation is wrong. But that might just be cutting and pasting gone awry.
This is untested, but it's a cleaner and more robust design, which I believe fits in with your logic and highlights the points where returning an HttpResponse is necessary:
def add_vote(request):
if not (request.method == 'POST' and request.is_ajax):
return # Some suitable response here
try:
vote_value = int(request.POST.get('vote_form_value',''))
except ValueError as e:
pass # Some suitable response here
def saveobj(model, key, val): # helper function to reduce code repetition
item = model.objects.get(id=key)
item.num_vote += val
item.save()
return item.num_vote, '%s_%s' % (model.__class__.__name__, key)
for model, key in [(Question, 'vote_form_q_id'), (Answer, 'vote_form_a_id')]):
try:
new_vote_value, name = saveobj(model, request.POST[key], vote_value)
break
except (KeyError, ObjectDoesNotExist) as e:
continue # or error out
else:
pass # neither question or answer found - so suitable response here
# return ajax response here....
Hay All, I've got a simple context processor which looks within a session and if a 'user' key exists. If it does i want to return it to the template.
Here's my context Processor
def get_user_details(request):
user = request.session['user']
data = {
'user':user
}
return data
and here is a sample view
def render_home(request):
return render_to_response("home", context_instance=RequestContext(request))
If the session['user'] doesn't exists, i want it to silently fail, or return False or Null.
Because the key doesnt exist within the session, i get a KeyError.
Any idea's how to fix this?
You can get a default value like None this way: request.session.get('user', None). Just like in normal Python dicts.
user = request.session.get('user', None)
or,
user = None
if 'user' in request.session:
user = request.session['user']
def get_user_details(request):
try:
user = request.session['user']
except KeyError:
return
data = {
'user':user
}
return data
Or if you want to catch it further away, do this instead:
def render_home(request):
try:
return render_to_response("home", context_instance=RequestContext(request))
except KeyError:
return