Django - Serialize response from another server - python

I'm using DRF to make a GET request to another server and return this reponse to the client.
What I want to know is how do I get only selected fields from that other server's response to return to my client.
What I have is a response like this:
{
"plans": [
{
"setup_fee": 500,
"amount": 990,
"code": "plano01",
}
{
"setup_fee:...
"code": "plano02",
}...
An array with many objects. And I want to give to the client something like this:
{
"plans": [
{
"code": "plano01",
}
{"code": "plano02"...
Only the code field.
Whats the best way to do this with Django/DRF?

There's not much point in using DRF here. You're getting a response in JSON format, you just need to parse it to a dict, extract the elements you want, and return it back as JSON again; DRF would be overkill. The view can simply be:
def get_plan_codes(request):
data = requests.get('external_api...').json()
codes = [{'code': plan['code']} for plan in data['plans']]
data['plans'] = codes
return JsonResponse(data)

I don't think I would use DRF for this personally, although you could.
I think this could be done in a fairly straight foward way using just requests and django itself.
Something like:
from django.http import JsonResponse, HttpResponse
import json
import requests
def plans(request):
response = requests.get("my url for the request")
if response.status_code == 200:
json_content = json.loads(response.content)
# json_content is a python dictionary, so you can just postprocess the way you want it here
return JsonResponse(json_content)
else:
return #You will want to return an HttpResponse with the appropriate status code, see the docs below on that
# https://docs.djangoproject.com/en/1.11/ref/request-response/#django.http.HttpResponse.status_code

Related

How to call Odoo data from JSON controller?

So I need to make a Rest service which will call data from an specific model.
In order to do this, I must create a controller.
I'm newbie with Odoo, also with Json, and I can't find anything that could work for this.
Far I have is all I need but in a http request.
class RestService(http.Controller):
#http.route('/orders/<model("sale.order"):order>', auth='none', website=True)
def consulta_orden(self , order):
return request.render('consulta_pedidos.order', {'order': order})
return {'order_id': order_id}
The idea is that when I hace the data loaded, I can render this data in a template. Already did this with an Http request, but I need it with Json.
Any tip about how to implement it better.
I solved my problem in a very simple way.
The code:
#http.route(['/orden_detalle', '/orden_detalle/<int:order_id>'], type='json', auth='user')
def orden_detalle(self, order_id=None):
if order_id:
domain = [('id', '=', order_id)]
else:
domain = []
sales_rec = request.env['sale.order'].search(domain)
sales = []
for rec in sales_rec:
vals = {
'id': rec.id,
'name': rec.name,
'partner_id': rec.partner_id.name,
'user_id': rec.user_id.name,
}
sales.append(vals)
data = {'status': 200, 'response': sales, 'message': 'Sale(s) returned'}
return data
My source:
https://www.youtube.com/watch?v=wGvuRbCyytk
Another of my questions: How to get specific record in json controller in Odoo 12
If you want to return Json (for a REST-API) you just need to return it in a Response object.
Make sure you have Response imported from odoo.http.
Then you can return the json data like this:
return Response(json.dumps({'order_id': order_id}), status=200, content_type="application/json")
I usually wrap this logic in a separate function (outside of the controller) that logs the request and response on the Odoo object that is updated, and then returns the Response object.
That way it can also be easily reused in Error handling (where you can then return a custom error response in Json format)
EDIT: also make sure you're not including Content-Type: application/json in the request, in that case Odoo will think it's a JSON-RPC request.

In odoo's controller file, how to change the json response format when the type is json?

The application/json in the request header and json string in the request body when I initiate an http request , the Odoo server receives the request, but the json returned to the client is not what I want to return.
Here are two additional key,jsonrpc,id,result.The dictionary corresponding to the key result is what I really want to return to the client.
And if I change the type variable in the http.route to http instead of json, I will can't receive json format data from the client.
What shoul I do?Thanks everyone!
My Odoo version is 10,python version is 2.7.12
Here is my code
controllers.py
from odoo.http import Controller,route
class API(Controller):
#route('/v1/access_something',type='json',auth='none',csrf=False,methods=['GET'])
def access_something(self,**kwargs):
return {"a":1,"b":2}
Test interface with requests
import requests
re = requests.get('http://192.168.1.55:8069/v1/access_something',json={"c":1},headers={'Content-Type':'application/json'})
print(re.json())
The data in re.json()
{
"jsonrpc": "2.0",
"id": null,
"result": {
"a": 1,
"b": 2
}
}
But the following result is what I want.
{
"a": 1,
"b": 2
}
I've found a way to solve this problem.
This problem arises because there is a method _json_responsein the source code JsonRequestthat we can overwrite dynamically.
In order not to interfere with the use of the original framework by others, we can pass our own specific parameters in our own decorator#http.routeby using kwargs. We construct the json dictionary we need to return to the client by determining whether the decorator has our own parameters.
Here is my codecontrollers.py
from odoo.http import Controller,route,JsonRequest
def _json_response(self, result=None, error=None):
lover = self.endpoint.routing.get('lover')
if lover == 'chun':
response = {}
if error is not None:
response['error'] = error
if result is not None:
response = result
else:
response = {
'jsonrpc': '2.0',
'id': self.jsonrequest.get('id')
}
if error is not None:
response['error'] = error
if result is not None:
response['result'] = result
if self.jsonp:
# If we use jsonp, that's mean we are called from another host
# Some browser (IE and Safari) do no allow third party cookies
# We need then to manage http sessions manually.
response['session_id'] = self.session.sid
mime = 'application/javascript'
body = "%s(%s);" % (self.jsonp, json.dumps(response),)
else:
mime = 'application/json'
body = json.dumps(response)
return Response(
body, headers=[('Content-Type', mime),
('Content-Length', len(body))])
setattr(JsonRequest,'_json_response',_json_response) #overwrite the method
class API(Controller):
#route('/v1/access_something',type='json',auth='none',csrf=False,methods=['GET'],lover='chun')
def access_something(self,**kwargs):
return {"a":1,"b":2}
The specific parameter lover='chun' is basis of our judgment.In method _json_response,we can get this parameter through self.endpoint.routing.get('lover')

python: post key/value pairs in request body

I have an existing python API using flask that submits a post request in the body, then calls and executes another python script:
testingREST.py:
import testingdf
import json
from flask import Flask, json, request
app = Flask(__name__)
#app.route("/results", methods=['POST'])
def createResults():
entry = request.get_json().get('entry', '')
passcode = request.get_json().get('passcode', '')
data = testingdf.test(entry, passcode)
response = app.response_class(
response=json.dumps(data),
status=200,
mimetype='application/json'
)
return response
if __name__ == "__main__":
app.run(debug = True, host='localhost', port=8080, passthrough_errors=False)
testingdf.py:
import pandas as pd
def test(entry, passcode):
df = pd.DataFrame()
testInput = zip(entry, passcode)
for entry, passcode in testInput:
df = df.append({'Entry': entry, 'Passcode': passcode}, ignore_index=True)
results = {
'Entry' : entry,
'Outcome' : passcode,
}
return results
This is what I post into the request body when calling the api
{
"entry":[21, 44, 31],
"passcode":["denied", "Try Again", "Retry"]
}
Response:
{
"Entry": 35,
"Outcome": "Success"
}
Problem: In the request body, instead of having one variable for 'entry' and another for 'passcode', is there a way that I have something like this as the request body:
{
"entryInfo":[(21, "denied"), (44, "Try Again"), (31, "Retry")]
}
Basically, passing them in as pairs since I think the way I've implemented it is weird where it can easily be mismatched. Also, I'm not sure if "key/value" is the correct term to use here but I hope this helps.
Using python3 by the way.
Your question seems to boil down to what data to pass to the flask server. There is also another issue about what you do with that data (you're only returning the last one), but you did not ask about that.
Send this as the request body. It uses list notation which is valid JSON. Your flask server can then access the list by decoding the JSON request.
{"entryInfo": [[21, "denied"], [44, "Try Again"], [31, "Retry"]]}
A better way though might be to use a dictionary mapping entry codes to pass codes:
{"entryInfo": {"21": "denied", "44": "Try Again", "31": "Retry"}}
and it's debatable whether the top-level dict is required, you could get away with:
{"21": "denied", "44": "Try Again", "31": "Retry"}

How do I post a lot of data to Django?

I am trying to send of list of files to my Django Website. Each set is transmitted with the following info:
File name,
File size,
File location,
File type
Now, suppose I have 100 such sets of data, and I want to send it to my Django Website, what is the best method I Should use?
PS: I was thinking of using JSON, and then POST that JSON data to my Django URL. The data might then look like this:
{
"files": [
{ "filename":"Movie1" , "filesize":"702", "filelocation":"C:/", "filetype":"avi" },
{ "filename":"Movie2" , "filesize":"800", "filelocation":"C:/", "filetype":"avi" },
{ "filename":"Movie3" , "filesize":"900", "filelocation":"C:/", "filetype":"avi" }
]
}
I think sending json data to your server makes sense. Now to actually implement it, you would need your server to accept the http POST request that you will use to send the data about the files.
So, the server code may look like this:
urls.py:
import myapp
# ...
urlpatterns = patterns('', url(r'^json/$',myapp.serve_json), #http://<site_url>/json/ will accept your post requests, myapp is the app containing view functions
#add other urls
)
#other code
views.py
import json
def serve_json(request):
if request.method == 'POST':
if 'files' in request.POST:
file_list = json.loads(request.POST['files'])
for file in file_list:
#do something with each file dictionary in file_list
#...
return HttpResponse("Sample message") #You may return a message
raise Http404
Now in your Desktop application, once you have the list of the dictionaries of files, you may do this:
import urllib,json
data = urllib.urlencode({'files':json.dumps(file_dict)}) #file_dict has the list of stats about the files
response = urllib.urlopen('http://example.com/json/', data)
print response.read()
You may also look into urllib2 and httplib and use them in place of urllib.

Unit testing Django JSON View

I'm trying to write some unit tests for some Django json_view views and I'm having trouble passing the json_string to the view. I posted a related question yesterday about passing a json string to a Django view from the JS, the issue was that in my JS I was just passing the json string where I needed to be passing the string as the attribute of an object, because I was failing to do this the string was being taken as the key for the resulting query dict. I'm having a similar problem again except that this time it is form a Django unit test to the Django View. Here is a simplified version of my code which produces the same result.
class MyTestCase(TestCase):
def setUp(self):
self.u = User.objects.create_user('test','test','test')
self.u.is_active = True
self.u.save()
self.client.login(username='test',password='test')
def test_create_object_from_form(self):
"""Test the creation of the Instance from the form data."""
import json
json_string json.dumps({'resource':{'type':'book','author':'John Doe'}})
print(json_string)
response = self.client.post(reverse('ajax_view'),
{'form':json_string},'json')
self.assetNotContains(response,'error')
and the view looks like this
#json_view
def ajax_view(request):
"""Process the incoming form data."""
if request.method == 'POST':
print(request.POST)
form_data = json.loads(request.POST['form'])
resource_data = form_data['resource']
form = MyUserForm(resource_data)
if form.is_valid():
...
Here is what the two print statements produce when the test is run. The json_string is
{"resource": {"type": "book", "author": "John Doe"}}
and the query dict looks like
<QueryDict: {u'{\'form\': \'{"resource": {"type": "book", "author": "John Doe"}}\'}': [u'']}>
I'm total newbie with JS and ajax, so don't worry about hurting my pride, the answer is probably so close it could jump up and bite me.
Final edit
I originally stated that header HTTP_X_REQUESTED_WITH='XMLHttpRequest' was necessary in the post call but this is currently false while in tests. This header is necessary for the csrf middleware but csrf is disabled in tests. However, I still believe it is a good practice to put in test even if middleware disables csrf since most javascript library already pass this header by default when doing ajax. Also, if another piece of code that is not disabled ever use the is_ajax method, you won't need to debug your unittest for hours to figure out that the header was missing.
The problem is with the content-type because when django gets a value in there that is different than text/html, it doesn't use the default post data handling which is to format your data like in a query: type=book&author=JohnDoe for example.
Then the fixed code is:
response = self.client.post(reverse('ajax_view'),
{'form':json_string},
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
Here's how I'm using it myself:
post_data = {
"jsonrpc" : "2.0", "method": method, "params" : params, "id" : id }
return client.post('/api/json/',
json.dumps(post_data), "text/json",
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
to do some json-rpc. Notice that since I pass a different content-type than the default value, my data is passed as is in the post request.
Thank you to #Eric_Fortin for turning me on to the header, it does not however resolve my issue with the malformed query dictionary using 'client.post'. Once I made the change from POST to GET with the XMLHttpRequest header my query dictionary straitened itself out. Here is the current solution:
response = self.client.get(reverse('ajax_view'),
{'form':json_string},'json',
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
this is only a partial answer since this request is going to change data on the server and should be POST not a GET.
Edit:
Here is the final code in my test that works for passing a JSON string via POST to my view:
response = self.client.post(reverse('ajax_view'),
{'form':json.dumps(json_dict)})
Now printing from the view shows that the query dictionary is well formed.
<QueryDict: {u'form': [u'{"resource": {"status": "reviewed", "name": "Resource Test", "description": "Unit Test"}}']}>
I found the answer while tinkering with one of my co-workers, removing the content_type 'json' fixed the malformed query dictionary. The view that is being tested does not make use of or call the 'HttpRequest.is_ajax()', sending the header XMLHttpRequest' has no impact on my issue, though including the header would constitute good-form since this post is an ajax request.

Categories