POST request uploading to Database but not getting custom response - flask

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()

Related

Django Session Variables Don't Work In Stripe Webhook?

I am trying to use data saved in django session variables to run a function once the webhook has confirmed that 'checkout.session.completed' but I always get a key error. I am 100% sure the keys exist in the session variables.
Here is my webhook:
#csrf_exempt
def stripe_webhook(request):
# You can find your endpoint's secret in your webhook settings
endpoint_secret = 'secret'
payload = request.body
sig_header = request.META['HTTP_STRIPE_SIGNATURE']
event = None
try:
event = stripe.Webhook.construct_event(
payload, sig_header, endpoint_secret
)
except ValueError as e:
# Invalid payload
return HttpResponse(status=400)
except stripe.error.SignatureVerificationError as e:
# Invalid signature
return HttpResponse(status=400)
# Handle the checkout.session.completed event
if event['type'] == 'checkout.session.completed':
session = event['data']['object']
fulfull_order(session)
return HttpResponse(status=200)
Here is my fulfill order function:
def fulfull_order(session):
generator = PlanMaker(goal=request.session['goal'], gender=request.session['gender'])
/// send email code.
This line generator = PlanMaker(goal=request.session['goal'], gender=request.session['gender'])
Always gives a key error on request.session['goal'] The key definitely exists, it just seems it is inaccessible from the webhook view.
How to solve?
You should save the information you want to the metadata field when creating the checkout.Session.
def checkout(request):
session = stripe.checkout.Session.create(
payment_method_types=['card'],
line_items=[{
'price': 'price_key',
'quantity': 1,
}],
mode='payment',
success_url=request.build_absolute_uri(reverse('success_url')) + '?session_id={CHECKOUT_SESSION_ID}',
cancel_url=request.build_absolute_uri(reverse('cancel_url')),
metadata={'someKeyHere': 'your session variable data'}
)
return JsonResponse({
'session_id' : session.id,
'stripe_public_key' : settings.STRIPE_PUBLISHABLE_KEY
})
then you can access the information like session['metadata']['someKeyHere']
The webhook event is a separate request coming directly from Stripe that would not be related to any Django session and so this lack of session data would seem expected. As #Anthony suggests you can store this information in the Checkout Session metadata when you create the session. The metadata will be included in the webhook object.

Django - can not access session

Using Django REST Framework in development, I have the following (previously working) code example, where in one view I set session data, and in another view I use that data.
And like I said, this code used to work, but now for some reason the stored session data can not be accessed anymore.
View for setting session data
#api_view(["POST"])
#permission_classes((AllowAny, ))
def set_session_data(request):
session_data_dict = loads(request.body.decode('utf-8'))
if not isinstance(session_data_dict, dict):
return Response({"message": "Expected a JSON object with key-val pairs to be sent. Key-val pairs to be set to session. Received something else.", status: status.HTTP_400_BAD_REQUEST})
try:
for key, value in session_data_dict.items():
request.session[key] = value
response_data = {"status": rest_status.HTTP_200_OK}
except:
response_data = {"status": rest_status.HTTP_500_INTERNAL_SERVER_ERROR}
return Response(response_data)
View that accesses stored session data:
#api_view(["GET"])
#permission_classes((AllowAny, ))
def check_user_logged_in(request):
try:
data = {"login_token": request.session["login_token"]}
except KeyError:
data = {"login_token": ""}
return Response(data, status=rest_status.HTTP_200_OK)
I have tested a little, and I can access the data in the session in the set_session_data view, after it has been added, like so:
request.session['login_token']
But when I try the same in the check_user_logged_in view, I get a KeyError.
So I tried checking if the sessions for both views are the same session, by checking the value of request.COOKIES[settings.SESSION_COOKIE_NAME] in each view. But in both views that results in the following error:
KeyError: 'sessionid'
Now, I have not touched the session settings, so they are the default settings from django-admin startproject. ('django.contrib.sessions' in INSTALLED_APPS, 'django.contrib.sessions.middleware.SessionMiddleware' in MIDDLEWARE, and nothing added.)
Can anyone explain why this is happening?

django lazy translation sometimes appears in response.data in unit tests

I'm writing unit tests for my django api written with django-rest-framework, and I'm encountering seemingly inconsistent response data from calls that generate 400_BAD_REQUEST.
When I make a request that fails because it references an invalid primary key, repr(response.data) is a string that I can check for the substring "Invalid pk". But when I make a request that fails because it's trying to create a non-unique object, repr(response.data) contains {'name': [<django.utils.functional.__proxy__ object at 0x7f3ccdcb26d0>]} instead of the expected {'name': ['This field must be unique.']}. When I make an actual POST call to the real server, I get the expected 400 response with {'name': ['This field must be unique.']}.
Here's a code sample:
class GroupViewTests(APITestCase):
def test_post_existing_group(self):
"""
Use POST to update a group at /groups
"""
# create a group
# self.group_data returns a valid group dict
data = self.group_data(1)
url = reverse('group-list')
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
# create the same group again
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
# this fails
self.assertIn('must be unique', repr(response.data))
def test_create_group_with_nonexistent_user(self):
"""
Create a group with an invalid userid in it.
"""
url = reverse('group-list')
data = self.group_data(5)
data.update({'users': ['testnonexistentuserid']})
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
# this succeeds
self.assertIn('Invalid pk', repr(response.data))
I'm using only the default middleware.
From what I've found online, the __proxy__ object is a representation of a lazy translation, which can be rendered as a string by calling unicode(obj) or obj.__unicode__. And indeed, response.data['name'][0].__unicode__ does return the expected value, but calling unicode(response.data) instead of repr(response.data) still returns the object identified by memory address.
Can anyone explain what's going on here, and how to fix it? I can work around the issue for now, but I'd like to know the "real way" to fix the problem. Is it possibly a bug in django or django-rest-framework?
Use response.content instead of response.data.
response.content contains the json output just like your client will receive it. It's also useful if you have custom renderer that change the response because response.data is not rendered, but response.content yes.
E.g :
import json
# we have to parse the output since it no more a dict
data = json.loads(response.content)
# if `response.content` is a byte string, decode it
data = json.loads(response.content.decode('utf-8'))

Django backend receives one less param than sent by frontend

I have a small web app with AngularJS front-end and Django ReST in the back. There's a strange hitch going on when I make POST request to the web service: the browser console clearly shows 3 parameters being sent, but the backend logging reports only 2 params received. The result is that the server throws a code 500 error due to a bad database lookup.
Here's the code:
Client
var b = newQuesForm.username.value;
$http.post('/myapp/questions/new', {username:b,title:q.title,description:q.description}).
success(function(data, status, headers, config) {
$http.get('/myapp/questions').success(function(data){
$scope.questions = data;
q = null;
$scope.newQuesForm.$setPristine();
}).error(function(data, status, headers, config) {
console.log(headers+data);
});
}).
error(function(data, status, headers, config) {
console.log(headers+data);
});
Both my manual logging and the dev console show a string like:
{"username":"admin","description":"What's your name?","title":"question 1"}
Server
class CreateQuestionSerializer(serializers.Serializer):
author = UserSerializer(required=False)
title = serializers.CharField(max_length=150)
description = serializers.CharField(max_length=350)
def create(self, data):
q= Question()
d = data
q.title = d.get('title')
q.description = d.get("description")
q.author = User.objects.get(username=d.get('username'))
q.save()
return q
Server-side logging shows the username parameter never succeeds in making the trip, and thus I end up with code 500 and error message:
User matching query does not exist. (No user with id=none)
What's causing some of the data to get lost?
So it turns out the problem was really with the serialization of fields, as #nikhiln began to point out. I followed his lead to refactor the code, moving the create() method to api.py, rather than serializers.py, and stopped relying altogether on the client-side data for the user's identity, something that was a bit silly in the first place (passing User to a hidden input in the view, and then harvesting the username from there and passing it back to the server in the AJAX params). Here's the new code, that works perfectly:
class QuestionCreate(generics.CreateAPIView):
model = Question
serializer_class = CreateQuestionSerializer
def create(self, request,*args,**kwargs):
q= Question()
d = request.data
q.title = d.get('title')
q.description = d.get("description")
q.author = request.user
q.save()
if q.pk:
return Response({'id':q.pk,'author':q.author.username}, status=status.HTTP_201_CREATED)
return Response({'error':'record not created'}, status=status.HTTP_400_BAD_REQUEST)
So here, I do it the right way: pull the User from the request param directly in the backend.

The view companies.views.view didn't return an HttpResponse object

I am still learning and none of the other questions answer my question, why do I have to have an HTTP Response?
def view(request):
namesTickers = Company.objects.all().values('name', 'ticker')
names, tickers = [], []
for nameTicker in namesTickers:
names.append(nameTicker['name'])
tickers.append(nameTicker['ticker'])
nameTickerDict = dict(zip(names, tickers))
print nameTickerDict
if 'ticker' in request.POST and request.POST['ticker']:
q = request.POST['ticker']
context = {}
context['companies'] = json.dumps(nameTickerDict)
context['companyInfo'] = Company.objects.filter(ticker__icontains=q)
context['financial'] = Financials.objects.filter(ticker__icontains=q).order_by('-year')
return render( request, "companies/view.html",[context])
Because HTTP is a request/response mechanism. You get a request and you must respond to it. It doesn’t have to be a successful response, though. If you cannot respond meaningfully without a ticker, you may return an error page instead. Or, if you have a form where the user enters a ticker and submits that to your view, then you probably want to return the user back to the same form but with an error message. If that is the case, Django’s forms framework will help you.