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

Алексей Гончарук – Современный веб с темлейтами...

Алексей Гончарук – Современный веб с темлейтами @ PythoNN

Avatar for Sobolev Nikita

Sobolev Nikita

April 09, 2025
Tweet

More Decks by Sobolev Nikita

Other Decks in Technology

Transcript

  1. ЧЕМ Я ЗАНИМАЮСЬ • Работаю в Газ ИТ • Делаю

    апишки на python • Делаю SPAшки на Vue • Разгребаю хранимки Oracle на PL/SQL
  2. • Flask – слишком маленький • FastAPI – лучше, но

    всё ещё мало • Django – В целом ОК, но с апишками после FastAPI грустно ПОЧЕМУ LITESTAR А НЕ …
  3. Готовые базовые модели from datetime import UTC, datetime from advanced_alchemy.base

    import UUIDBase from advanced_alchemy.types import DateTimeUTC from sqlalchemy.orm import Mapped, mapped_column class Task(UUIDBase): __tablename__ = 'task' name: Mapped[str] = mapped_column(unique=True) description: Mapped[str] is_done: Mapped[bool] = mapped_column(default=False) creation_date: Mapped[datetime] = mapped_column( DateTimeUTC(timezone=True), default=lambda: datetime.now(UTC), )
  4. Интеграция с Litestar from advanced_alchemy.extensions.litestar import ( AsyncSessionConfig, SQLAlchemyAsyncConfig, SQLAlchemyPlugin,

    ) from litestar import Litestar session_config = AsyncSessionConfig( expire_on_commit=False ) sqlalchemy_config = SQLAlchemyAsyncConfig( connection_string='sqlite+aiosqlite:///db.sqlite3', session_config=session_config, create_all=True, ) sqlalchemy_plugin = SQLAlchemyPlugin( config=sqlalchemy_config ) app = Litestar( plugins=[ sqlalchemy_plugin, ], )
  5. Интеграция с Litestar $ litestar database init $ litestar database

    make-migration -m "add_todo_table“ $ litestar run
  6. Паттерн репозиторий from advanced_alchemy.repository import ( SQLAlchemyAsyncRepository, ) from todoshnik.database

    import models class TaskRepository( SQLAlchemyAsyncRepository[models.Task] ): model_type = models.Task uniquify = True
  7. Паттерн сервис from advanced_alchemy.service import ( SQLAlchemyAsyncRepositoryService, ) from todoshnik.database.models

    import Task from todoshnik.database.repository import TaskRepository class TaskService( SQLAlchemyAsyncRepositoryService[ Task, TaskRepository ] ): repository_type = TaskRepository uniquify = True
  8. • Быстрый роутинг за счёт Radix Tree • Быстрый DI

    но неудобный • Классы контроллеры РОУТИНГ
  9. Контроллеры class PagesController(Controller): path = '/page' tags = ['Pages'] dependencies

    = { 'task_service': Provide(provide_task_service), } @get('/tasks', name='Задачи') async def tasks_page( self, task_service: TaskService, ) -> Template: ...
  10. Контроллеры class PagesController(Controller): path = '/page' tags = ['Pages'] dependencies

    = { 'task_service': Provide(provide_task_service), } @get('/tasks', name='Задачи') async def tasks_page( self, task_service: TaskService, ) -> Template: ...
  11. Контроллеры class PagesController(Controller): path = '/page' tags = ['Pages'] dependencies

    = { 'task_service': Provide(provide_task_service), } @get('/tasks', name='Задачи') async def tasks_page( self, task_service: TaskService, ) -> Template: tasks = await task_service.list( models.Task.is_done == False, ) return HTMXTemplate( template_name='pages/tasks.html', context={'tasks': tasks}, )
  12. DTO слой class BaseTask(BaseModel): name: str description: str | None

    is_done: bool = False creation_date: datetime | None = Field( default_factory=lambda: datetime.now(tz=UTC) ) class IdMixin(BaseModel): id: UUID | None = None class TaskIn(BaseTask): pass class TaskOut(IdMixin, BaseTask): pass
  13. DTO слой class Task(BaseModel): id: UUID | None = None

    name: str description: str | None is_done: bool = False creation_date: datetime | None = Field( default_factory=lambda: datetime.now(tz=UTC) ) class TaskDTO(PydanticDTO[Task]): config = DTOConfig(exclude={'id'})
  14. DTO слой @post( path='/api/task', dto=TaskDTO, dependencies={ 'task_service': Provide(provide_task_service), }, )

    async def create_task( task_service: TaskService, data: Annotated[ schemas.Task, Body( media_type=RequestEncodingType.URL_ENCODED ), ], ) -> None: await task_service.create( data=data, auto_commit=True, )
  15. DTO слой @post( path='/api/task', dto=TaskDTO, dependencies={ 'task_service': Provide(provide_task_service), }, )

    async def create_task( task_service: TaskService, data: Annotated[ schemas.Task, Body( media_type=RequestEncodingType.URL_ENCODED ), ], ) -> None: await task_service.create( data=data, auto_commit=True, )
  16. • Шаблонизаторы с поддержкой HTMX • Есть встроенное Observability •

    По тестам TechEmpower быстрее FastAPI • Умеет в каналы • Поддерживается сообществом ЧТО ЕЩЁ ЕСТЬ
  17. • Хорошая событийная система • Подтверждения <button hx-delete="/task" hx-confirm="Delete all?">Delete<button>

    • Индикаторы запросов • Тюнинг заголовками ответов litestar_htmx.HTMXTemplate() • Декларативное описание в самих HTML шаблонах ЧТО ЕЩЁ ЕСТЬ
  18. Варианты утилит // Псевдо классы hover:bg-fuchsia-600 focus:text-red-500 invalid:border-pink-500 // Псевдо

    элементы after:ml-0.5 before:-inset-1 backdrop:bg-gray-50 // Медиа запросы <div class="grid grid-cols-3 md:grid-cols-4"> <!-- ... --> </div>
  19. Варианты утилит // Контейнерные запросы <div class="@container"> <div class="flex flex-col

    @md:flex-row"> <!-- ... --> </div> </div> // Тёмная тема dark:bg-black
  20. Динамические утилиты @import "tailwindcss"; @theme { --font-display: "Satoshi", "sans-serif"; --breakpoint-3xl:

    1920px; --color-avocado-200: oklch(0.98 0.04 113.22); --color-avocado-300: oklch(0.94 0.11 115.03); --color-avocado-400: oklch(0.92 0.19 114.08); --color-avocado-500: oklch(0.84 0.18 117.33); } <div class="text-avocado-500 bg-avocado-300"> Avocado Shop </div>
  21. @apply для шаблонов @layer components { .btn { @apply bg-action

    cursor-pointer rounded-xl p-2 text-base text-white uppercase shadow-xs shadow-black duration-300; } .btn-flat { @apply text-action cursor-pointer rounded-xl bg-transparent p-2 text-base uppercase duration-300; } }
  22. <button _="on click increment :x then put the result into

    the next <output/>" > Click Me </button> <output>--</output> Простой синтаксис
  23. Сахар для читаемости <button _="on click increment :x then put

    it into the next <output/>" > Click Me </button> <output>--</output>
  24. Сахар для читаемости <button _="on click increment :x then put

    the result into the next <output/>" > Click Me </button> <output>--</output>
  25. • Небольшой фреймворк с актуальными батарейками - Litestar • Динамичный

    UI с шаблонами – HTMX • Если вам нужна хорошая кастомизация – TailwindCSS • Немного скриптов для большей динамики - Hyperscript ВЫВОДЫ