FastApi urls path and 2 values into a query - python

im using FastApi and get some troubles with url.
i have a root url
#app.get("/myurl")
http://host/myurl
and
http://host/myurl?id=2
and here function returns all info from needed table.
on url like http://host/myurl?id=2&type=3 i need to get another query from table. how i need to create function because now this http://host/myurl?id=2 overlapping this function http://host/myurl?id=2&type=3
how i can use multiple urls with different values in it in fastapi?
and i want to know how to make url like http://host/myurl?id=2&type=3,2 to return result from table for two types (query example is SELECT * from mytable WHERE id=%(id)s and type IN (1,2) but type IN (,) should be parameters which i need to inpout

how i can use multiple urls with different values in it in fastapi?
As far as I know, you can't. But fortunately, you don't need to. What you can do is define only one route ("/myurl") with both parameters id and type, and set the second as optional.
Then, if you don't receive type, you process a different query.
By the way, don't use id and type as parameter names, that will mess with the name of the in-built function id()and type().
Here a working example:
from fastapi import FastAPI, Query
app = FastAPI()
#app.get("/myurl")
async def my_url(my_id: int = Query(...), my_type: int = Query(None)):
if my_type:
return f"You gave an id ({my_id}) and a type ({my_type})."
return f"You gave only an id ({my_id}) but no type."
i want to know how to make url like http://host/myurl?id=2&type=3,2
Not sure you can do it at all. What you can do is add the type parameter several times, like this:
http://host/myurl?my_id=2&my_type=3&my_type=2
In this case, you need to slightly change your code:
from fastapi import FastAPI, Query
from typing import List
app = FastAPI()
#app.get("/myurl")
async def my_url(my_id: int = Query(...), my_type: List[int] = Query(None)):
if my_type:
if len(my_type) > 1:
return f"You gave an id ({my_id}) and a list of types ({my_type})."
else:
return f"You gave an id ({my_id}) and a type ({my_type})."
return f"You gave only an id ({my_id}) but no type."
Note that you'll always receive my_type as a list then, even if you pass it only one time.

Related

PATCH, Update a row with SQLalchemy, project build using Flask and CORS

I hope everything is going well.
I'm working in a project really big and it wasn't set up by me. The project is buils using flask and cors.
I'm trying to create a query to update a row with SQLAlchemy follow the structure that the project has. so basically is like that:
#app.route("/update-topic", methods=['PATCH'])
async def update_by_id():
input_data = request.get_json()
await update_record(input_data)
return ApplicationTopicSchema(many=True).dump(data)
As you see in the code above is just a simple endpoint with PATCH method that get the input data and pass it to a function update_record(), that function is in charge to update the record like you can see in the next code:
from sqlalchemy import and_, update
class AppTopics(Base):
__tablename__ = AppTopics.__table__
async def update_record(self, data):
id_data = data['id']
query = self.__tablename__.update().\
where(self.__tablename__.c.id == id_data).values(**data).returning(self.__tablename__)
await super().fetch_one(query=query)
return 'updated'
Basically is something like that, and when I try to use the endpoint I get the next message error:
TypeError: The response value returned by the view function cannot be None
Executing <Handle <TaskWakeupMethWrapper object at 0x000001CAD3970F10>(<Future f
inis...events.py:418>) created at C:\Python\Python380\lib\asyncio\tasks.py:881>
Also, I'm trying to structure the query in another like this:
from sqlalchemy import and_, update
class AppTopics(Base):
__tablename__ = AppTopics.__table__
async def update_record(self, data):
u = update(self.__tablename__)
u = u.values({"topic": data['topic']})
u = u.where(self.__tablename__.c.id == data['id'])
await super().fetch_one(query=u)
return 'updated'
However I got the same error.
May you guys knows what is happening and what means this error:
TypeError: The response value returned by the view function cannot be None
Executing <Handle <TaskWakeupMethWrapper object at 0x000001B1B4861100>(<Future f
inis...events.py:418>) created at C:\Python\Python380\lib\asyncio\tasks.py:881>
Thanks in advance for your help and time.
Have a good day, evening, afternoon :)
The error message "TypeError: The response value returned by the view function cannot be None" is indicating that the view function (in this case, the update_by_id function) is not returning a value.
It seems that the function update_record does not return anything. If you want to return the string "updated" after updating the record, you should use a return statement like this:
async def update_record(self, data):
# update code here
return 'updated'
And on the update_by_id function you should call the return value of await update_record(input_data) to return it.
async def update_by_id():
input_data = request.get_json()
result = await update_record(input_data)
return result
Another point is that in the second example, you are not returning anything either, you should add a return statement before the end of the function.
Also, you are returning 'ApplicationTopicSchema(many=True).dump(data)' but the input data data is not being defined in the function, you should use the 'result' variable returned by update_record function instead.
async def update_by_id():
input_data = request.get_json()
result = await update_record(input_data)
return ApplicationTopicSchema(many=True).dump(result)
It's important to note that in the first example, the update_record function seems to be missing the self parameter, and could be causing some issues with the class.
It's also important to check if the fetch_one function from super() is waiting for the query with await keyword, and also if the fetch_one is returning something, otherwise it could be the cause of the None return value.
My understanding and knowledge is limited, but I hope this helps. Feel free to shoot me any further questions.

Why does the URL string of my API filter have a forward slash instead of an equal sign? [duplicate]

Could you please explain to me why in FastAPI the following works:
#app.post("/items/v1/cards{sku}")
async def create_item(sku: str):
return {"status":200,"sku":sku} # returns status:200 and sku
but, the same endpoint with questionmark in it like the one given below does not?
#app.post("/items/v1/cards?{sku}")
async def create_item(sku: str):
return {"sku":sku} # returns 404
In the first code snippet, you defined the parameter as a Path parameter and worked as expected.
#app.post('/items/v1/cards/{sku}')
async def create_item(sku: str):
return {'sku': sku}
URL Example:
http://127.0.0.1:8000/items/v1/cards/something
In the second one, however, you attempted to pass a Query parameter in the wrong way. As per the documentation:
When you declare other function parameters that are not part of the
path parameters, they are automatically interpreted as "query"
parameters.
Hence, your endpoint should look like this:
#app.post('/items/v1/cards')
async def create_item(sku: str):
return {'sku': sku}
The query is the set of key-value pairs that go after the ? in a URL,
separated by & characters.
URL Example:
http://127.0.0.1:8000/items/v1/cards?sku=something

FastAPI: How to get raw URL path from request?

I have a GET method with requested parameter in path:
#router.get('/users/{user_id}')
async def get_user_from_string(user_id: str):
return User(user_id)
Is it possible to get base url raw path (i.e., '/users/{user_id}') from the request?
I have tried to use the following way:
path = [route for route in request.scope['router'].routes if
route.endpoint == request.scope['endpoint']][0].path
But it doesn't work and I get:
AttributeError: 'Mount' object has no attribute 'endpoint'
The below solution worked fine for me
using the string replace with count parameter replaces the first occurence only. And request.path_params will return the path parameters in the sequence you take it in the request.
def get_raw_path(request):
path = request.url.path
for key, val in request.path_params.items():
path = path.replace(val, F'{{{key}}}',1)
return path
As per FastAPI documentation:
As FastAPI is actually Starlette underneath, with a layer of several
tools on top, you can use Starlette's Request object directly when you
need to.
Thus, you can use Request object to get the URL path. For instance:
from fastapi import Request
#app.get('/users/{user_id}')
def get_user(user_id: str, request: Request):
return request.url.path
Output (if the received user_id was 1):
/users/1
Update
If, however, what you need is the original route path, i.e., /users/{user_id}, you could use the below. The way it works is by getting the root_path first—which would normally be an empty string, unless you have mounted sub-application(s) to the top-level app (e.g., app.mount("/subapi", subapi)), and hence, you need the result to be prefixed with that specific path /subapi—and then append to it the route's path , which you can get from the APIRoute object. Example:
from fastapi import Request
#app.get('/users/{user_id}')
def get_user(user_id: str, request: Request):
path = request.scope['root_path'] + request.scope['route'].path
return path
Output:
/users/{user_id}
I'm working on implementing this for OpenTelemetry and the way to get the original route with the data that's available is as follows:
def get_route_from_request(req):
root_path = req.scope.get("root_path", "")
route = scope.get("route")
if not route:
return None
path_format = getattr(route, "path_format", None)
if path_format:
return f"{route_path}{path_format}"
return None
Note that the accepted answer is not returning what was asked, as it returns the path as received by the server.
None of the other answers deal with mounted apps.
And finally, answers checking the name of the endpoint are also wrong as the same function could be used in different endpoints.
Having found both the answers to not work I'll share what I use.
It's not great as if there's shared values in path params it will not work
path = request.url.path
for key, val in request.path_params.items():
path = path.replace(val, F'{{{key}}}')
You can use the APIRout object property in the request to get the actual path
example:
raw_path = request.scope['route'].path
#'/user/{id}'

If the query string of URL has multi-value parameters like ?name=a&name=b what happened to the second value in flask?

If the query string of URL has multi-value parameters like /obj?name=a&name=b what happened to the second value? Flask by default assumes query parameters to be of single-value.
the example for the parameters to be returned from JSON objects:
obj=[{"name"="a","class"="x"},{"name"="b","class"="y"},{"name"="c","class"="z"}}
then the value returned by request.args.to_dict() is:
{"name"="a","class"="x"}
what happened to the second value?
required output is:
{"name"="a","class"="x"},{"name"="b","class"="y"}
#app.route('/obj', methods=['GET'])
def api_name():
# Check if a name was provided as part of the URL.
# If the name is provided, assign it to a variable.
# If no name is provided, display an error in the browser.
if 'name' in request.args:
name = request.args['name'])
else:
return "Error: No name field provided. Please specify an name."
# Create an empty list for our results
results = []
# Loop through the data and match results that fit the requested name.
# name's are unique, but other fields might return many results
for n in obj:
if n['name'] == name:
results.append(n)
# Use the jsonify function from Flask to convert our list of
# Python dictionaries to the JSON format.
return jsonify(results)
# Get a specific parameter
params=request.args.getlist('name')
it converts the parameter into a list, in this case params=['a','b']

Newbie in Python [2.7] function not working

I am using a script i've downloaded from the web to access our service API.
I am trying to run the function, but keep getting errors no matter what I am trying to do.
from PyBambooHR import PyBambooHR
bamboo = PyBambooHR(subdomain='domain', api_key='apicode')
changes = bamboo.get_employee_changes()
When I run this, I get the following error:
ValueError: Error: since argument must be a datetime.datetime instance
Now, no matter what I set as arguments, I still getting errors. I've also tried the syntax from: https://www.bamboohr.com/api/documentation/changes.php
The function is:
def get_employee_changes(self, since=None):
"""
Returns a list of dictionaries, each with id, action, and lastChanged keys, representing
the employee records that have changed since the datetime object passed in the since= argument.
#return List of dictionaries, each with id, action, and lastChanged keys.
"""
if not isinstance(since, datetime.datetime):
raise ValueError("Error: since argument must be a datetime.datetime instance")
url = self.base_url + 'employees/changed/'
params = {'since': since.strftime('%Y-%m-%dT%H:%M:%SZ')}
r = requests.get(url, params=params, headers=self.headers, auth=(self.api_key, ''))
r.raise_for_status()
return utils.transform_change_list(r.content)
Thanks for your help
As you see in that function is a parameter since of type datetime.datetime expected.
import datetime
changes = bamboo.get_employee_changes(since=datetime.datetime.now() - datetime.timedelta(days=365))
Should give you the changes since last year
Pass a variable of type datetime.datetime while calling the function bamboo.get_employee_changes()

Categories

Resources