Django backend receives one less param than sent by frontend - django

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.

Related

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 to properly send data from APIClient in Django (rest_framework) for a POST request

I've encountered some strange behavior in a Django unittest. Specifically, I'm using the APIClient module from rest_framework.test to simulate GET/POST requests from a unittest.
The issue occurs when updating/creating a new object in the Django ORM via a POST request (see the code below):
def test_something(self):
data = {
"name": 'unit testing',
"data": {}
}
response = self.api_client.post(reverse('save_model'), data=data, format='json')
self.assertEqual(response.status_code, 200)
#api_view(['GET', 'POST'])
def save_model(request):
obj, created = MyModel.objects.update_or_create(
user_id=request.user,
**request.data
)
return JsonResponse({
'id': obj.id,
'name': obj.name,
'user_id': obj.user_id.id
})
The error i receive when running the test case:
Error binding parameter 1 - probably unsupported type
Based on other stack posts involving this error, i would assume i have a type issue for the second parameter (the data field). However when the same exact data is used to store an object in Django shell, it works every time. Additionally, when the request is made from the client (with the same data) the request succeeds every time.
If i print the data in the unittest request i get the following:
(, u'{}')
(, u'unit testing')
Model code is below:
class MyModel(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
user_id = models.ForeignKey(AUTH_USER_MODEL)
data = JSONField()
So i thought this might be a unicode issue. But once again, storing the object with unicode data in the shell works just fine. One subtle difference to note, the django unittest will create a new test db for the models, whereas running in the shell does not.
Im out of answers, so if someone can shed some light on what's occuring here, that would be amazing.

on data.put() i need to display to the user that the data has been successfully submitted or failure incase pof one

am using python and google app engine majorly on jinja2 templates
i could like when a user registers a new account, they get a popup indicating that their registration is successful of even any alert on the very interface before moving to the next registration step.
def post(self):
user = (str(users.get_current_user().email()))
userquery = Users.query(Users.email == user)
count = userquery.count()
if count == 0:
#test if user is admin or employee
qry = Users.query()
count = qry.count()
if count == 0:
privilage = 'admin'
db_put = Users(
f_name=self.request.get("f_name"),
l_name = self.request.get("l_name"),
org = self.request.get("org"),
email=users.get_current_user().email(),
privilage = privilage
)
db_put.put()
How are you calling this POST method? Are you sending a form there directly (use method 1) or is this being done with an AJAX call (use method 2)?
Method 1
You can redirect to a GET page where you render a template with a success or error message for Jinja to use. This would however involve a page change.
import webapp2
class MyHandler(webapp2.RequestHandler):
def get(self): # Let's assume /someurl is mapped to this handler.
template_values = {}
notification = self.request.get('notification')
if notification:
template_values['notification'] = notification
self.response.set_status(200)
self.response.headers['Content-Type'] = 'text/html; charset=utf-8'
# Need to get the template from jinja and set it as template variable.
self.response.out.write(template.render(template_values))
def post(self):
# Do all your stuff here.
self.redirect('/someurl?notification=Success')
Alternatively you can set the parameters directly on the request instead of passing them as URI parameters:
def post(self):
# Do all your stuff here.
self.redirect('/someurl, params={'notification': 'Success'})
Method 2
In this method you can send back a JSON response with a success or error message. The caller (whatever function in your javascript that submitted the request to the backend) can use that to render a butterbar message or other popup notification of your choosing:
import json
import webapp2
class MyHandler(webapp2.RequestHandler):
def post(self):
# Do all your stuff here.
self.response.set_status(200)
self.response.headers['Content-Type'] = 'application/json; charset=utf-8'
self.response.headers['Content-Disposition'] = 'attachment'
self.response.out.write(json.JsonEncoder(sort_keys=True).encode('Success'))
For the latter, make sure you think about cross-site scripting (XSS) vulnerabilities and perhaps add a JSON prefix.

AngularJS/Django Post Response Data

I'm using AngularJS for the front-end and Django for the backend of a web app I'm working on. Right now I'm working on logging in users and I'm having a strange problem. Heres the relevant Angular code:
app.factory('AuthService', ["$http", "$q", "Session", "URL", function($http, $q, Session, URL) {
return {
login: function(credentials) {
var deferred = $q.defer();
$http.post(URL.login, credentials)
.then(function(data, status, headers, config) {
data=data.data; //WHY DOES THIS WORK?
if (data.success == true) {
alert("logged in");
Session.create(credentials.username, data.api_key);
deferred.resolve();
}
else {
deferred.reject("Login failed!");
}
}, function(data, status, headers, config) {
deferred.reject("Login failed!");
});
return deferred.promise
},
And here is the corresponding Django view:
def login_user(request):
'''
Given a username and password, returns the users API key.
'''
if request.method == 'POST':
username = request.POST.get('username',None)
password = request.POST.get('password',None)
user = authenticate(username=username,password=password)
if user is not None:
api_key = ApiKey.objects.get(user=user)
response_data = {}
response_data["api_key"] = str(api_key).split(" ")[0]
response_data["success"] = True
return HttpResponse(json.dumps(response_data), content_type="application/json")
else:
return HttpResponse(json.dumps({"username":username,"success":False}),content_type="application/json")
return HttpResponseBadRequest()
When the user logs in a POST request is sent and handled by the above Django code. The response is then picked up by the AngularJS code above. As you can see the then() method in the Angular code takes the usual four parameters: data, status, config and headers. I expect to see data contain the dictionary output from the Django code, appropriately serialized into a JSON object.
However what happens is that the only parameter of the then() method which is not undefined is data, and this contains EVERYTHING; headers, data, status code,etc.
The line commented 'WHY DOES THIS WORK' fixes the problem, by accessing the data inside. However, I want to know why this is happening and if there is any way to avoid this. My best guess is that it has something to do with the way Django serializes a response but I'm not sure.
I'm using Django 1.6.5.
That is actually how Angular promises work according to the docs. Here is the relevant quote.
Since the returned value of calling the $http function is a promise,
you can also use the then method to register callbacks, and these
callbacks will receive a single argument – an object representing the
response. See the API signature and type info below for more details.
The emphasis was mine.

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.