GET with and without parameter in flask-restful - flask-restful

The following strikes me as inelegant but it works. Is there a way to have flask-restful handle the two standard flavors of get (i.e. get everything and get one item) using the same resource class?
Thanks.
from flask_restful import Resource
# ...
class People(Resource):
def get(self):
return [{'name': 'John Doe'}, {'name': 'Mary Canary'}]
class Person(Resource):
def get(self, id):
return {'name': 'John Doe'}
# ...
api.add_resource(People, '/api/people')
api.add_resource(Person, '/api/people/<string:id>')

I think this is what you're looking for:
from flask_restful import Resource
# ...
class People(Resource):
def get(self, id=None):
if not id:
return {'name': 'John Doe'}
return [{'name': 'John Doe'}, {'name': 'Mary Canary'}]
api.add_resource(People, '/api/people', '/api/people/<id>')
You can put restrictions on the id buy adding it as an argument to the request parser:
parser.add_argument('id', location='view_args', type=..)

Yeah, check out the argument parsing documentation. If needed, this will give you the flexibility to create some logic around the arguments received with the request.
from flask_restful import Api, Resource, reqparse
# ...
api = Api(app)
parser = reqparse.RequestParser()
parser.add_argument('id')
class People(Resource):
def get(self):
data = parser.parse_args()
if not data['id']:
return [{'name': 'John Doe'}, {'name': 'Mary Canary'}]
else:
# data lookup using id
# for example using SQLAlchemy...
# person = Person.query.filter_by(id = data['id']).first_or_404()
return {'id': data['id'], 'name': 'John Doe'}
api.add_resource(People, '/api/people')
Then try with:
localhost:/api/people
localhost:/api/people?id=1

Related

FastAPI RuntimeError: Use params or add_pagination

I'm writing my second project on FastAPI. And I got this error.
For example I have this code in my routers.users.py:
#router.get('/', response_model=Page[Users])
async def get_all_users(db: Session = Depends(get_db)):
return paginate(db.query(models.User).order_by(models.User.id))
And it works. It has fields limit and page in swagger documentation.
I tried to write the same for routers.recipes.py, but in this case I have no fields for pagination(limit, page) in swagger. Ok, I googled and found out that adding dependencies could help me. And now I see pagination parameters in swagger, but error is still the same.
routers.recipes:
#router.get('/', response_model=Page[PostRecipes], dependencies=[Depends(Params)])
async def get_all_recipes(db: Session = Depends(get_db)):
return paginate(db.query(models.Recipe).order_by(models.Recipe.id))
pagination:
class Params(BaseModel, AbstractParams):
page: int = Query(1, ge=1, description="Page number")
limit: int = Query(50, ge=1, le=100, description="Page size")
def to_raw_params(self) -> RawParams:
return RawParams(
limit=self.limit,
offset=self.limit * (self.page - 1),
)
class Page(BasePage[T], Generic[T]):
page: conint(ge=1) # type: ignore
limit: conint(ge=1) # type: ignore
__params_type__ = Params
#classmethod
def create(
cls,
items: Sequence[T],
total: int,
params: AbstractParams,
) -> Page[T]:
if not isinstance(params, Params):
raise ValueError("Page should be used with Params")
return cls(
total=total,
items=items,
page=params.page,
limit=params.limit,
)
__all__ = [
"Params",
"Page",
]
So, does anyone have ideas about it?
according to doc you have to specify default parameters,
your code should look like paginate(iterable, params)

phonenumbers.PhoneNumber as a FastAPI response_model field

FastAPI supports having some (predefined) classes as pydantic model fields and have them be converted to JSON. For example datetime:
class MyModel(pydantic.BaseModel):
created_at: datetime.datetime
When used this model would convert datetime to/from str in the output/input JSON, when used as a response model or request body model, respectively.
I would like to have similar type safety for my own classes:
class MyModel(pydantic.BaseModel):
phone_number: phonenumbers.PhoneNumber
This can be made to work for request body models by using a custom validator but I also need MyModel to be convertible to JSON. Is this possible to achieve today? Note that I don't control the PhoneNumber class so the solution can't involve modifying that class.
Edit: the best I've come up with but still doesn't work:
def phone_number_validator(value: str) -> phonenumbers.PhoneNumber:
...
class MyModel(pydantic.BaseModel):
phone_number: phonenumbers.PhoneNumber
_validate_phone_number = pydantic.validator(
'phone_number', pre=True, allow_reuse=True)(phone_number_validator)
class Config:
arbitrary_types_allowed = True
json_encoders = {
phonenumbers.PhoneNumber: lambda p: phonenumbers.format_number(
p, phonenumbers.PhoneNumberFormat.E164),
}
This fails in FastAPI with:
fastapi.exceptions.FastAPIError: Invalid args for response field! Hint: check that <class 'phonenumbers.phonenumber.PhoneNumber'> is a valid pydantic field type
As you have already noticed, this is a bug in FastAPI. I just created a PR to fix it.
The arbitrary_types_allowed config directive is lost during the processing of the response model.
Until the PR is merged, you can use the workaround of monkey-patching the Pydantic BaseConfig like this:
from pydantic import BaseConfig
...
BaseConfig.arbitrary_types_allowed = True
# Your routes here:
...
But keep in mind that independent from this bug you might also need to adjust the JSON schema for the custom type, if you want the OpenAPI docs to work properly. Arbitrary types are not generally supported by the BaseModel.schema() method.
For that you can probably just inherit from phonenumbers.PhoneNumber and set a proper __modify_schema__ classmethod. See here for an example. Though I have not looked thoroughly into phonenumbers.
Check the example code in my PR text, if you want to see how you could implement validation and schema modification on your PhoneNumber subclass.
PS
Here is a full working example:
from __future__ import annotations
from typing import Union
from fastapi import FastAPI
from phonenumbers import PhoneNumber as _PhoneNumber
from phonenumbers import NumberParseException, PhoneNumberFormat
from phonenumbers import format_number, is_possible_number, parse
from pydantic import BaseModel, BaseConfig
class PhoneNumber(_PhoneNumber):
#classmethod
def __get_validators__(cls):
yield cls.validate
#classmethod
def validate(cls, v: Union[str, PhoneNumber]) -> PhoneNumber:
if isinstance(v, PhoneNumber):
return v
try:
number = parse(v, None)
except NumberParseException as ex:
raise ValueError(f'Invalid phone number: {v}') from ex
if not is_possible_number(number):
raise ValueError(f'Invalid phone number: {v}')
return number
#classmethod
def __modify_schema__(cls, field_schema: dict) -> None:
field_schema.update(
type="string",
# pattern='^SOMEPATTERN?$',
examples=["+49123456789"],
)
def json_encode(self) -> str:
return format_number(self, PhoneNumberFormat.E164)
class MyModel(BaseModel):
phone_number: PhoneNumber
class Config:
arbitrary_types_allowed = True
json_encoders = {
PhoneNumber: PhoneNumber.json_encode,
}
test_number = PhoneNumber(
country_code=49,
national_number=123456789
)
# Test:
obj = MyModel(phone_number=test_number)
obj_json = obj.json()
parsed_obj = MyModel.parse_raw(obj_json)
assert obj == parsed_obj
BaseConfig.arbitrary_types_allowed = True
api = FastAPI()
#api.get("/model/", response_model=MyModel)
def example_route():
return MyModel(phone_number=test_number)

Fastapi and Pydantic to build POST API: TypeError: Object of type is not JSON serializable

I have a problem with FastAPI and Pydantic.
I want to build a post api, program show this:
#router.post('/productRoute', response_model=SuccessCreate, status_code=status.HTTP_201_CREATED)
async def create_product_route(create: CreatePR):
query = ProductRouteModel.insert().values(
user_id=create.user_id,
route_id=create.route_id,
route_name=create.route_name,
head=create.head.dict(),
body=create.body,
route=create.route
)
await database.execute(query)
return {"status": "Successfully Created!"}
This is Pydantic class:
class RouteSchema(BaseModel):
id: str
next: Optional[List[str]]
prev: List[str]
class HeadSchema(BaseModel):
b1: str
b2: str
b3: str
class BodySchema(BaseModel):
a1: Optional[str]
a2: Optional[str]
class CreatePR(BaseModel):
user_id: str
route_id: str
route_name: str
head: HeadSchema
body: List[BodySchema]
route: List[RouteSchema]
Finally, this i want to post's parameter format:
{
user_id: "test1",
route_id: "route_1",
route_name: "route_name",
head: {...},
body: [{...}, {...}, ..., {...}],
route: [{...}, {...}, ..., {...}]
}
When I executed, I got TypeError: Object of type BodySchema is not JSON serializable.
How can I fix the program to normal operation?
Your code seems OK. I would not make a strong statement, but I suppose your post body is erroneous. Could you please verify whether your JSON format is correct or not. You could check it by using an online JSON editor (ex: https://jsonbeautifier.org/). Probable errors could be the usage of single quotes, missing/extra commas or even perhaps you forgot to put any quotes on your keys.

why this show me 'DNS lookup failed'?

scrapers.here is my code. I am using scrapy basic spider template and I am getting DNS lookup failed error. where is my mistake?
class TopmoviesSpider(scrapy.Spider):
name = 'topmovies'
allowed_domains = ['www.imdb.com']
start_urls = ['https://https://www.imdb.com/chart/top/']
def parse(self, response):
movies = response.xpath("//td[#class='titleColumn']/a")
for movie in movies:
link = movie.xpath(".//#href").get()
yield response.follow(url=link, callback=self.scrape_movie)
def scrape_movie(self,response):
rating = response.xpath("//span[#itemprop='ratingValue']/text()").get()
for mov in response.xpath("//div[#class='title_wrapper']"):
yield {
'title': mov.xpath(".//h1/text()").get(),
'year_of_release': mov.xpath(".//span/a/text()").get(),
'duration': mov.xpath(".//div[#class='subtext']/time/text()").get(),
'genre': mov.xpath(".//div[#class='subtext']/a/text()").get(),
'date_of_release': mov.xpath("//div[#class='subtext']/a[2]/text()"),
'rating': rating
}
Check the start_urls. You had given an invalid url. If you are trying to crawl imdb, check this post.

missing method exceptions with Groovy

I am new to groovy. I have a code like this.
String flavor
HashMap config = new HashMap([ ttl: 0, url: url, appName: appName, enable: true ])
client.put("${data}.json", config)
From this client Map I need to iterate the values of appName and enable.
For that I used get method... I am not sure about this.
def values = client.get("${data}.json");
while using this get method am getting following error. Since I am new to groovy i don't know what is happening here
groovy.lang.MissingMethodException: No signature of method: com.comcast.csv.haxor.SecureFirebaseRestClient.get() is applicable for argument types: (org.codehaus.groovy.runtime.GStringImpl) values: [testJson.json]
Possible solutions: get(com.comcast.tvx.megahttp.utils.URL, java.lang.Class), get(java.lang.String, java.lang.Class), grep(), grep(java.lang.Object), getAt(java.lang.String), wait()
not sure what you are trying to do, but (without knowing other details) I'd put your code that way:
Map config = [ ttl: 0, url: url, appName: appName, enable: true ]
client[ "${data}.json" ] = config
def values = client[ "${data}.json" ]
assuming, that you wanted to use getAt() (short-cut with [] ) method instead of get()
Try this:
def config = [ ttl: 0, url: url, appName: appName, enable: true ]
def endpoint = "${data}.json" as String
client.put(endpoint, config)
def values = client.get(endpoint, HashMap)
def appName = values.appName
def enable = values.enable
I couldn't find any info on SecureFirebaseRestClient, so I'm guessing about how it works.

Resources