flask nested post data - python

I'm trying to build in a way to handle a large number of posted options, e.g.
my_posted_data = {"item": "value", "item_options":{"a":2, "b":2} }
This would be coming from somewhere else in an api situation where I'm not in control of the environment and it is simulated for now. I'll post that through the requests library; and moving server-side, I try to get this from the route/view in my application. request.form gets read into a variable(form) which is passed to a task_manager queue. In the task I'll try to do:
options = form.get("item_options", None)
options always ends up as NoneType. Why is this not selecting the dict(like) value of {"a": 2, "b": 2}? I guess I'm doing it wrong, but what at this point I am unable to pinpoint.
Based on this scant picture I've provided, how do I post and and retrieve nested values with Flask request in the most effective way?
EDIT: I had to go a different way, using JSON data because I realized for the situation that is best, the form is more for user input from an html page, and this is something that requires a different approach.

By using Flask-WTF with Field Enclosures in WTForms, you can easily handle nested post data.

class ListField(Field):
def process_formdata(self, valuelist):
self.data = valuelist
class ItemsForm(Form):
a = StringField()
b = StringField()
class TestForm(FlaskForm):
item = StringField()
product = ListField(FormField(ItemsForm))
Since FormField add default prefix, the JSON will looks like
{
"item": "value",
"product": {
"product-a": "string",
"product-b": "string",
}
}

Related

Django Model Table Input

I am trying to insert a data table in my current project. I was wondering if it is possible to insert one whole table inside one particular Django Model. If I let the user fill this table, like in the example below:
How could I send that data into my model after POST?
As you stated in the comment under the question, your table has got fixed fields which should correspond to your already prepared database model - each table column is a separate model attribute. So the only thing you need to do is send each row input by the user as a separate entry in the JSON POST request which your code will convert into a model class instance and save it to the database. The JSON sent in the POST request could look something like this:
{
"table_entries": [
{
"sku": "22A",
"name": "2B",
"description": "2C",
"price": "2D",
"size": "2E"
},
{
"sku": "3A",
"name": "3B",
"description": "3C",
"price": "3D",
"size": "3E"
},
...
]
}
The functionality you're asking for is quite well covered in the Django REST Framework (DRF), a toolkit to build Web APIs. The general pipeline will be:
Receive the POST request in views.py
Use a model serializer that you need to prepare to convert the request data to a Python object
Save this object in the database
So the views could look something like that (based on examples from the DRF views docs):
from myapp.models import Table
from myapp.serializers import TableSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
class TableView(APIView):
"""
Create a new entry in the Table.
"""
def post(self, request, format=None):
table_entries = request.data["table_entries"]
for entry in table_entries:
# serialize each row
serializer = TableSerializer(data=entry)
if serializer.is_valid():
# you can store the objects in some list and if the're all fine
# use serializer.save() for each of them to save them into the db
else:
# At least one of the entries data is corrupted. You probably want
# to reject the whole request
# ...
This is to show you the general workflow, because the question is quite broad. To go further into details I strongly advise to have a look into the DRF tutorial, as it covers well the topic.

Django Rest Framework: Custom JSON Serializer

I am struggling to figure out how to write a custom serializer in DRF to parse a complex JSON data structure that is being passed to an endpoint. The JSON looks like this:
{
"name": "new",
"site": "US",
"data": {
"settings": [],
"meta": {
"meta1":{}
}
}
}
Here is my backend code:
# views.py
class SaveData(views.APIView):
def post(self, request, *args, **kwargs):
name = request.POST.get('name')
site = request.POST.get('site')
data = request.POST.get('data')
But data always returns None. On closer inspection into the request object, the incoming JSON looks like this:
# POST attribute of request object
'name' = 'new'
'site' = 'US'
'data[settings][0] = ''
'data[meta][meta1][] = ''
Basically it looks like the nested JSON objects associated with the data key are not getting properly serialized into Python dict and list objects. I've been looking for examples of custom DRF serializers but most of the ones I've found have been for serializing Django models, but I don't need to do that. The incoming data does not directly map to my models; instead I need to do some processing before I save any of the data.
Does anyone have any advice on a custom serializer that would properly convert the data JSON into proper Python objects? I started with this but it throws an exception (When a serializer is passed a 'data' keyword argument you must call '.is_valid()' before attempting to access the serialized '.data' representation.
You should either call '.is_valid()' first, or access '.initial_data' instead.). Here is my code with the custom serializer I created:
# serializers.py
class SaveDataSerializer(serializers.Serializer):
name = serializers.CharField()
site = serializers.CharField()
data = serializers.DictField()
def create(self, validated_data):
return dict(**validated_data)
Thanks.
I was able to solve my problem by converting the JS object to JSON when passing it to $.ajax, which then DRF was able to correctly parse into Python objects. Here's a snippet of the jQuery I'm using:
$.ajax({
url: '/api/endpoint',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(ajaxData),
done: function(data) {}
...
})
Note though that jQuery will default to x-www-urlencoded as the Content-Type if you don't specify anything. Since I'm using the JSON.stringify() method to explicitly convert the ajaxData object to a JSON string the Content-Type needed to be explicitly set as well.
I got the idea from this SO answer: Convert Object to JSON string
Of course, this doesn't really answer my original question, but this is another solution that is simpler and works like I wanted. By doing this I didn't have to create any custom serializers, but I did have to modify my API view like so:
class SaveData(views.APIView):
def post(self, request, *args, **kwargs):
name = request.data.get('name')
site = request.data.get('site')
data = request.data.get('data')
After doing this the expected object types are returned
>>> type(name)
str
>>> type(site)
str
>>> type(data)
dict

Django json serialize doesn't return Object

I'm trying to write an API view for my Django REST API that takes a Location object and serializers it "by hand" -- with json.dumps. Here's an example:
class LocationDetail(APIView):
def get(self, request, location_id, format=None):
l = Location.objects.get(id=location_id)
response_dict = {
"id": l.id,
"name" : l.name,
}
json_data = json.dumps(response_dict)
return Response(json_data)
this will, quite unsurprisingly, return a json object, such as this:
{"name": "Some Place", "id" : 1, ...}
This does not return a proper API response, according to https://www.hurl.it/.
But I need the API to return an Object. Here's the version where I use the built-in REST Framework's Serializer class:
serialized_location = LocationSerializer(l)
return Response(serialized_location.data)
This throws back the "proper" response, and does not cause an error in hurl.it:
Object {id: 1, name: "Some Place", …}
I'd like to figure out how to emulate the behavior of the REST serializer -- how do I get it to return an Object of json instead of just the json?
Here is the difference in images, one is clearly correct and the other just doesn't look like an API response:
REST framework serializer:
My custom json thing:
My custom json with "object" key addition:
They're weirdly different -- I want mine to be recognized as an API response too.
SOLUTION: If anyone else is interested, what you can do is just not json dump the object. Just return:
return Response(response_dict)
that simple. That will return an object appropriate for parsing.
Maybe you should just try this:
json_data = json.dumps(dict(object=response_dict))

Json response list with django

I want to use typeahead.js in my forms in Django 1.7. Furthermore I want to implement that using class based views.
As far as I understand the problem, I need to create a view that generates a JSON response for the ajax request coming from typeahead.js.
Is it a good idea to use django-braces for that?
What I have so far is this:
from braces.views import JSONResponseMixin
[...]
class TagList(JSONResponseMixin, ListView):
"""
List Tags
"""
model = Tag
context_object_name = 'tags'
def get(self, request, *args, **kwargs):
objs = self.object_list()
context_dict = {
"name": <do something with "obs" to get just the name fields>
"color": <do something with "obs" to get just the color fields>
}
return self.render_json_response(context_dict)
That's where I'm stuck at the moment. Am I on the right path? Or would it even be possible (and easy) to go without a third party app?
Serializing non-dictionary objects¶
In order to serialize objects other than dict you must set the safe parameter to False:
response = JsonResponse([1, 2, 3], safe=False)
https://docs.djangoproject.com/en/1.10/ref/request-response/#jsonresponse-objects
Edit:
But please be aware that this introduces a potentially serious CSRF vulnerability into your code [1] and IS NOT RECOMMENDED by the Django spec, hence it being called unsafe. If what you are returning requires authentication and you don't want a third party to be able to capture it then avoid at all costs.
In order to mitigate this vulnerability, you should wrap your list in a dictionary like so:
{'context': ['some', 'list', 'elements']}
[1] https://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx/
I usually use the python json library like this:
import json
from django.http import HttpResponse
class TagList(ListView):
...
context_dict = {
"name": <do something with "obs" to get just the name fields>
"color": <do something with "obs" to get just the color fields>
}
return HttpResponse(json.dumps({'context_dict': context_dict}),
content_type='application/json; charset=utf8')
But in the new Django 1.7 you have JsonResponse objects
Hope you find it useful.

Why won't this Pymongo update work?

I'm writing a small web app with the Pyramid framework (which is awesome) and I'm having trouble updating a document. I have no problems querying for a document with the same id- I just can't seem to update it. Here is the code I'm using:
for key, value in request.POST.iteritems():
if value:
to_insert[key] = value
if "_id" in request.POST:
try:
_id = ObjectId(request.matchdict['id'])
except InvalidId:
return Response("Error generating id")
request.db['concerts'].update(
{ '_id': _id },
{ "$set": to_insert },
upsert=False
)
If I do:
request.db['concerts'].find( {'_id' : _id }
I find the document that I wish to update, so it doesn't seem to be an issue of the id not being there. It just doesn't seem to commit anything to the database. The only issue I could see is that I update the entire document- I don't check the fields beforehand. Is this the proper way to go about this? I didn't see much in the documentation about updating an indiscriminate number of fields.

Categories