Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Moscow Python Meetup №92. Иван Елфимов (Ostrov...

Moscow Python Meetup №92. Иван Елфимов (Ostrovok.ru, Devrel). Пишем свой фреймворк поверх Django

Ваш джанго-проект стал слишком большим? Наскучил DRF? Вместо разработки логики копируете шаблонный код? Мы через все это прошли и знаем решение. Мы пишем свои фреймворки! Это не так сложно как кажется.

Я расскажу вам, как мы организовывали десятки групп API до появления нашего фреймворка, как мы его придумывали, чего стоило перейти на него и какие мы сделали из всего этого выводы.

Видео: https://moscowpython.ru/meetup/92/own-framework-over-django/

Moscow Python: http://moscowpython.ru
Курсы Learn Python: http://learn.python.ru
Moscow Python Podcast: http://podcast.python.ru
Заявки на доклады: https://bit.ly/mp-speaker

Moscow Python Meetup

August 30, 2024
Tweet

More Decks by Moscow Python Meetup

Other Decks in Programming

Transcript

  1. 4 Объявляем billing_api = APIView( name='billing', steps=[ steps.consumer_keys_auth, steps.validation, steps.rate_limit,

    steps.lock, ], consumer_keys=[settings.BILLING_CONSUMER_KEY], ) BillingApiView = billing_api.View billing_api_method = billing_api.method
  2. 6 Не единственный фреймворк в Островке ◦некоторые плотно на DRF

    ◦кто-то смотрит на Django Ninja •иногда копипастят
  3. 6 Не единственный фреймворк в Островке ◦некоторые плотно на DRF

    ◦кто-то смотрит на Django Ninja ◦иногда копипастят •или пишут своё (привет, extrota и crm)
  4. 7 С чего начинается ◦много одинакового кода ◦8 лет копипастим

    200+ строк ◦уже 20 копий декоратора •повторяем ошибки
  5. 7 С чего начинается ◦много одинакового кода ◦8 лет копипастим

    200+ строк ◦уже 20 копий декоратора ◦повторяем ошибки •меняются библиотеки валидации
  6. 7 С чего начинается ◦много одинакового кода ◦8 лет копипастим

    200+ строк ◦уже 20 копий декоратора ◦повторяем ошибки ◦меняются библиотеки валидации •мидлвари в джанге недостаточно гибкие
  7. 8 Old school декоратор def api_method(methods: Optional[list], model_in=None): def wrapper(view):

    @csrf_exempt @wraps(view) def wrapped(request, *args, **kwargs): CHECK METHOD STEP GET DATA RAW STEP AUTH STEP VALIDATION STEP VIEW STEP return wrapped return wrapper
  8. 8 Old school декоратор def api_method(methods: Optional[list], model_in=None): def wrapper(view):

    @csrf_exempt @wraps(view) def wrapped(request, *args, **kwargs): CHECK METHOD STEP GET DATA RAW STEP AUTH STEP VALIDATION STEP VIEW STEP return wrapped return wrapper
  9. 8 Old school декоратор CHECK METHOD STEP GET DATA RAW

    STEP AUTH STEP VALIDATION STEP VIEW STEP def api_method(methods: Optional[list], model_in=None): def wrapper(view): @csrf_exempt @wraps(view) def wrapped(request, *args, **kwargs): return wrapped return wrapper
  10. 9 Другой пример def intranet_api_method(methods: list, endpoint: str, **kwargs): def

    wrapper(view): @csrf_exempt @wraps(view) def wrapped(request, *args, **kwargs): CHECK HELP STEP CHECK METHOD STEP AUTH STEP GET DATA RAW STEP VALIDATION STEP VIEW STEP return wrapped return wrapper
  11. 9 Другой пример def intranet_api_method(methods: list, endpoint: str, **kwargs): def

    wrapper(view): @csrf_exempt @wraps(view) def wrapped(request, *args, **kwargs): CHECK HELP STEP CHECK METHOD STEP AUTH STEP GET DATA RAW STEP VALIDATION STEP VIEW STEP return wrapped return wrapper
  12. 9 Другой пример CHECK HELP STEP CHECK METHOD STEP AUTH

    STEP GET DATA RAW STEP VALIDATION STEP VIEW STEP def intranet_api_method(methods: list, endpoint: str, **kwargs): def wrapper(view): @csrf_exempt @wraps(view) def wrapped(request, *args, **kwargs): return wrapped return wrapper
  13. 9 Другой пример CHECK HELP STEP def intranet_api_method(methods: list, endpoint:

    str, **kwargs): def wrapper(view): @csrf_exempt @wraps(view) def wrapped(request, *args, **kwargs): CHECK METHOD STEP AUTH STEP GET DATA RAW STEP VALIDATION STEP VIEW STEP return wrapped return wrapper
  14. 10 Тем временем в компании ◦стало больше сервисов и проектов

    •нужно вкладываться в логи, метрики и алерты
  15. 10 Тем временем в компании ◦стало больше сервисов и проектов

    ◦нужно вкладываться в логи, метрики и алерты •больше людей → чаще просят доки на апишку
  16. 12 Фабрика декораторов @property def View(self, *args, **kwargs): @method_decorator(csrf_exempt, name='dispatch')

    class _View(DjangoView): def http_method_not_allowed(this, request, *args, **kwargs): return self._error_response(errors.ERROR_METHOD) return _View
  17. 12 Фабрика декораторов class _View(DjangoView): @property def View(self, *args, **kwargs):

    @method_decorator(csrf_exempt, name='dispatch') def http_method_not_allowed(this, request, *args, **kwargs): return self._error_response(errors.ERROR_METHOD) return _View
  18. 14 Гибрид class-based и обычных view def method(self, *args, **kwargs):

    def wrapper(_method): @wraps(_method) def wrapped(view_obj, request: HttpRequest, *m_args, **m_kwargs): ctx = dict( # LOTS OF OTHER ARGS request=request, ) return self._view(view_obj, _method, ctx, *m_args, **m_kwargs) return wrapped return wrapper
  19. 15 Гибрид class-based и обычных view billing_api = APIView( name='billing',

    steps=[], ) BillingApiView = billing_api.View billing_api_method = billing_api.method
  20. 15 Гибрид class-based и обычных view billing_api_method = billing_api.method billing_api

    = APIView( name='billing', steps=[], ) BillingApiView = billing_api.View
  21. 16 Гибрид class-based и обычных view class ContractInfoView(BillingApiView): @billing_api_method() def

    get( self, request: HttpRequest, data_in: ContractInfoIn ) -> ContractInfoOut: # ...
  22. 16 Гибрид class-based и обычных view class ContractInfoView(BillingApiView): @billing_api_method() def

    get( self, request: HttpRequest, data_in: ContractInfoIn ) -> ContractInfoOut: # ...
  23. 16 Гибрид class-based и обычных view @billing_api_method() class ContractInfoView(BillingApiView): def

    get( self, request: HttpRequest, data_in: ContractInfoIn ) -> ContractInfoOut: # ...
  24. 17 Имплементация stories на минималках Фактически Dry Python Stories aka

    Returns (нет) def _view(self, view_obj, method, ctx, *args, **kwargs): for step in self.steps: # METRICS ctx = step(method, ctx) if error := ctx.get('error'): return self._error_response(error, ctx.get('debug'))
  25. 18 Обработчик исключений try: # LOCK MANAGER data_out = method(view_obj,

    request, data_in, *args, **kwargs) status = 'error' if ctx.get('error') else 'ok' # METRICS except LockError: return self._error_response(errors.ERROR_BUSY) except ErrorResponse as e: return e.http_response except RawResponse as e: return e.http_response except Exception as e: return self._error_response(errors.ERROR_UNKNOWN, debug=str(e)) return HttpResponse(json.dumps(data_out.dict()), content_type=CT_JSON)
  26. 19 Возвращать или рейзить ◦raise для ответов с ошибками ◦raise

    для ответов с файлами •return – для ответов 200 OK
  27. 19 Возвращать или рейзить ◦raise для ответов с ошибками ◦raise

    для ответов с файлами ◦return – для ответов 200 OK •ошибки из логики в стиле Go
  28. 20 Набор общих мидлварей steps billing_api = APIView( name='billing', steps=[

    steps.consumer_keys_auth, # (view, ctx: dict) -> dict steps.validation, steps.rate_limit, steps.lock, ], consumer_keys=[settings.BILLING_CONSUMER_KEY], ) BillingApiView = billing_api.View billing_api_method = billing_api.method
  29. 20 Набор общих мидлварей steps steps=[ steps.consumer_keys_auth, # (view, ctx:

    dict) -> dict steps.validation, steps.rate_limit, steps.lock, ], billing_api = APIView( name='billing', consumer_keys=[settings.BILLING_CONSUMER_KEY], ) BillingApiView = billing_api.View billing_api_method = billing_api.method
  30. 23 В итоге ◦сломали почти каждого потребителя ◦зато есть OpenAPI

    спека •наклепали ещё с десяток апишек довольно быстро
  31. 23 В итоге ◦сломали почти каждого потребителя ◦зато есть OpenAPI

    спека ◦наклепали ещё с десяток апишек довольно быстро •быстро вставили трассировку
  32. 23 В итоге ◦сломали почти каждого потребителя ◦зато есть OpenAPI

    спека ◦наклепали ещё с десяток апишек довольно быстро ◦быстро вставили трассировку •структурно не трогали уже 3 года
  33. 25 Возможные вопросы ◦Когда опенсорсить будешь? ◦Какие вообще планы на

    этот фреймворк дальше? ◦После всего этого что бы выбрал в качестве фреймворка? ◦Зачем это вообще? ◦Почему не FastAPI? ◦Почему так нравится Django?
  34. Вопроооосы ◦💼 Карьера в Островке ◦🎬 YouTube-канал «Ostrovok! Tech» ◦🎙️

    Подкаст «Два Ивана (название обсуждается)» ◦📝 t.me/biozz_dev 26