Python flask not working with url containing "?" - python

I am new to flask and I was trying to make GET request for url containing "?" symbol but it look like my program is just skipping work with it. I am working with flask-sql alchemy, flask and flask-restful. Some simplified look of my program looks like this:
fields_list = ['id']
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
class Get(Resource):
#staticmethod
def get():
users = User.query.all()
usr_list = Collection.user_to_json(users)
return {"Users": usr_list}, 200
class GetSorted(Resource):
#staticmethod
def get(field, type):
if field not in fields_list or type not in ['acs', 'desc']:
return {'Error': 'Wrong field or sort type'}, 400
users = db.session.execute(f"SELECT * FROM USER ORDER BY {field} {type}")
usr_list = Collection.user_to_json(users)
return {"Users": usr_list}, 200
api.add_resource(GetSorted, '/api/customers?sort=<field>&sort_type=<type>')
api.add_resource(Get, '/api/customers')
Output with url "http://127.0.0.1:5000/api/customers?sort=id&sort_type=desc" looks like this
{
"Users": [
{
"Id": 1
},
{
"Id": 2
},
{
"Id": 3
},
]
}
But I expect it to look like this
{
"Users": [
{
"Id": 3
},
{
"Id": 2
},
{
"Id": 1
},
]
}
Somehow if I replace "?" with "/" in url everything worked fine, but I want it to work with "?"

In order to get the information after ?, you have to use request.args. This information is Query Parameters, which are part of the Query String: a section of the URL that contains key-value parameters.
If your route is:
api.add_resource(GetSorted, '/api/customers?sort=<field>&sort_type=<type>')
Your key-values would be:
sort=<field>
sort_type=<type>
And you could get the values of the field and type keys like this:
sort = request.args.get('field', 'field_defaul_value')
sort_type = request.args.get('type', 'type_defaul_value')
More info:
1
2

With Flask you can define path variables like you did, but they must be part of the path. For example, defining a path of /api/customers/<id> can be used to get a specific customer by id, defining the function as def get(id):. Query parameters cannot be defined in such a way, and as you mentioned in your comment, you need to somehow "overload" the get function. Here is one way to do it:
from flask import Flask, request
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
USERS = [
{"id": 1},
{"id": 3},
{"id": 2},
]
class Get(Resource):
#classmethod
def get(cls):
if request.args:
return cls._sorted_get()
return {"Users": USERS, "args":request.args}, 200
#classmethod
def _sorted_get(cls):
field = request.args.get("sort")
type = request.args.get("sort_type")
if field not in ("id",) or type not in ['acs', 'desc']:
return {'Error': 'Wrong field or sort type'}, 400
sorted_users = sorted(USERS, key=lambda x: x[field], reverse=type=="desc")
return {"Users": sorted_users}, 200
api.add_resource(Get, '/api/customers')
if __name__ == '__main__':
app.run(debug=True)
Here is Flask's documentation regarding accessing request data, and Flask-Restful's quickstart guide.

Related

Unable to resolve KeyError in Python using Flask

I continue to get this KeyError even though the key "name" obviously exists. I've attempted a couple methods to iterate through an array of dictionaries in Python with no luck. Any help is appreciated.
import re
from flask import Flask, json, request
from werkzeug.user_agent import UserAgent
app = Flask(__name__)
users = [
{"name:": "bitley",
"age": 5000,
"items": [
{"name:": "toyota"},
{"name": "camry"}
]
}
]
#/GET a user by ID
#app.route('/users/<string:name>')
def get_user_by_name(name):
#check to see if the user exists
for user in users:
if user['name'] == 'name':
return json.jsonify(users)
return json.jsonify({"message: " "user not found.."})
Error at line
if user['name'] == 'name':
KeyError: 'name'
It's because there is an extra colon "name:":.
What I think you mean is "name":.
And probably you mean return json.jsonify({"message:" "user not found.."}) instead of return json.jsonify({"message": "user not found.."}).
And if user['name'] == name: instead of if user['name'] == 'name':.
Your problem is when you create your users list, you have an extra colon in in the name key, it should be:
users = [
{"name": "bitley",
"age": 5000,
"items": [
{"name:": "toyota"},
{"name": "camry"}
]
}
]
The problem is not only the extra colon but also how you access the data in the list because, in list indices, you can only use integers or slices, not strings. Moreover, when you check the username, it should be a variable and not a string, otherwise, it will never pass through.
So it should be:
from flask import Flask, json, request
app = Flask(__name__)
users = [
{"name": "bitley",
"age": 5000,
"items": [
{"name:": "toyota"},
{"name": "camry"}
]
}
]
#app.route('/users/<string:name>')
def get_user_by_name(name):
#check to see if the user exists
for user in users:
if users[0]['name'] == name:
return json.jsonify(users)
return json.jsonify({"message: " "user not found.."})

Django - How to map object from another API and send in GET response

I would like to map object from another API and send in GET response. I'm going to change only id of received object. Let's assume I get data from another API in such format:
{
"id": "31242",
"name": "sth1",
"price": "44",
"data": "2017-06-07",
}
In my database I have table object1 with values:
{
"id": "123",
"name": "sth1",
},
{
"id": "124",
"name": "sth2",
},
{
"id": "125",
"name": "sth3",
}
Field name is unique both in data from API and in data from database. I receive an object named sth1. So now I would like to find it in my database and get his id, replace with id from API and send GET response. In this case my response would look in this way:
{
"id": "123",
"name": "sth1",
"price": "44",
"data": "2017-06-07",
}
At this moment this is my URL - url(r'^data/(?P<name>\w+)$', views.DataList),
but I would like to have such URL - localhost:8000/data?name=sth
Myview.py:
#api_view(['GET'])
def DataList(request, name=None):
if request.method == 'GET':
quote = getDataFromAPI().get(name)
return Response(quote)
serializers.py:
class Object1Serializer(serializers.ModelSerializer):
class Meta:
model = Object1
depth = 1
fields = '__all__'
models.py:
class Object1(models.Model):
name = models.CharField(max_length=200)
I have done it in this way:
#api_view(['GET'])
def DataList(request):
t = request.GET.get("t","")
quote = getDataFromAPI().get(t)
id = Object1.objects.get(t=t)
quote["id"] = id
return Response(quote)
But I get error:
TypeError: Object of type 'Object1' is not JSON serializable
I suppose, your view should look somewhat like this,
#api_view(['GET'])
def DataList(request):
t = request.GET.get("t","")
quote = getDataFromAPI().get(t)
id = Object1.objects.get(t=t).id #put the id of the object in the variable.
#not the object itself.
quote["id"] = id
return Response(quote)
If you want to change the url from
url(r'^data/(?P<name>\w+)$', views.DataList) to localhost:8000/data?name=sth you'd need to change your api endpoint from
#api_view(['GET'])
def DataList(request, name=None):
to
#api_view(['GET'])
def DataList(request):
name = request.GET.get("name","")
and then take the id of object from your database by querying
id = Object1.objects.get(name=name)
and then updating id in response to be sent
quote["id"] = id

DRF-Extension cache ignoring query parameters

I am using drf-extension for caching my APIs. But it is not working as expected with cache_response decorator.
It caches the response for say /api/get-cities/?country=india . But when I hit /api/get-cities/?country=usa, I get the same response.
Here is the sample code:
settings.py
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/0",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient"
},
"KEY_PREFIX": "city"
}
}
REST_FRAMEWORK_EXTENSIONS = {
'DEFAULT_USE_CACHE': 'default',
'DEFAULT_CACHE_RESPONSE_TIMEOUT': 86400,
}
views.py
class GetCities(APIView):
#cache_response()
def get(self, request):
country = request.GET.get("country", "")
return get_cities_function(country)
Please help with this.
I was able to find a solution for the problem. I created my own keys in redis with the combination of api name and parameter name(in my case country). So when API is hit with query parameters, I check if a key exists corresponding to that, if it exists then cached response is returned.
class GetCities(APIView):
def calculate_cache_key(self, view_instance, view_method, request, args, kwargs):
api = view_instance.get_view_name().replace(' ', '')
return "api:" + api + "country:" + str(request.GET.get("country", ""))
#cache_response(key_func='calculate_cache_key')
def get(self, request):
country = request.GET.get("country", "")
return get_cities_function(country)

How to access ForeignKey data without making extra queries in Wagtail(django)

I have the following two classes in my app.models and i'm using the wagtail APIs to get the data as json
class AuthorMeta(Page):
author=models.OneToOneField(User)
city = models.ForeignKey('Cities', related_name='related_author')
class Cities(Page):
name = models.CharField(max_length=30)
So, when I try /api/v1/pages/?type=dashboard.AuthorMeta&fields=title,city, it returns the following data:
{
"meta": {
"total_count": 1
},
"pages": [
{
"id": 11,
"meta": {
"type": "dashboard.AuthorMeta",
"detail_url": "http://localhost:8000/api/v1/pages/11/"
},
"title": "Suneet Choudhary",
"city": {
"id": 10,
"meta": {
"type": "dashboard.Cities",
"detail_url": "http://localhost:8000/api/v1/pages/10/"
}
}
}
]
}
In the city field, it returns the id and meta of the city. How can I get the name of the city in the response here, without making an extra query? :/
I couldn't find any solution in the Documentation. Am I missing something?
Use Django model property to return through the ForeignKey:
class AuthorMeta(Page):
author=models.OneToOneField(User)
city = models.ForeignKey('Cities', related_name='related_author')
city_name = property(get_city_name)
def get_city_name(self):
return self.city.name
Check Term Property to better understand the concept
In case you have the foreign key in a Streamfield, e.g. a PageChooserBlock, you can customize the api response by overwriting the get_api_representation of a block, as described in the example as provided here:
class CustomPageChooserBlock(blocks.PageChooserBlock):
""" Customize the api response. """
def get_api_representation(self, value, context=None):
""" Return the url path instead of the id. """
return value.url_path

Flask mongoengine for a json api

I want to develop a json api using flask-mongoengine, but it seems a litle difficult to return json responses.
This is my view's code:
#users.route('/', methods=["GET"])
def index():
return json_util.dumps(User.objects.first(), default=encode_model)
All this returns is this:
["username", "password", "email", "foods", "id"]
And doesn't return the objects values. How can I return a list of all my users in json format without having to build a dict manually?
What I would like to achive is something like this:
[
"this_documents_id" : {
"username" : "superjhondoe",
"password" : "12345",
"email" : "jhon#doe.com",
"foods" : ["hot dog", "pasta", "banana"]
},
...
]
change it to:
#users.route('/', methods=["GET"])
def index():
return json_util.dumps(User._collection_obj.find(User.objects().first()))
Depending on the version you use, in mongonengine 0.8 there is a to_json method.

Categories

Resources