Django + React axios POST cant access Json properties - python

I created two abjects that need to be treated separated in the backend, and sent it to backend with:
const order = {
order_id: 1,
customer: {
name: "Jonas Illver"
age: 18
}
}
const car = {
model: 'XHL'
brand: 'Toyota'
}
const postData = {
order: order,
car: car
}
const res = await axios.post("orders/shipping/create", JSON.stringify(postData));
console.log(res);
And here I return the response: views.py
#api_view(['POST'])
#permission_classes([IsAuthenticated])
def shipping_create_order(request):
if request.method == 'POST':
return JsonResponse(request.POST)
Here's what the console.log prints:
res.data // {"order:"{"order_id": 1, "customer": {"name": "Jonas Illver", "age": 18}}, "car": **car object content**}
The problem is that if I try to access those data in my django view, I always got a 500 error
I tried accessing it via:
order = request.POST['order']
# and also
data = json.loads(request.POST)
order = data['order']
# also
order = request.POST.get('order')
None os the above approaches worked, the only one that doesn't occurs an 500 error is the last one, using the POST.get, but as soon as I add the if not order: return HttpResponse("No order") line, it always return "No order"
How can I access specific info from the request.POST? Am I doing something wrong with the axios request?

Try using request.data to get the data passed in the body. Have a look at the rest-framework docs.
Or another way to get the data is using json.loads(request.body). But using request.data is a bit shorter as it does the json.loads job for you.

Related

Get picture profile user with getUserProfilePhotos in Telegram

I am trying to save the photos of the users of a Telegram bot (a game), using the getUserProfilePhotos method
I am testing it with my own id_user. The code:
def getUserProfilePicture (self, user : User):
if (user is None) : return None
requestStr = self.httpbase + self.token + "/getUserProfilePhotos"
par ={
"user_id" : user.id, #correct id passed.
"limit" : 5
}
dataReq = requests.post(requestStr, json=par)
updateData = json.loads(dataReq.content)
isOk = updateData.get("ok")
And the result (I have also tried with Postman, passing my user id, with and without the limit parameter):
{
"ok": true,
"result": {
"total_count": 0,
"photos": []
}
}
This is not the correct result. In my profile I have 3 images. I have tried with other profiles, and the result is the same: 0 images, and an empty array.
The link to the API is as follows: link getUserProfilePhotos method
Any idea?
Thank you.
If your privacy settings say that only your contacts can see your profile pictures, the bot can't see your profile picture (because you can't add it as contact).
Forgot about that when commenting …

im trying to make an api in flask that takes in 2 inputs

ive got an api that takes in an id
http://127.0.0.1:5000/api/v1/resources/books?id=u3qR4Ps4TbATrg97
looks like that
what im trying to do after that is add something to the end of the url, for example
http://127.0.0.1:5000/api/v1/resources/books?id=u3qR4Ps4TbATrg97uid=something
im not 100% sure if this is possible
# Create some test data for our catalog in the form of a list of dictionaries.
books = [
{'id': 'u3qR4Ps4TbATrg97',
'uid': 'what',
'title': 'A Fire Upon the Deep',
'author': 'Vernor Vinge',
'first_sentence': 'The coldsleep itself was dreamless.',
'year_published': '1992'}
]
#app.route('/api/v1/resources/books', methods=['GET'])
def api_id():
# Check if an ID was provided as part of the URL.
# If ID is provided, assign it to a variable.
# If no ID is provided, display an error in the browser.
if 'id' and 'uid' in request.args:
id = str(request.args['id'])
uid = str(request.args['uid'])
else:
return "Error: No id field provided. Please specify an id."
results = []
for book in books:
if book['id'] == id:
results.append(book)
if book['uid'] == uid:
results.append(book)
this is what i have so far, mostly copy pasted from here
thats no the whole file just the important bits i can think of
You can add two inputs inside the GET query like this
http://127.0.0.1:5000/api/v1/resources/books?id=u3qR4Ps4TbATrg97&uid=something
Just put an & in between!
Use request.args.get method to get parameters from your url. Also add & to your URL as a parameter separator.
from flask import Flask, request
app = Flask(__name__)
#app.route('/api/v1/resources/books')
def books():
id_ = request.args.get('id')
uid = request.args.get('uid')
return f'id: {id_}, uid: {uid}'
app.run()
Open http://127.0.0.1:5000/api/v1/resources/books?id=u3qR4Ps4TbATrg97&uid=something
in browser and you'll get:
id: u3qR4Ps4TbATrg97, uid: something
Multiple parameters|arguments are passed with & character. ?params1=5&params2=3. For your example: http://127.0.0.1:5000/api/v1/resources/books?id=u3qR4Ps4TbATrg97&uid=what. For the code, I would do:
from flask import Flask, request, jsonify, make_response
app = Flask(__name__)
# Create some test data for our catalog in the form of a list of dictionaries.
books = [
{
"id": "u3qR4Ps4TbATrg97",
"uid": "what",
"title": "A Fire Upon the Deep",
"author": "Vernor Vinge",
"first_sentence": "The coldsleep itself was dreamless.",
"year_published": "1992",
}
]
#app.route("/api/v1/resources/books", methods=["GET"])
def api_id():
# Check if an ID was provided as part of the URL.
# If ID is provided, assign it to a variable.
# If no ID is provided, display an error in the browser.
if set(["id","uid"]).intersection(set(request.args)):
id_ = str(request.args["id"])
uid = str(request.args["uid"])
else:
return make_response(
jsonify({"message": "Error: No id field provided. Please specify an id."}),
400,
)
results = []
for book in books:
if book["id"] == id_:
results.append(book)
if book["uid"] == uid:
results.append(book)
response = make_response(
jsonify({"message": results}),
200,
)
response.headers["Content-Type"] = "application/json"
return response
This would return status code 400 if no match and 200 when match

How to use Axios/Vue.js to call data from SQLite/Django backend?

Status Quo:
Whenever a user visits my web application, Axios makes a request to a third party API to fetch data and populate the frontend with that data using v-for.
Conclusion: I have one API call per website visitor.
Desired status:
Whenever a user visits my web application, Axios shall fetch the data from the SQLite database which itself is populated every XX seconds by a python request to reduce API calls.
Questions:
Now I implemented a SQLite database using Django models and views. So far so good, the API gets fetched regularly and updates the database table properly.
1.) How can I now call the data in the database using Axios? As by my research Axios somehow needs to call a view and the view will call the data from the database, is this correct?
2.) If Axios needs to call a view, do I need another view.py file that calls the database? If I would insert the needed view function into the existing view.py file it would initiate another API call, wouldn't it?
3.) And how can I implement the link to the view function to Axios? Instead of a third party API url would I just use the path to the view file?
Quotes_app/Views.py:
from django.shortcuts import render
from Quotes_app.models import ratesEUR
import json
import requests
response = requests.get("http://data.fixer.io/api/latest?access_key=XXXX&base=EUR")
rates_EUR = json.loads(response.content.decode('utf-8'))
timestamp = rates_EUR['timestamp']
base = rates_EUR['base']
date = rates_EUR['date']
rates = rates_EUR['rates']
id = 1
rates_new = ratesEUR(id=id, timestamp=timestamp, base=base, date=date, rates=rates)
rates_new.save()
def render_Quotes_app(request, template="Quotes_app/templates/Quotes_app/Quotes_app.html"):
return render(request, template)
Quotes_app/models.py:
from django.db import models
class ratesEUR(models.Model):
timestamp = models.CharField(max_length=10)
base = models.CharField(max_length=3)
date = models.DateField(auto_now=False, auto_now_add=False)
rates = models.CharField(max_length=8)
def __str__(self):
return self.base
Vue.js/axios: (as of now directly fetching the API)
Vue.config.devtools = true;
var app = new Vue({
delimiters: ['[[', ']]'],
el: '.eurQuotesWrapper',
data() {
return {
rates: [],
};
},
computed: {
rates1() {
const ratesArr1 = Object.entries(this.rates);
const ret = ratesArr1.reduce((a, c, i, d) => {
if (i < d.length / 2) a[c[0]] = c[1];
return a;
}, {});
console.log('rates1', ret);
return ret;
},
rates2() {
const ratesArr2 = Object.entries(this.rates);
const ret = ratesArr2.reduce((a, c, i, d) => {
if (i >= d.length / 2) a[c[0]] = c[1];
return a;
}, {});
console.log('rates2', ret);
return ret;
}
},
created() {
axios
.get("http://data.fixer.io/api/latest?access_key=XXXX&base=EUR")
.then(response => {
this.rates = response.data.rates;
console.log(this.rates);
for(key in this.rates) {
this.rates[key] = new Intl.NumberFormat('de-DE', {
minimumFractionDigits: 5,
maximumFractionDigits: 5
}).format(this.rates[key]);
}
console.log(this.rates);
});
}
});
In advance thank you very much for your help!
You can use DRF(Django Rest Framework) to make REST API's or You can use JsonResponse
to send JSON objects as response.
from django.http import JsonResponse
data = # Your data
JsonResponse(data, encoder=MyJSONEncoder)
For more details visit django's documentation
Better would be if you use DRF for making rest apis. It's much easier.

python url not found - Accessing views.py from AJAX

New to Python and Django and I'm trying to make a simple ajax call from a button click to pass certain data to my views.py, however, when I try to make a url as seen on my ajax code below, the documentId.id does not append unless I directly append in without the "?id=".
{%for document in documents%}
{{document.filename}}
<input type="button" id="{{document.id}}" onclick="loadData(this)" name="load-data" value="Add"/>
{%endfor%}
<script type ="text/javascript">
function loadData(documentId){
$.ajax({
url:"upload-data/load" + "?id=" + documentId.id,
data: {'documentId': documentId},
type: 'GET',
success: function(){
window.location.href = "http://127.0.0.1:8000/url/locations";
}
});
}
</script>
This gives me then an error that says the url cannot be found. I have a line in my urls.py below:
url(r^"upload-data/load/([0-9]+)/$', views.loadFile, name="load-data"),
Other than this method, I am stumped as to how I am going to extract my data to my views.py.
def loadFile(request):
documentId = request.GET.get('id')
newLayer = Layer(get_object_or_404(Document, pk = documentId))
newLayer.save()
layers = Layer.objects.all()
return render(request, 'url/loaded.html', { 'layers': layers})
The persisting error in the console would be:
http://127.0.0.1:8000/upload-data/load/ [HTTP/1.0 404 Not Found]
Use something like this:
def loadFile(request):
documentId= request.GET.get('id', '').
newLayer = Layer(get_object_or_404(Document, pk = documentId))
newLayer.save()
layers = Layer.objects.all()
return render(request, 'url/loaded.html', { 'layers': layers})
And update your url as :
url(r^"upload-data/load/', views.loadFile, name="load-data")
And the script would be like :
<script type ="text/javascript">
function loadData(documentId){
$.ajax({
url:"upload-data/load/?id="+ documentId.id,
data: {'documentId': documentId},
type: 'GET',
success: function(){
window.location.href = "http://127.0.0.1:8000/url/locations";
}
});
}
</script>
Thanks.
In JavaScript you need
"upload-data/load/" + documentId.id
Django doesn't use ?id= in url definition r^"upload-data/load/([0-9]+)/$'. It expects ie. upload-data/load/123 instead of upload-data/load?id=123
EDIT: and you need id in def loadFile(request, id).
And then you don't have to use request.GET.get('id')
From the above answers and comments it seems like rather than passing id as a url param you want to pass the same as a get param. In that case make your urls like below.
url(r^"upload-data/load/', views.loadFile, name="load-data")
and in views, check for get params by replacing id with documentId. document id will be in your dict named as data passed to view. So look for request.GET.get('data','') and from data extract id as below
def loadFile(request):
data = request.GET.get('data', None)
if data:
documentId = data['documentId']
newLayer = Layer(get_object_or_404(Document, pk = documentId))
newLayer.save()
layers = Layer.objects.all()
return render(request, 'url/loaded.html', { 'layers': layers})
else:
return JsonResponse({'error': 'pass document id'}, status=400)
Since you are passing a get param from javascript named as documentId not id.
HTH

Django request data returns str instead of list

I'm developing a REST api with Django and REST-framework. I have endpoint which takes a POST request with this kind of json:
{
"pipeline": ["Bayes"],
"material": [
"Rakastan iloisuutta!",
"Autojen kanssa pitää olla varovainen.",
"Paska kesä taas. Kylmää ja sataa"
]
}
It is a machine-learning analysis api and the json tells to use Bayes classifier to provided strings and return results. This works fine when I test it manually by doing the post requests. However, it breaks down when I try to write an unit test. I have the following test:
class ClassifyTextAPITests(APITestCase):
fixtures = ['fixtures/analyzerfixtures.json'] #suboptimal fixture since requires bayes.pkl in /assets/classifiers folder
def test_classification(self):
""" Make sure that the API will respond correctly when required url params are supplied.
"""
response = self.client.post(reverse('analyzer_api:classifytext'), {
"pipeline": ["Bayes"],
"material": [
"Rakastan iloisuutta!",
"Autojen kanssa pitää olla varovainen.",
"Paska kesä taas. Kylmää ja sataa",
]
})
self.assertTrue(status.is_success(response.status_code))
self.assertEqual(response.data[0], 1)
test fails everytime because of the latter assert gives "AssertionError: 'P' != 1"
Here is my view code:
class ClassifyText(APIView):
"""
Takes text snippet as a parameter and returns analyzed result.
"""
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.AllowAny,)
parser_classes = (JSONParser,)
def post(self, request, format=None):
try:
self._validate_post_data(request.data)
print("juttu", request.data["material"])
#create pipeline from request
pipeline = Pipeline()
for component_name in request.data["pipeline"]:
pipeline.add_component(component_name)
response = pipeline.execute_pipeline(request.data['material'])
status_code = status.HTTP_200_OK
except Exception as e:
response = {"message": "Please provide a proper data.",
"error": str(e) }
status_code = status.HTTP_400_BAD_REQUEST
return Response(response, status=status_code)
def _validate_post_data(self, data):
if "pipeline" not in data:
raise InvalidRequest("Pipeline field is missing. Should be array of components used in analysis. Available components at /api/classifiers")
if len(data["pipeline"]) < 1:
raise InvalidRequest("Pipeline array is empty.")
if "material" not in data:
raise InvalidRequest("Material to be analyzed is missing. Please provide an array of strings.")
if len(data["material"]) < 1:
raise InvalidRequest("Material to be analyzed is missing, array is empty. Please provide an array of strings.")
The really interesting part was when I fired the debugger to check what happens here. Turns out that the line
request.data['material']
gives the last entry of the list in in my request, in this case
"Paska kesä taas. Kylmää ja sataa"
However, while I inspect the contents of the request.data, it shows a querydict with lists pipeline and material as they are in request. Why do I get string instead of material list when I call request.data["material"] ? Is there something I have forgotten and I have to specify some kind of serializer? And why it works during normal execution but not with tests?
I'm using Django 1.8 with Python 3. Also, I'm not tying the view to any specific model.
Finally here is what my debugger shows when I put break points into view:
request.data:
QueryDict: {'material': ['Rakastan iloisuutta!', 'Autojen kanssa pitää olla varovainen.', 'Paska kesä taas. Kylmää ja sataa'], 'pipeline': ['Bayes']}
asd = request.data["material"]:
'Paska kesä taas. Kylmää ja sataa'
This is because QueryDict returns the last value of a list in __getitem__:
QueryDict.getitem(key)
Returns the value for the given key. If the key has more than one value, getitem() returns the last value. Raises django.utils.datastructures.MultiValueDictKeyError if the key does not exist. (This is a subclass of Python’s standard KeyError, so you can stick to catching KeyError.)
https://docs.djangoproject.com/en/1.8/ref/request-response/#django.http.QueryDict.getitem
If you post a form, in which a key maps to a list:
d = {"a": 123, "b": [1,2,3]}
requests.post("http://127.0.0.1:6666", data=d)
this is what you get in the request body:
a=123&b=1&b=2&b=3
Since the test method post the data as a form, what you get from request.data is a QueryDict (the same as request.POST), hence you get the last value in the list when getting request.data.
To get expected behavior, post the data as JSON in the request body (as in #Vladir Parrado Cruz's answer).
By default the QueryDict will return a single item from the list when doing a getitem call (or access by square brackets, such as you do in request.data['material'])
You can instead use the getlist method to return all values for the key:
https://docs.djangoproject.com/en/1.8/ref/request-response/#django.http.QueryDict.getlist
class ClassifyText(APIView):
"""
Takes text snippet as a parameter and returns analyzed result.
"""
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.AllowAny,)
parser_classes = (JSONParser,)
def post(self, request, format=None):
try:
self._validate_post_data(request.data)
print("juttu", request.data["material"])
print("juttu", request.data.getlist("material"]))
#create pipeline from request
pipeline = Pipeline()
for component_name in request.data["pipeline"]:
pipeline.add_component(component_name)
response = pipeline.execute_pipeline(request.data.getlist('material'))
status_code = status.HTTP_200_OK
except Exception as e:
response = {"message": "Please provide a proper data.",
"error": str(e) }
status_code = status.HTTP_400_BAD_REQUEST
return Response(response, status=status_code)
def _validate_post_data(self, data):
if "pipeline" not in data:
raise InvalidRequest("Pipeline field is missing. Should be array of components used in analysis. Available components at /api/classifiers")
if len(data["pipeline"]) < 1:
raise InvalidRequest("Pipeline array is empty.")
if "material" not in data:
raise InvalidRequest("Material to be analyzed is missing. Please provide an array of strings.")
if len(data["material"]) < 1:
raise InvalidRequest("Material to be analyzed is missing, array is empty. Please provide an array of strings.")
Try to do something like that on the test:
import json
def test_classification(self):
""" Make sure that the API will respond correctly when required url params are supplied.
"""
response = self.client.post(
reverse('analyzer_api:classifytext'),
json.dumps({
"pipeline": ["Bayes"],
"material": [
"Rakastan iloisuutta!",
"Autojen kanssa pitää olla varovainen.",
"Paska kesä taas. Kylmää ja sataa",
]
}),
content_type='application/json'
)
self.assertTrue(status.is_success(response.status_code))
self.assertEqual(response.data[0], 1)
Perhaps if you send the data as json it will work.

Categories

Resources