Django Rest Framework parser does not accept string as valid JSON - python

I'm sending some data to Django Rest Framework backend with Content-Type set as application/json.
If I try to send any JSON that is shaped like a typical nested key-value object, everything works fine.
But if I want to send a simple string like test, I get a 400 Bad request due to malformed JSON : JSON parse error - Expecting value: line 1 column 1 (char 0).
But aren't strings valid JSON values according to RFC 8259 ?
I don't understand from the official DRF documentation if this is a normal feature for Django JSON Parser or if I'm doing something wrong somewhere ?
The class-based view that I'm using is ListCreateAPIView:
class ObjectList(ListCreateAPIView):
"""
View dedicated to GET a list of instances or POST a new one
"""
serializer_class = MySerializer
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
def get_queryset(self):
# Filter objects based on user identity (token based)
queryset = MyObject.objects.filter(user=self.request.user)
return queryset
def post(self, request, format=None):
transect_data = request.data # Returns 400 Bad request
The piece of code that send data to the server (JS using Axios)
…
return axios
.post(API_URL + 'create/',
"test",
{
headers: headers,
}
)
.then(response => {
return response.data;
})
.catch((error) => {
console.log(error);
});

You can pass any JSON value [json.org], so that includes a string, but a string is put between double quotes ("…") and some characters should be escaped, so you send "test", not test.
test itself is not a valid JSON value. Indeed, you can check for example with the Online JSON valiator [jsonlint.com] if a JSON blob is valid JSON.
A string literal is, just like for example in Python and JavaScript thus put in an "envelope" of double quotes, and escapes certain characters such that if the string itself contains a double quote, that character is escaped properly.

Related

Why does django return the data I submitted as the HttpResponse?

So I am making Angular 8 and Django project. The scenario is that from a form in Angular, data is sent to Django to store it in a database.
class NewProfileView(viewsets.ModelViewSet):
queryset = NewProfile.objects.all()
serializer_class = NewProfileSerializer
def post(self,request,*args,**kwargs):
email = request.data['email']
password = request.data['password']
username = request.data['username']
NewProfile.objects.create(email=email,password=password,username=username)
return HttpResponse({'message':'Registered Successfully'},status=200)
The above represents my Django view. Now, seeing this, for a successful creation, I should get the response as 'Registered Successfully'. But what I get is the data I submitted in JSON format (basically a dictionary. I don't really know if it is json).
Why is it happening?
export class PostService {
private url = 'http://localhost:8000/login/';
constructor(private httpClient:HttpClient) {}
getPosts(data){
return this.httpClient.get(this.url+'?email='+data.email+'&password='+data.password);
}
create(url,post){
return this.httpClient.post<any>(url,post);
}
}
This is what is there in my angular service.
onSubmit(){
console.log(this.userForm);
this.service.create('http://localhost:8000/profile/',this.userForm)
.subscribe(response=>{
console.log(response);
});
This is the code in my component.ts file.
P.S.- I know I'm storing the passwords wrong way. But its just for debugging purposes.
To return a dict data from Django should use JsonResponse. This will serializer the Dict into json and add the correct Content-Type header.
from django.http import JsonResponse
>>> response = JsonResponse({'foo': 'bar'})
>>> response.content
>>> b'{"foo": "bar"}' # This will be the body sent to the client
# In your Case
>>> return JsonResponse({'message':'Registered Successfully'},status=200)
On the Javascript side, how you access the JSON data will vary based on the client you use to make http call. You can parse the response body using JSON.parse(body), but most http clients I have used will handle that for you (Fetch API has a response.json() method, and I think axios gives you JS object automatically when the response is json type)
Note on Dict vs JSON
the data I submitted in JSON format (basically a dictionary. I don't really know if it is json).
Dictionary is a native python type. Looks similar to JSON but they are not the same. eg. Python uses None, while JSON uses null, and many other differences like that.
JSON (Javascript Object Notation) is a way to serializer and deserializer javascript objects.
To send a dict data to your JS client as a JSON object, you would need to json.dumps(dict) to get the serialized json version of your dict, and then return that in the response body. JsonResponse handles the serialization for your, and also adds the "Content-Type" headers "application/json" to let your client know it's receiving a json body body that can be deserialized into a JS object.

What does the DRF Format option mean

I am using DRF for the first time and I would like to know what the "format" in the following code snippet from their website do:
class CommentList(APIView):
def get(self, request, format=None):
# do stuff...
def post(self, request, format=None):
# do stuff...
I read the docs, but I am not sure how it works still. Can someone enlighten me with an example? Thanks
For example,
Suppose their is an APIView with following code :
class HelloAPIView(APIView):
def get(self, request, format):
# do stuff...
def post(self, request, format):
# do stuff...
And endpoint url for HelloAPIView is :
http://example.com/api/users
Now if u want the data in response to be in json format then the URL you would access
will be like
http://example.com/api/users.json
So when you access urls like above , then value " json " will be passed as 3rd argument (1st is self , 2nd is request and 3rd is format) to get() or post() methods of HelloAPIView.
So basically format parameter is used to define in which format you want the response.
For more details refer
https://www.django-rest-framework.org/api-guide/format-suffixes/
https://www.django-rest-framework.org/tutorial/2-requests-and-responses/#adding-optional-format-suffixes-to-our-urls
Format is added to handel multiple content types in DRF
Using format suffixes gives us URLs that explicitly refer to a given format, and means our API will be able to handle URLs such as
http://localhost/api/items/4.json
Adding format-suffix patterns to each individual entry in the URLconf for your API is error-prone and non-DRY, so REST framework provides a shortcut to adding these patterns to your URLConf.
for more details refer this

Postman Swift code showing different url from my normal url

I have created a filter api with more than one arguements to pass in url to filter product in my iOS application. The api is written in Python in Django rest framework. The api is working fine in postman, but when I check the code for Swift in postman is shows some complex url structure and I am confused how to apply that url string my swift code.
My serializer class:
class ProductOptionsSerializer(serializers.ModelSerializer):
optionproductid = ProductSerializer()
optionattributeid = AttributeSerializer()
optionsid = serializers.IntegerField()
class Meta:
model = Productoptions
fields = ('optionproductid', 'optionattributeid', 'optionsid')
View:
class GetProductByMulipleFilter(ListAPIView):
serializer_class = ProductOptionsSerializer
def get_queryset(self):
query_params = self.request.query_params
somethings = query_params.get('filters', None)
# create an empty list for parameters to be filters by
somethingParams = []
serializer = self.serializer_class()
# create the list based on the query parameters
if somethings is not None:
for something in somethings.split(','):
somethingParams.append(str(something))
if somethings is not None:
queryset_list = Productoptions.objects.all()
queryset_list = queryset_list.filter(optionattributeid__attributename__in=somethingParams)
return queryset_list
url
url(r'^$', GetProductByMulipleFilter.as_view(), name='filters')
http://192.168.1.13:8000/api/?filters=puma,nike
Its working fine and showing right results. But the code in swift is showing url like:
let request = NSMutableURLRequest(url: NSURL(string: "http://192.168.1.13:8000/api/?filters=puma%2Cnike")! as URL
My question is why the url in Swift code is not showing parameter seperated with ',' only.
Because request encode , symbol into %2C while it was processed. You can find more about URL percent encoding on Wiki
When a character from the reserved set (a "reserved character") has
special meaning (a "reserved purpose") in a certain context, and a URI
scheme says that it is necessary to use that character for some other
purpose, then the character must be percent-encoded. Percent-encoding
a reserved character involves converting the character to its
corresponding byte value in ASCII and then representing that value as
a pair of hexadecimal digits. The digits, preceded by a percent sign
(%) which is used as an escape character, are then used in the URI in
place of the reserved character. (For a non-ASCII character, it is
typically converted to its byte sequence in UTF-8, and then each byte
value is represented as above.)
You can try
let param = "anyThing"
let request = NSMutableURLRequest(url: NSURL(string: "http://192.168.1.13:8000/api/?filters=\(param)")! as URL

Order of fields in Django forms

I'm using Django to execute some scripts in server-side and I'm using forms to get parameters.
I define my forms in the order I need those parameters and it renders it in that order. The problem is that when I submit the form it is sent without that order so the scripts execute something like:
./script parameter3 parameter1 parameter2
instead of
./script parameter1 parameter2 parameter3
each form has different fields and I only have one method to execute them all. So I take all the fields except csrftoken and so this way:
for arg in request.POST:
args.append(request.POST.get(arg))
And executing then using
command.extend(args)
I have tried to define the order in the forms.py with
self.fields.keyOrder = ['parameter1','parameter2','parameter3']
But I think its only for ordering when render. Is there anyway to control the order the submit send the data?
Thanks in advance
If you want to maintain the order of fields, don't use form to post data.
Consider posting JSON with AJAX, and puting the fields in a list:
// You can compose such a JSON string by extracting the fields with jQuery
var field1_obj = {
name:"field1",
value: $("#field1").value()
}
var field2_obj = {
name:"field2",
value: $("#field2").value()
}
var payload = {data:[field1_obj, field2_obj]}
var payload_json = JSON.stringify(payload)
// JSON string
{data: [{name: "field1", value:"val1"}, {name: "field1", value:"val1"}]}
// Post it to server
$.post(url, payload_json ...)
On server side, it is pretty trivial to load the json string into a python dictionary:
import json
form = json.loads(request.body)

Django: serialize json as a parameter when redirecting to external url

I am trying to redirect a user to a Facebook app once they login to my Django application. This is happening from inside my view. The Facebook app is expecting a Json encoded variable. Here is what I am trying (which is not working):
json_data = serializers.serialize("json", UserProfile.objects.filter(user=user), fields=('study_code'))
url = "https://www.facebook.com/" + json_data + "some_other_stuff"
return redirect(url)
My question main question: What is the proper way to do this?
One issue I am having is that the "json_data" variable is returning the following:
[{"fields": {"study_code": "XXX001"}, "model": "django_app.userprofile", "pk": 12}]
I thought the 'fields' parameter, in the serialize method, will limit the returned data to only the specified field (I only need the study_code) but I'm getting all of this extra stuff. Also, I can't append the 'json_data' variable to the string 'url' (obviously). What is the correct way to append this serialized data to the url string?

Categories

Resources