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
160
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
250
ИИ-Агенты в каждый дом – Алексей Порядин, PythoNN
sobolevn
0
220
Внутреннее устройство сборки мусора в CPython 3.14+ – Сергей Мирянов, PythoNN
sobolevn
0
140
Генератор байткода и байткод генератора, Михаил Ефимов, PythoNN
sobolevn
0
140
Дотянуться до кремния. HighLoad Python: SIMD, GPU – Пётр Андреев, PythoNN
sobolevn
0
120
Проектирование — это когда чувствуешь, а не какие-то там циферки, Николай Хитров, PythoNN
sobolevn
0
140
Continuous profiling, Давид Джалаев, PythoNN
sobolevn
0
180
Михаил Гурбанов – Are you NATS? @ PythoNN
sobolevn
0
330
Дмитрий Бровкин – Почему исправление опечаток сложнее, чем кажется, и как мы с этим српавляемся @ PythoNN
sobolevn
0
75
Other Decks in Technology
See All in Technology
薬剤師(ドメインエキスパート)と一緒に育てる薬局向けAIアシスタント
kakehashi
PRO
2
120
全員がプロダクトへ向き合う組織を持続成長させるために——組織づくりのフライホイールと4象限 / The Flywheel Model and Four Quadrants for Organizational Design
hiro_torii
4
900
AIで実装は速くなった。なのにプロダクトは速くならない。職能の壁を越えて価値のフローを設計する
nwiizo
7
7.4k
Azure Cost Management の FOCUS コストデータを迷わず読むための“3つの軸”
tetsuyaooooo
0
150
こんなアーキテクチャ図は嫌だ BEYOND THE TIME: 半年後の自分へ贈る15のメッセージ / 15 of Anti-pattern in AWS Architecture Diagrams
naospon
3
260
多層防御と最⼩権限で実現する、安全なAIエージェント設計パターン
lycorptech_jp
PRO
0
210
振り返りこそエンジニアの本領
negima
0
330
Bet AI Day 2026丨Agentは、「金融」という巨大産業の何を変えられるのか
layerx
PRO
0
1.2k
Guerilla InnerSource in enterprises, during the AI hype
onenashev
PRO
0
150
『止めない』を設計する — 制約の中で、事業の根幹を支える判断
hiroyaterui
0
220
Driving AI Adoption Using In-House GPUs to Serve Qwen
po3rin
2
460
Kiro Crewしか勝たん!?
miu_crescent
PRO
0
190
Featured
See All Featured
Mind Mapping
helmedeiros
1
350
Java REST API Framework Comparison - PWX 2021
mraible
34
9.7k
Prompt Engineering for Job Search
mfonobong
0
440
The State of eCommerce SEO: How to Win in Today's Products SERPs - #SEOweek
aleyda
2
11k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
4.2k
Bash Introduction
62gerente
615
220k
Utilizing Notion as your number one productivity tool
mfonobong
4
570
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.6k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
The Art of Programming - Codeland 2020
erikaheidi
57
14k
Thoughts on Productivity
jonyablonski
76
5.4k
30 Presentation Tips
portentint
PRO
1
390
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 ВЫВОДЫ
КОНТАКТЫ