Комментарии 50
Много кода и мало текста, люблю такие мануалы =)
Мое ИМХО:
DRF это all-inclusive где с коробки есть почти всё что нужно для большинства сервисов.
Благодаря этому для него есть много плагинов, потому что автор плагина знает "ага раз DRF значить Django ORM, DRF Serializers/Views/etc."
С другой стороны есть Flask компоненты для которого нужно собирать словно кубики лего что требует траты много времени на исследованные всех "кубиков". Они к тому же очень часто не совместимы между собой.
А FastAPI это где то по середине. (ближе к Flask, но со некоторыми фишками)
Я бы пока брал DRF для продакшена.
Тоже проникся им где-то пол-года назад.
Боялся, что будут проблемы, потому что нет популярности, а значит и обмуждения в коммьюнити, но оказалось, что документация настолько хороша, что предугадывает практически все мои вопросы
Слова add и delete нарушают правила наименования restfull api. Стоит их убрать, что бы не вводить в заблуждение тех, кто будет пользоваться туториалом. У post и delete методов можно сделать /phrases или просто слеш.
То, что метод delete и так видно, дополнительное слово не нужно и не по стандарту.
Почему так, можно прочитать, например, тут.
https://restfulapi.net/resource-naming/
Это распространенный миф. Не существует никаких правил именования URI для REST. Все, что написано об именовании URI в этом мануале (и в других) является не больше, чем стилистической рекомендацией.
То, что метод delete и так видно, дополнительное слово не нужно и не по стандарту.
По какому стандарту?) Не бойтесь самостоятельно копнуть глубже, перед тем как открывать кому-то глаза.
По какому стандарту?)
Ну не стандарту, а стандартному стайл гайду. Это ничего не меняет.
Зачем писать:
POST /add
GET /get
DELETE /delete
вместо стандартного:
POST /
GET /
DELETE /
это то же самое что писать коментарии типа: n += 2 # add 2 to n
— никакой новой информации
Но ведь никто не говорит, что такие комментарии нарушают, например, ООП.
Никакие глаголы в URI не нарушают REST. Вопрос стиля URI это ортогональный вопрос вкуса и внутренних соглашений.
Но ведь никто не говорит, что такие комментарии нарушают, например, ООП.
Вы правы, тип ошибки указана не верно. Но всё же она есть, согласны?
Вопрос стиля URI это ортогональный вопрос вкуса и внутренних соглашений.
Есть основной стиль, а есть "все другие".
Чем DELETE /delete
лучше за DELETE /
?
Чем DELETE /delete лучше за DELETE /?
Этот вопрос выходит за рамки REST. Я не хочу показаться ханжой, но поймите одну вещь. Зацикливаясь на необязательных ограничениях по стилистике URI, мы нарушаем одно из обязательных для REST — Uniform Interface, центральной частью которого является т. н. HATEOAS (https://restfulapi.net/hateoas/). Именно грубое нарушение этого ограничения не позволяет большинству API называться RESTful, вне зависимости от того, какой стиль URI используется.
Реализация HATEOAS в API сама по себе является полноценной работой, поэтому не удивительно, что очень мало кто так делает. Да и не всем это действительно нужно, если честно. Проблема лишь в том, что мы зациклены не на тех вещах.
Повторю вопрос:
В стандартном, указаном вами, гайде в разделе в разделе Never use CRUD function names in URIs предлагают делать DELETE /
, но вы защищаете вариант с DELETE /delete
(пожалуйста не расширяйте фокус вопроса. можете даже проигнорировать то что сверху и ответить только на следующий вопрос)
Чем DELETE /delete
лучше за DELETE /
?
Коротко: Никоим образом не нарушая REST, он ничем не лучше.
Во-первых, я не защищаю никакой вариант URI. Во-вторых, вас точно не смущает слово "tips" по ссылке выше? В своем гайде по именованию автор отталкивается от собственных утверждений, не ссылаясь на первоисточники. Проблема всех таких гайдов в том, что они дают противоречивую информацию, не согласуясь даже друг с другом. Один запрещает использовать глаголы в URI; второй разрешает только те, которые нельзя выразить с помощью HTTP-методов, используя для этого свои понятия, которые отсутствуют в REST. Другие же честно разделяют обязательные и необязательные ограничения. Это порождает просто чудовищную путаницу в головах, превращая REST в хреновину, которую никто не может может толком описать.
вас точно не смущает слово "tips" по ссылке выше?
Нет.
Один запрещает использовать глаголы в URI; второй разрешает только те, которые нельзя выразить с помощью HTTP-методов, используя для этого свои понятия, которые отсутствуют в REST. Другие же честно разделяют обязательные и необязательные ограничения. Это порождает просто чудовищную путаницу в головах, превращая REST в хреновину, которую никто не может может толком описать.
Не согласен, но сейчас не об этом.
он ничем не лучше.
Я скажу чем хуже: +одно слово +0 информации (не я один такой умный, поэтому, это уже описали в этих "tips")
Вы согласны?
Согласен. Какое это имеет отношение к REST?
Лично я говорил только о DELETE /delete
vs DELETE /
.
И мы сошлисть на том, что пункт "Never use CRUD function names in URIs" оправдан. Можно пройтись и по другим пунктам.
Это всё что я хотел сказать.
https://developer.twitter.com/en/docs/api-reference-index.html
https://api.slack.com/methods
https://www.flickr.com/services/api/
https://vk.com/dev/methods
https://core.telegram.org/methods
Увы, не оправдан. Но перо скрипит, бумага терпит)
The problem is that various people have described “I am using HTTP” as some sort of style in itself and then used the REST moniker for branding (or excuses) even when they haven’t the slightest idea what it means. The only way to stop people from misusing the term is to make a negative example of them when they do.
https://roy.gbiv.com/untangled/2009/it-is-okay-to-use-post#comment-996
Это очень узкая трактовка термина. Архитектура очень раннего веба (начало 1990 годов, еще до стандартизации HTTP/1.1) действительно определяла URI как идентификаторы документов, но такое определение оказалось неудовлетворительным и было отброшено. Любая адресуемая концепция вписывается в это понятие.
Чтобы не повторятся, можете прочитать https://habr.com/post/476576/#comment_20949240
Так и rest "не существует", если так рассуждать. Это просто правила, о которых люди договорились. К тому же не все, а некоторые. Вас же никто не заставляет их соблюдать. Но если вы хотите соблюдать и сделать именно rest api, тогда использовать delete или add неуместно.
И умерьте пафос, по возможности.
Чтобы долго не тошнить в комментариях, я задам несколько вопросов, а заодно кину несколько цитат от автора REST для дальнейших размышлений.
- Что именно нарушают /add, /crop, /verify, /withdraw, глобальный /search в REST?
- Как именно должны быть переименованы такие идентификаторы?
- Кто именно создал такие требования?
A REST API must not define fixed resource names or hierarchies (an obvious coupling of client and server). Servers must have the freedom to control their own namespace. Instead, allow servers to instruct clients on how to construct appropriate URIs, such as is done in HTML forms and URI templates, by defining those instructions within media types and link relations.
http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
There is no such thing as a REST endpoint. There are resources. A countably infinite set of resources bound only by restrictions on URL length. A client can POST to a REST service to create a resource that is a GraphQL query, and then GET that resource with all benefits of REST [...]
https://twitter.com/fielding/status/1052976631374000128
At no time whatsoever do the server or client software need to know or understand the meaning of a URI — they merely act as a conduit through which the creator of a resource (a human naming authority) can associate representations with the semantics identified by the URI.
https://www.ics.uci.edu/~fielding/pubs/dissertation/evaluation.htm#sec_6_2_4
REST accomplishes this by defining a resource to be the semantics of what the author intends to identify, rather than the value corresponding to those semantics at the time the reference is created.
https://www.ics.uci.edu/~fielding/pubs/dissertation/evaluation.htm#sec_6_2_1
/crop, /verify, /withdraw не нарушают ничего. В коротком документе, на который я дал ссылку, это описано, если вам интересно, прочитайте.
Цитаты — вода (что и не удивительно, диссертация же). Общий смысл — делайте так, как нужно для вашего приложения.
Что ж, соглашусь, если для вашего приложения действительно нужно два раза повторить слово delete и вы даже сможете это аргументировать, то пожалуйста. Если у вас таких аргументов нет, то на этом можно закончить.
Общий смысл — делайте так, как нужно для вашего приложения.
А когда-то было по другому?
Выбирая архитектуру/архитектурный стиль для своего приложения, вы полагаетесь на то получение какого-то профита от этого, а не просто для красивого словца берёте(надеюсь).
Если конкретный архитектурный стиль вам не подходит — вы его не берёте. Но зачем пытаться сделать по другому, явно указывая что ограничения REST вам не нужны, и в то же время продолжать называть это REST, ещё и производя подмену понятий?
Просто фраза «делайте так, как нужно для вашего приложения» не может представлять собой архитектурный стиль.
Цитаты — вода (что и не удивительно, диссертация же).
Из приведённых выше цитат только две из диссертации, и даже их я бы водой не назвал. Просто вы вряд ли являетесь целевой аудиторией сего документа(и я тоже).
А что с перфом?
не helloworld перфом, а с нормальной нагрузкой — доставать данные по сети, совершать обсчет каких-нибудь матриц ( не очень больших, но всё равно матриц)
Более подробные бенчмарки тык.
По поводу обсчёта матриц — если это нужно сделать в роуте который ничего асинхронно не делает, то можно маркнуть его как обычный def
, а не async def
, тогда FastAPI запустит его в тредпуле. А если всё таки хочется и сделать запрос с помощью aiohttp и посчитать что-то, то можно использовать такой код (WARNING: не тестировал, но уверен что работает):
from starlette.concurrency import run_in_threadpool
@app.get(...)
async def some(..):
await do_async_request()
result = await run_in_threadpool(compute_some(123))
Тут тоже всё не так однозначно: если нужно что-то посчитать, и числодробилка потенциально не высвобождает GIL, то всё равно выйдет блокирующая CPU-bound операция, и по-хорошему нужно либо вызвать loop.set_default_executor с инстансом ProcessPoolExecutor, либо спуститься на уровень ниже к loop.run_in_executor и делать ровно то же самое.
Конечно при условии, что это реально нужно, а не преждевременная оптимизация.
Есть какой-нибудь рабочий образец?
С обычным sqlite работает норм:
from typing import List
import databases
import sqlalchemy
from fastapi import FastAPI
from pydantic import BaseModel
# SQLAlchemy specific code, as with any other app
DATABASE_URL = "sqlite:///./test.db"
database = databases.Database(DATABASE_URL)
metadata = sqlalchemy.MetaData()
notes = sqlalchemy.Table(
"notes",
metadata,
sqlalchemy.Column("id", sqlalchemy.Integer, primary_key=True),
sqlalchemy.Column("text", sqlalchemy.String),
sqlalchemy.Column("completed", sqlalchemy.Boolean),
)
engine = sqlalchemy.create_engine(
DATABASE_URL, connect_args={"check_same_thread": False}
)
metadata.create_all(engine)
class NoteIn(BaseModel):
text: str
completed: bool
class Note(BaseModel):
id: int
text: str
completed: bool
app = FastAPI()
@app.on_event("startup")
async def startup():
await database.connect()
@app.on_event("shutdown")
async def shutdown():
await database.disconnect()
@app.get("/notes/", response_model=List[Note])
async def read_notes():
query = notes.select()
return await database.fetch_all(query)
@app.post("/notes/", response_model=Note)
async def create_note(note: NoteIn):
query = notes.insert().values(text=note.text, completed=note.completed)
last_record_id = await database.execute(query)
return {**note.dict(), "id": last_record_id}
Подсовываю в
DATABASE_URL = "mysql://mydb:welcome@localhost/mydb"
И пишет, мол ModuleNotFoundError: No module named 'MySQLdb'
Вот мы видим очередной фреймворк (а то ж мы страдали, что мало фреймворков), и чо? Сильно ли оно поможет условному Васе Пупкину, developer-у, ускорить и обезглючить dev?
Для примера, доки на asyncio начинаются (ну надо же!!!) со слов: Why use asyncio?
И это правильно, товарищи. Условный Вася Пупкин, developer, для начала должен понять: why? Почему следует считать бесцельно прожитими все те годы, когда он не юзал фреймворк?
И уж только после этого начинать тратить годы жизни (если это можно назвать жизнью) на изучение этой
и никто че-то не берется сравнить те фреймворки, которые на первых местах были еще в начале 2019-го года, ну странновато
Чем FastAPI может привлечь мое внимание на фоне указанных модулей?
Это не вопрос с кандибобером, мне действительно интересно, стоит ли переходить на использование FastAPI с вышеперечисленного?
Вот даже задал вопрос на тостере:
qna.habr.com/q/692597
То есть, концептуально, кроме «быстроты» ничего нового? Но я ведь правильно понимаю, что если надо что-то тяжелое достать или обработать, то можно использовать условный Celery и не париться?
1) Flask задумывался как микро фреймворк и лишь позже там появились модули отвечающие за restfull, и они ещё между собой спорят, кто лучше.
А fastapi уже на уровне архитектуры сразу rest.
2) Я согласен с вот этим комментом цитата:
Нафиг это фласк в 2019. Тогда уж лучше просто drf взять.
Есть aiomongo и aiohttp. Вот пример api на нем dev.to/apcelent/how-to-create-rest-api-using-aiohttp-54p1
3) Отличная документация у fastapi, читаешь как роман.
4) Очень понравилась работа с параметрами, которая идёт из коробки.
5) Никаких blueprint, которые дико бесят во фляшке. Разбивание на файлы, роуты и тд работает тоже из коробки.
Ну вот как-то так, но я пока с fastapi только разбираюсь и кое-что пишу на нем чисто для себя.
Я же порекомендую связку aiohttp + jsonschema (для валидации схем в openapi) + iko (асинхронный аналог Marshmallow).
Замечу что асинхронный код писать сложнее, и нет устоявшихся решений для restapi.
Если передаю в FastAPI текст типа:
«Trevoga sozdana
Aktivnaya Vyezd tehniki za pridely bazy
Duster»
, то получаю такую ошибку:
doc": "{\n \«secret_key\»: \"\",\n \«message_id\»: \«123123\»,\n \«message_text\»: \«Trevoga sozdana\nAktivnaya Vyezd tehniki za pridely bazy\nDuster\»\n}",
то есть он не хочет парсить строку, в которой есть \n (перенос строки).
Как это можно исправить?
Спасибо
Почему Вы должны попробовать FastAPI?