Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Алексей Гончарук – Современный веб с темлейтами...
Search
Sobolev Nikita
April 09, 2025
Technology
150
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Алексей Гончарук – Современный веб с темлейтами @ PythoNN
Sobolev Nikita
April 09, 2025
More Decks by Sobolev Nikita
See All by Sobolev Nikita
Чего вы не знали о строках в Python – Василий Рябов, PythoNN
sobolevn
0
240
ИИ-Агенты в каждый дом – Алексей Порядин, PythoNN
sobolevn
0
220
Внутреннее устройство сборки мусора в CPython 3.14+ – Сергей Мирянов, PythoNN
sobolevn
0
130
Генератор байткода и байткод генератора, Михаил Ефимов, PythoNN
sobolevn
0
130
Дотянуться до кремния. HighLoad Python: SIMD, GPU – Пётр Андреев, PythoNN
sobolevn
0
120
Проектирование — это когда чувствуешь, а не какие-то там циферки, Николай Хитров, PythoNN
sobolevn
0
140
Continuous profiling, Давид Джалаев, PythoNN
sobolevn
0
160
Михаил Гурбанов – Are you NATS? @ PythoNN
sobolevn
0
320
Дмитрий Бровкин – Почему исправление опечаток сложнее, чем кажется, и как мы с этим српавляемся @ PythoNN
sobolevn
0
69
Other Decks in Technology
See All in Technology
13年運用タイトルのサーバーサイドが辿り着いた現在地 ― モンスターストライクにおける技術・組織・AI活用から得た知見
mixi_engineers
PRO
1
320
現場で使える AWS DevOps Agent 活用ノウハウ - Release Management 機能の検証結果を添えて / AWS DevOps Agent Release Management and Know-How
kinunori
5
770
AIがAPIを書く時代に、私たちは何を設計すべきか
nagix
0
160
事業成長とAI活用を止めないデータ基盤アーキテクチャの設計思想
hiracky16
0
770
AI工学特論: MLOps・継続的評価
asei
11
3k
LLMリーダーボードアップデートに向けたAgentic Math_SWEのトレースについて
nejumi
0
130
NYC Summit 2026 におけるAmazon Bedrock AgentCore のアップデート
ren8k
2
270
plamo-3-translateの開発
pfn
PRO
0
170
ウォーターフォール開発案件のPMとしてAI活用を模索している話
hatahata021
2
200
自己解決や回答速度を上げる、サポート業務へのAIの組み込み方【SORACOM Discovery 2026】
soracom
PRO
0
110
AI時代の強いチームの作り方
yuukiyo
6
4.7k
運用を犠牲にせずコストを制御し事業成長を支える B2B SaaS ID管理基盤におけるS3 Tableのログストレージ活用
kaminashi
1
110
Featured
See All Featured
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
3
370
ラッコキーワード サービス紹介資料
rakko
1
4.1M
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
260
GraphQLの誤解/rethinking-graphql
sonatard
75
12k
Reflections from 52 weeks, 52 projects
jeffersonlam
356
21k
Utilizing Notion as your number one productivity tool
mfonobong
4
480
Google's AI Overviews - The New Search
badams
0
1.1k
Color Theory Basics | Prateek | Gurzu
gurzu
0
400
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
1
2.8k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
118
120k
Faster Mobile Websites
deanohume
310
32k
Designing Powerful Visuals for Engaging Learning
tmiket
1
470
Transcript
СОВРЕМЕННЫЙ WEB С СОВРЕМЕННЫМИ ТЕМПЛЕЙТАМИ Алексей Гончарук Газ ИТ
ЧЕМ Я ЗАНИМАЮСЬ • Работаю в Газ ИТ • Делаю
апишки на python • Делаю SPAшки на Vue • Разгребаю хранимки Oracle на PL/SQL
ЧТО МЫ ХОТИМ ПОЛУЧИТЬ
ИСХОДНЫЙ КОД
Между Django и FastAPI
• Flask – слишком маленький • FastAPI – лучше, но
всё ещё мало • Django – В целом ОК, но с апишками после FastAPI грустно ПОЧЕМУ LITESTAR А НЕ …
• Готовые базовые модели • Настроенный DI • Миграции •
Репозитории и Сервисы ADVANCED ALCHEMY
Готовые базовые модели 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), )
Интеграция с 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, ], )
Интеграция с Litestar $ litestar database init $ litestar database
make-migration -m "add_todo_table“ $ litestar run
Паттерн репозиторий from advanced_alchemy.repository import ( SQLAlchemyAsyncRepository, ) from todoshnik.database
import models class TaskRepository( SQLAlchemyAsyncRepository[models.Task] ): model_type = models.Task uniquify = True
Паттерн сервис 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
• Быстрый роутинг за счёт Radix Tree • Быстрый DI
но неудобный • Классы контроллеры РОУТИНГ
Контроллеры 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: ...
Контроллеры 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: ...
Контроллеры 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}, )
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
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'})
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, )
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, )
• Шаблонизаторы с поддержкой HTMX • Есть встроенное Observability •
По тестам TechEmpower быстрее FastAPI • Умеет в каналы • Поддерживается сообществом ЧТО ЕЩЁ ЕСТЬ
ГРУСТНЫЙ СЛАЙД
</>htmx Настоящий hypermedia
Бустинг HTML <div hx-boost="true"> <a href="/page1">Go To Page 1</a> <a
href="/page2">Go To Page 2</a> </div>
Бустинг HTML <form action="/task" method="post" hx-boost="true"> <input type="text" name="task" />
<button>Post Your Task</button> </form>
Больше методов <form action="/task/{{ task.id }}/delete" method="post"> <button>Delete Task</button> </form>
Больше методов <button hx-delete="/task/{{ task.id }}"> Delete Task </button>
Выбор цели <button hx-delete="/task/{{ task.id }}" hx-target="#task-{{ task.id }}" >
Delete Task </button>
Способ замены <button hx-delete="/task/{{ task.id }}" hx-target="#task-{{ task.id }}" hx-swap="outerHTML"
> Delete Task </button>
Способ замены <button hx-post="/task" hx-target="#tasks-list" hx-swap="beforeend" > Save Task </button>
• Хорошая событийная система • Подтверждения <button hx-delete="/task" hx-confirm="Delete all?">Delete<button>
• Индикаторы запросов • Тюнинг заголовками ответов litestar_htmx.HTMXTemplate() • Декларативное описание в самих HTML шаблонах ЧТО ЕЩЁ ЕСТЬ
Сделать красиво
Варианты утилит // Псевдо классы 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>
Варианты утилит // Контейнерные запросы <div class="@container"> <div class="flex flex-col
@md:flex-row"> <!-- ... --> </div> </div> // Тёмная тема dark:bg-black
Динамические утилиты @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>
@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; } }
///_hyperscript Как JQuery, только нормальный
<button _="on click increment :x then put the result into
the next <output/>" > Click Me </button> <output>--</output> Простой синтаксис
Сахар для читаемости <button _="on click increment :x then put
it into the next <output/>" > Click Me </button> <output>--</output>
Сахар для читаемости <button _="on click increment :x then put
the result into the next <output/>" > Click Me </button> <output>--</output>
Декларативный подход document.querySelector('#example-btn') .addEventListener('click', e => { document .querySelectorAll(".elements-to-remove") .forEach(value
=> value.remove()); }) on click from #example-btn remove .elements-to-remove
• Небольшой фреймворк с актуальными батарейками - Litestar • Динамичный
UI с шаблонами – HTMX • Если вам нужна хорошая кастомизация – TailwindCSS • Немного скриптов для большей динамики - Hyperscript ВЫВОДЫ
КОНТАКТЫ