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.
Related
I'm using Django==1.10.5 and djangorestframework==3.5.3.
I have a few ModelViewSets that handle JSON API requests properly. Now, I'd like to use TemplateRenderer to add HTML rendering to those same ModelViewSets. I started with the list endpoint, and created a simple template that lists the available objects. I implemented the get_template_names to return the template I created.
Accessing that endpoint through the browser works fine when there are no objects to list, so everything related to setting up HTML renderers alongside APIs seems to work.However, when tere are objects to return the same endpoint fails with the following error:
ValueError: dictionary update sequence element #0 has length XX; 2 is required
Where XX is the number of attributes the object has.
This documentation section suggests the view function should act slightly differently when returning an HTML Response object, but I assume this is done by DRF's builtin views when necessary, so I don't think that's the issue.
This stackoverflow Q/A also looks relevant but I'm not quite sure it's the right solution to my problem.
How can I make my ModelViewSets work with both HTML and JSON renderers?
Thanks!
DRF has a brief explanation of how to do this in their documentation.
I think you'd do something like this...
On the client side, tell your endpoint what type of response you want:
fetch(yourAPIUrl, {
headers: {
'Accept': 'application/json'
// or 'Accept': 'text/html'
}
})
In your view, just check that and act accordingly:
class FlexibleAPIView(APIView):
"""
API view that can render either JSON or HTML.
"""
renderer_classes = [TemplateHTMLRenderer, JSONRenderer]
def get(self, request, *args, **kwargs):
queryset = Things.objects.all()
# If client wants HTML, give them HTML.
if request.accepted_renderer.format == 'html':
return Response({'things': queryset}, template_name='example.html')
# Otherwise, the client likely wants JSON so serialize the data.
serializer = ThingSerializer(instance=queryset)
data = serializer.data
return Response(data)
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
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))
I'm building an API with django-rest-framework and I started using django-rest-swagger for documentation.
I have a nested serializer with some read_only fields, like this:
# this is the nested serializer
class Nested(serializers.Serializer):
normal_field = serializers.CharField(help_text="normal")
readonly_field = serializers.CharField(read_only=True,
help_text="readonly")
# this is the parent one
class Parent(serializers.Serializer):
nested_field = Nested()
In the generated docs, nested serializers in the Parameters part of the page are rendered with field data type and no hint is given about its content, they are just like other fields.
Now you can see the problem there, as I would like to inform the user that there is a readonly field that should not be sent as part of the nested data but I can not see a way of doing so.
The ideal would be having a model description in Data Type column, just like the Response Class section.
Is there any proper way of doing so?
1. of everything please use drf-yasg for documentation .
2. you can find its implementation in one of my repository Kirpi and learn how to use that.
3. if you in 3. ; have question,let me know.
Try to use drf_yasg instead, Swagger will generate the documentation for APIs, but it's not absolutely right!
If you want to correct Swagger documentation, you can do it. You will need to use swagger_auto_schema decorator. Below is the sample code:
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
class ProductSuspendView(CreateAPIView):
#swagger_auto_schema(
tags=['dashboard'],
request_body=openapi.Schema(
type=openapi.TYPE_OBJECT,
properties={
'id': openapi.Schema(
type=openapi.TYPE_INTEGER,
description='Id',
),
'suspend_kinds': openapi.Schema(
type=openapi.TYPE_ARRAY,
items=openapi.Items(type=openapi.TYPE_INTEGER),
description='Array suspend (Inappropriate image: 1, Insufficient information: 2, Bad language: 3) (suspend_kinds=[1,2])'
),
}
),
responses={
status.HTTP_200_OK: SuccessResponseSerializer,
status.HTTP_400_BAD_REQUEST: ErrorResponseSerializer
}
)
def post(self, request, *args, **kwargs):
"""
Suspend a product.
"""
...
if success:
return Response({'success': True}, status.HTTP_200_OK)
return Response({'success': False}, status.HTTP_400_BAD_REQUEST)
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",
}
}