How to add a status field to a dictionary output? - python

I need to write a python API for users to call. The input and output specifications are below, as one example:
// input
{"keyword":"iPhone",
"category":"phone"
}
// output
{
"keyword": "iPhone",
"status": "success",
"result": [
{
"word": "iPhone",
"type": "smart_phone"
}, "extend":{}
]
}
I am not going to write http service, but just regular python function for call, and the user needs to install:
pip install my_api
The input is a dictionary, and the output is also a dictionary. How to populate the 'status' field in the output dictionary? I feel this is required for http service to indicate whether the call is successful or not. Since I am not using http service, I don't know how to generate value for the 'status' in the output. The function I write would look like:
def query_by_dict(input: Dict) -> Dict:
result = {}
...
result = self.query_helper(input)
return result
if the self.query_helper() fails or didn't produce any value, I will just return an empty dict 'result' to the client. Why is the 'status' field in the output specification?

If we know that result in the query_by_dict function is already a dictionary, we can just add another argument to that dictionary. As seen below:
def query_by_dict(input: Dict) -> Dict:
result = {}
...
result = self.query_helper(input)
result['status'] = 'success' if result else 'failure'
return result
In the above example, the important part of the code is the result['status'] where we are defining the status argument of the result dictionary. Since we know an empty dictionary result is failure, we can just check for this and set the status argument.

Related

Extracting certain value from MongoDB using Python

I have a mongo database including the following collection:
"
"_id": {
"$oid": "12345"
},
"id": "333555",
"token": [
{
"access_token": "ac_33bc",
"expires_in": 3737,
"token_type": "bearer",
"expires_at": {
"$date": "2021-07-02T13:37:28.123Z"
}
}
]
}
In the next python script I'm trying to return and print only the access_token but can't figure out how to do so. I've tried various methods which none of the worked.I've given the "id" as a parameter
def con_mongo():
try:
client = pymongo.MongoClient("mongodb:localhost")
#DB name
db = client["db1"]
#Collection
coll = db["coll1"]
#1st method
x = coll.find({"id":"333555"},{"token":"access_token"})
for data in x:
print(x)
#2nd method
x= coll.find({"id":"333555"})
tok=x.distinct("access_token")
#print(x[0])
for data in tok:
print(data)
except Exception:
logging.info(Exception)
It doesn't work this way, although if I replace (or remove) the "access_token" with simply "token" it works but I get back all the informations included in the field "token" where I only need the value of the "access_token".
Since access_token is an array element, you need to qualify it's name with the name of the array, to properly access its value.
Actually you can first extract the whole document and get the desired value through simple list and dict indexing.
So, assuming you are retrieving many documents with that same id:
x = [doc["token"][0]["access_token"] for doc in coll.find({"id":"333555"})]
The above, comprehensively creates a list with the access_tokens of all the documents matching the given id.
If you just need the first (and maybe only) occurrence of a document with that id, you can use find_one() instead:
x = coll.find_one({"id":"333555"})["token"][0]["access_token"]
# returns ac_33bc
token is a list so you have to reference the list element, e.g.
x = coll.find({"id":"333555"},{"token.access_token"})
for data in x:
print(data.get('token')[0].get('access_token'))
prints:
ac_33bc

How can I differentiate a parameter being None or being not present in Python?

I'm new to Python so sorry in advance if the answer to this is a basic concept.
I'm struggling with trying to identify when a parameter is None or is not received.
I wrote a quick example below to illustrate my doubt.
Considering I have the following Enum that I'll use as a datatype:
class Status(Enum):
ACTIVE = "1"
PENDING = "2"
DELETED = "3"
I want to have a single function that can do 3 scenarios:
Build a dict with a field with a value:
{"name": "John", "status": "1"}
Build a dict with a field with null value:
{"name": "John", "status": None}
Build a dict without a field:
{"name": "John"}
For that, I created 3 tests:
def test_send_valid_status():
request_body = build_request(name="John", status=Status.ACTIVE)
assert body == {"name": "John", "status": "1"}
def test_send_null_status():
request_body = build_request(name="John", status=None)
assert body == {"name": "John", "status": None}
def test_dont_send_status():
request_body = build_request(name="John")
assert body == {"name": "John"}
How can I write build_request() in order to pass the above scenarios? Is important to me that the person calling build_request() knows there's a field status that can be sent and what datatype should be.
My first attempt was to make status optional:
def build_request(name: str, status: bool = None):
request_body = {
"name": name,
}
if status is not None:
request_body["status"] = status.value
return request_body
Of course this fails on test_dont_send_status() because status is assigned a None if I don't send the parameter.
My second attempt was to use kwargs and tell the user in the docs what to send:
def build_request(name: str, **kwargs):
"""
:param name: str
:param status: Status = None
:return: dict
"""
request_body = {
"name": name,
}
for arg, value in kwargs.items():
request_body[arg] = value.value
return request_body
The problem here is that pycharm complains param status is not an argument. Also, it doesn't seem to be a robust solution.
How can I handle that difference between the parameter being None and the parameter not being sent?
In general, you cannot differentiate. That's why using **kwargs is the right way.
Pycharm complaints because status indeed is not an argument of this function, it can take anything. Co you must do the type/value check manually (you cannot enforce the enum).
Another option is to use different default value than None, some constant would work. And then compare it to this constant.
NOT_PRESENT = []
def build_request(name: str, status: Union[bool, list] = NOT_PRESENT):
request_body = {
"name": name,
}
if status is not NOT_PRESENT:
request_body["status"] = status.value
return request_body
You can use the dictionary's get() method, which can take a value for the cases when the value is not found in the dict, the default is None here too but you can change it to something that makes sense and signals that the value was not found in the dict. You use it like this:
result = mydict.get(key_of_val_to_be_found, value_if_not_found)
You can put a default value that you won't use.
For example:
def build_request(name: str, status="Empty"):
request_body = { "name": name }
if status == "Empty":
pass
elif status is None:
request_body["status"] = None
elif isinstance(status, bool):
request_body["status"] = int(status)
else:
# manage invalid status here
print("invalid status")
return request_body

Django returns an array instead of a dictionary (rest-api-framework

I send a post request with data (using requests.post('url', data=data) ):
data = {
'token':1234,
'data':{
'name':'test',
'etc':True,
}
}
When processing reguest.POST in django i get:
<QueryDict: {'token':[1234], 'data':['name', 'etc']} >
What could be the reason?
What could be the reason?
This is simply how a QuerDict is represented. This is because in a querystring and in a HTTP header, the same key can occur multiple times. It thus maps a key to the value.
If you subscript the item [Django-doc], like request.POST['token'] it will always return the last element, if you use .getlist(…) [Django-doc], it will return a list of all items:
request.POST['token'] # 1234
request.POST.getlist('token') # ['1234']
Furthermore, as you found out, you can not pass a dictionary as value. If you want to send this, you need to serialize it, for example as a string:
import json
data = {
'token':1234,
'data': json.dumps({
'name':'test',
'etc':True,
})
}
then at the receiving end, you can deserialize these:
import json
json.loads(request.POST['data'])

Why does Marshmallow return empty for multiple items but returns a single item?

I'm building an API using Flask and marshmallow to format the query results but for some reason Filter.query.all() is returning empty, but when I replace that with Filter.query.first() it returns the first filter. I've also double checked my database to make sure multiple entries are there. Is there anything I'm doing wrong here?
from Model import db, Filter
class FilterSchema(ma.Schema):
id = fields.Integer()
filter_schema = FilterSchema()
### returns list of all filters in Filter db
def get(self):
filters = Filter.query.all()
filters = filter_schema.dump(filters).data
return {'status': 'success', 'data': filters}, 200
returns:
{
"status": "success",
"data": {}
}
while Filter.query.first() returns:
{
"status": "success",
"data": {
"id": 1
}
}
It turns out for a schema in marshmallow you need to specify whether the schema is dumping multiple entries or not. When there is more than one, add many=True to your FilterSchema():
ie: filter_schema = FilterSchema(many=True)
or even better, add a different variable called
filters_schema = FilterSchema(many=True)
and choose which one to use depending on the data you want returned.

Modify JSON response of Flask-Restless

I am trying to use Flask-Restless with Ember.js which isn't going so great. It's the GET responses that are tripping me up. For instance, when I do a GET request on /api/people for example Ember.js expects:
{
people: [
{ id: 1, name: "Yehuda Katz" }
]
}
But Flask-Restless responds with:
{
"total_pages": 1,
"objects": [
{ "id": 1, "name": "Yahuda Katz" }
],
"num_results": 1,
"page": 1
}
How do I change Flask-Restless's response to conform to what Ember.js would like? I have this feeling it might be in a postprocessor function, but I'm not sure how to implement it.
Flask extensions have pretty readable source code. You can make a GET_MANY postprocessor:
def pagination_remover(results):
return {'people': results['objects']} if 'page' in results else results
manager.create_api(
...,
postprocessors={
'GET_MANY': [pagination_remover]
}
)
I haven't tested it, but it should work.
The accepted answer was correct at the time. However the post and preprocessors work in Flask-Restless have changed. According to the documentation:
The preprocessors and postprocessors for each type of request accept
different arguments, but none of them has a return value (more
specifically, any returned value is ignored). Preprocessors and
postprocessors modify their arguments in-place.
So now in my postprocessor I just delete any keys that I do not want. For example:
def api_post_get_many(result=None, **kw):
for key in result.keys():
if key != 'objects':
del result[key]

Categories