Машинный перевод под контролем человека
Этот контент переведён с помощью машинной генерации, направляемой составленными людьми глоссариями и руководствами по стилю. Поскольку текст не проверяется вручную построчно, возможны отдельные ошибки или неестественные формулировки.
В случае любых расхождений авторитетным источником считается оригинальная версия на английском языке.
Руководство по миграции
На этой странице собраны инструкции по обновлению между релизами starlette-admin. Перейдите к разделу, соответствующему версии, с которой вы выполняете обновление.
С 0.17.x до 1.0.0
В этом релизе переработана внутренняя архитектура starlette-admin и добавлен большой набор новых возможностей. Хотя высокоуровневый API в целом остался прежним, наиболее значимым изменением стала переработка отрисовки страницы списка: мы отказались от DataTables в пользу таблицы, формируемой на сервере. Большинство остальных изменений сводится к переименованиям или незначительным изменениям сигнатур.
Это руководство охватывает все критические изменения в порядке, в котором вы с наибольшей вероятностью с ними столкнётесь. В каждом разделе старый API сравнивается с его заменой. Если ваша реализация опирается на базовые функции или лёгкие кастомизации (например, экземпляр Admin, несколько подклассов ModelView, fields и searchable_fields), миграция, скорее всего, ограничится разделами Требования и Конструктор Admin, а также несколькими переименованиями.
Кастомизации старой страницы списка требуют наибольшего внимания. Опции DataTables и JavaScript-функции отрисовки не имеют прямых аналогов и должны быть перенесены в серверные шаблоны (см. раздел Удаление DataTables).
Tip
Обновите зависимости за один шаг и запустите приложение. Большинство удалённых или переименованных атрибутов вызывают понятные ошибки при запуске, а не молча ломают работу во время выполнения.
Что нового
Помимо критических изменений, описанных ниже, этот релиз включает:
- Нативные таблицы списков: DataTables удалён в пользу встроенной реализации с отрисовкой на сервере. Состояние таблицы теперь полностью определяется URL, то есть все настройки страницы, фильтров и сортировки можно сразу передавать по ссылке и сохранять в закладки.
- Фильтры: вложенный конструктор фильтров
AND/ORзаменяет SearchBuilder из DataTables. Фильтры выводятся из типов полей и полностью расширяемы на чистом Python. Вы можете написать класс фильтра без какого-либо JavaScript. - Импорт и экспорт на стороне сервера: импорт данных из CSV, JSON, Excel и других форматов с отчётом об ошибках по каждой строке, а также серверные экспортёры (CSV, JSON, Excel, PDF и т. д.), заменяющие клиентские кнопки DataTables.
- События: подписка на хуки жизненного цикла, такие как
before_create,after_edit_committed,after_login, и на различные события действий. - Темы и плагины: упаковка и повторное использование пользовательского оформления и поведения. Доступны шаблоны Cookiecutter, чтобы быстро начать работу.
- Виджеты и дашборды: создание индексных страниц и пользовательских представлений с помощью
StatWidget,ChartWidget,TableWidgetи других. - Компоновка форм: логичное расположение форм создания/редактирования с помощью строк, колонок, fieldset'ов и вкладок.
- Инлайн-редактирование: редактирование отдельного поля прямо со страницы списка.
- Инлайн-формы: редактирование связанных моделей внутри родительской формы с помощью
InlineModelView. - Прочие улучшения: flash-сообщения, вход через OAuth, backend для Tortoise ORM, новые поля (
ComputedField,SlugField,UUIDField,IPAddressField), валидаторы на уровне полей и копирование в буфер обмена для любого поля. - Логирование: пакет теперь ведёт внутренние логи в пространстве имён
starlette_admin, по умолчанию отключённые. ПередайтеAdmin(debug=True)или вызовитеstarlette_admin.logging.configure_logging(), чтобы видеть маршрутизацию запросов, работу middleware и решения о правах доступа в консоли — это особенно удобно при миграции. - Расширенное тестовое покрытие: набор тестов существенно вырос, включая end-to-end тесты на Playwright, проверяющие ключевые сценарии работы административного интерфейса.
- Более лёгкий пакет: размер публикуемого пакета на PyPI сокращён примерно на 50%.
Требования
- Поддержка Python: требуется Python 3.11 или новее. Поддержка Python 3.9 и 3.10 прекращена.
- Основные зависимости:
itsdangerousтеперь является основной зависимостью, используемой для подписи cookie административной панели (CSRF-токены и flash-сообщения). -
Новые необязательные extras:
Extra Что включает starlette-admin[email]серверная валидация EmailFieldчерезemail-validatorstarlette-admin[pdf]экспорт PDF через reportlabstarlette-admin[s3]файловое хранилище S3 через aiobotocorestarlette-admin[tinymce]санитизация HTML в TinyMCEEditorFieldчерезnh3starlette-admin[i18n]переводы через babel(без изменений) -
Backend Beanie: требуется Beanie 2.0+.
- Backend Odmantic: удалён. Если вы от него зависите, оставайтесь на
starlette-admin<=0.17.1и выразите заинтересованность, создав issue; поддержка может быть возвращена при достаточном спросе.
Конструктор Admin
# Before
admin = Admin(engine, statics_dir="statics")
# After
admin = Admin(engine, static_dir="statics", secret_key=os.environ["ADMIN_SECRET_KEY"])
- Параметр
statics_dirпереименован вstatic_dir. - Задайте
secret_key. Этот ключ подписывает cookie CSRF и flash-сообщений. Если он не указан, при запуске генерируется случайный ключ (что допустимо для разработки). Однако подписанные значения будут инвалидироваться при каждом перезапуске и между несколькими воркерами. В продакшене всегда передавайте стабильный секретный ключ. logo_url,login_logo_urlиfavicon_urlтеперь принимают callable вида(request) -> str | None. Это заменяет брендирование per-request, которое ранее предоставлялось черезAdminConfig.- Новые необязательные параметры:
theme,plugins,additional_loaders,import_configиexport_config. - Особенности SQLAlchemy: первый аргумент теперь называется
session_provider. Он принимаетEngineилиAsyncEngine, а такжеsessionmakerилиasync_sessionmaker. Существующие вызовы видаAdmin(engine)продолжат работать. timezone_configпо умолчанию равенTimezoneConfig()вместоNone. Значения datetime теперь по умолчанию отображаются в локальном часовом поясе пользователя. Передайтеtimezone_config=None, чтобы сохранить исходные значения.
Переименованные идентификаторы представлений
Соглашение об именовании представлений унифицировано. Обновите конструкторы ModelView и атрибуты классов соответствующим образом:
| Было | Стало |
|---|---|
identity |
key |
name |
display_name |
label |
menu_label |
form_include_pk |
show_pk_in_forms |
# Before
admin.add_view(PostView(Post, identity="post", name="Post", label="Posts"))
# After
admin.add_view(PostView(Post, key="post", display_name="Post", menu_label="Posts"))
Обратите внимание, что Link и DropDown также используют menu_label вместо label.
Удаление DataTables
Страница списка больше не использует DataTables. Атрибуты, которые ранее его настраивали, полностью удалены:
| Удалено | Замена |
|---|---|
datatables_options |
Нет. Таблица формируется на сервере. Настраивайте через шаблоны. |
search_builder |
Новый конструктор фильтров, включаемый через searchable_fields. |
responsive_table |
Нет. Таблица нативно обрабатывает переполнение. |
save_state |
Всегда включено. Состояние списка (страница, сортировка, фильтры, поиск, видимые колонки) теперь хранится в URL. |
BaseField.search_builder_type |
BaseField.filters (список классов фильтров). |
BaseField.render_function_key |
BaseField.list_template (серверный шаблон Jinja). |
Если вы ранее писали собственные JavaScript-функции отрисовки или плагины DataTables, перенесите их в переопределения list_template. Теперь каждая ячейка списка рендерится напрямую из шаблона templates/fields/list/*.html.
Действия
Обработчики batch-действий теперь получают объект ActionSelection вместо списка первичных ключей. Это поддерживает новый баннер «выбрать все подходящие», позволяющий применить действие ко всем строкам, соответствующим текущему фильтру, без их материализации на клиенте.
# Before
@action(name="publish", text="Publish")
async def publish_action(self, request: Request, pks: List[Any]) -> str:
for article in await self.find_by_pks(request, pks):
...
return f"{len(pks)} articles were published"
# After
@action(name="publish", text="Publish")
async def publish_action(self, request: Request, selection: ActionSelection) -> None:
for article in await selection.rows():
...
flash(request, f"{await selection.count()} articles were published")
- Методы вроде
selection.rows(),selection.pks()иselection.count()разрешают целевые строки лениво. Это работает как при индивидуальной отметке строк пользователем, так и при выборе всех подходящих строк. - Свойства вроде
selection.is_select_all,selection.filtersиselection.qпозволяют выполнить операцию одним bulk-запросом. - Возврат строки с сообщением об успехе заменён flash-сообщениями.
- Обработчики row action сохраняют исходную сигнатуру
(request, pk). - Новые опции
@action:header,allow_empty_selection,dedicated_button,modal_sizeи callable-параметрformдля каждого запроса.
Аутентификация
Модуль starlette_admin/auth.py теперь является пакетом starlette_admin.auth. Существующие импорты из starlette_admin.auth продолжат работать, однако контракт провайдера изменился.
# Before
class MyAuthProvider(AuthProvider):
async def login(self, username, password, remember_me, request, response):
request.session.update({"username": username})
return response
async def logout(self, request, response):
request.session.clear()
return response
async def is_authenticated(self, request) -> bool:
request.state.user = my_users_db.get(request.session.get("username"))
return request.state.user is not None
def get_admin_user(self, request) -> AdminUser:
return AdminUser(username=request.state.user["name"])
def get_admin_config(self, request) -> AdminConfig:
return AdminConfig(app_title="My Admin")
# After
class MyAuthProvider(AuthProvider):
async def login(self, username, password, remember_me, request):
if username in my_users_db:
request.session.update({"username": username})
return None # default redirect (`next` param or admin index)
raise LoginFailed("Invalid username or password")
async def logout(self, request):
request.session.clear()
async def authenticate(self, request) -> AdminUser | None:
user = my_users_db.get(request.session.get("username"))
return AdminUser(username=user["name"]) if user else None
- Методы
is_authenticated,get_admin_userиget_admin_configобъединены в один методauthenticate(request) -> AdminUser | None. ВозвратNoneозначает неаутентифицированное состояние. - Методы
loginиlogoutбольше не получают и не возвращают подготовленныйresponse. ВернитеNoneдля стандартного редиректа или верните собственныйResponse, чтобы переопределить это поведение. AdminConfigудалён. Управляйте заголовками и логотипами для каждого запроса через callable-формы параметровlogo_urlиlogin_logo_urlэкземпляраAdmin.- Встроенный
OAuthProviderподдерживает сценарии входа OAuth2/OIDC «из коробки». - Декоратор
login_not_requiredостался без изменений.
Экспорт и импорт
Экспорт переехал с клиентских кнопок DataTables на серверные streaming-endpoint'ы. Функциональность импорта совершенно новая, а ExportType больше не существует.
# Before
from starlette_admin import ExportType
class PostView(ModelView):
export_types = [ExportType.CSV, ExportType.EXCEL]
export_fields = ["id", "title"]
# After
class PostView(ModelView):
exporters = ["csv", "xlsx"]
importers = ["csv", "json"]
exclude_fields_from_export = ["content"]
exclude_fields_from_import = ["id"]
export_typesзаменён наexporters. Он принимает список названий форматов или экземпляровBaseExporter. Из встроенных поддерживаютсяcsv,json,tsv,xlsx,ods,html,yamlиpdf. Для форматов, отличных отcsvилиjson, требуетсяtablib, а для PDF — extrapdf.importersпринимает список названий форматов или экземпляровBaseImporter. Из встроенных поддерживаютсяcsv,tsv,json,yaml,xlsx,xls,ods,dbfиhtml. Для форматов, отличных отcsv,tsvилиjson, требуетсяtablib.export_fields(список включения) заменён наexclude_fields_from_export(список исключения). Это соответствует соглашению об именовании остальных атрибутовexclude_fields_from_*.- Отдельные поля также принимают параметры
exclude_from_exportиexclude_from_import. - Настройте глобальные лимиты с помощью
ExportConfigиImportConfigна экземпляреAdmin. Подробности см. в документации Export & Import.
Пользовательские поля и переопределение шаблонов
Шаблоны полей реорганизованы. Обновите пути, если вы переопределяете встроенные шаблоны или поставляете собственные поля:
| Было | Стало |
|---|---|
templates/displays/*.html |
templates/fields/detail/*.html |
templates/forms/*.html |
templates/fields/form/*.html |
| (клиентская функция отрисовки) | templates/fields/list/*.html |
BaseField.display_template |
BaseField.detail_template |
BaseField.form_template (путь) |
То же имя атрибута, новый префикс пути fields/form/ |
# Before
@dataclass
class RatingField(BaseField):
display_template: str = "displays/rating.html"
form_template: str = "forms/rating.html"
render_function_key: str = "rating"
# After
@dataclass
class RatingField(BaseField):
detail_template: str = "fields/detail/rating.html"
form_template: str = "fields/form/rating.html"
list_template: str = "fields/list/rating.html"
Среди новых возможностей на уровне полей стоит изучить validators, filters, default, хуки (getter, formatter, parser), copy_to_clipboard и словарь extra для произвольных метаданных. Подробнее см. документацию Custom Fields.
CustomView
Класс CustomView больше не принимает template_path и methods. Простые страницы создавайте с помощью виджетов. Для страниц, требующих полного контроля, создайте подкласс CustomView и объявите маршруты напрямую.
# Before
admin.add_view(CustomView(label="Home", path="/home", template_path="home.html"))
# After: widget-based page
admin.add_view(
CustomView(
menu_label="System Status",
path="/status",
widget=StatWidget(title="Pending jobs", value_callback=count_pending_jobs),
)
)
# After: full control
class HomeView(CustomView):
menu_label = "Home"
path = "/home"
@route("")
async def index(self, request: Request) -> Response:
return self.templates.TemplateResponse(request=request, name="home.html")
Декоратор @route также позволяет любому представлению предоставлять дополнительные endpoint'ы для таких задач, как JSON-данные для графиков или webhooks.
Пользовательские backend'ы
Если вы реализовали BaseModelView поверх собственного источника данных, обратите внимание на обновлённый контракт доступа к данным:
# Before
async def find_all(self, request, skip=0, limit=100, where=None, order_by=None): ...
async def count(self, request, where=None): ...
# After
async def find_all(
self, request, skip=0, limit=100, q=None, sorts=None, filters=None
): ...
async def count(self, request, q=None, filters=None): ...
- Строковый параметр
whereразделён наq(термины полнотекстового поиска) иfilters(типизированное деревоFilterGroup, формируемое конструктором фильтров). - Параметр
order_by(ранее список строк вида"field direction") заменён наsorts, который принимает список кортежей(field_name, direction). - Каждый backend теперь поставляется с реестром фильтров, сопоставляющим типы полей реализациям фильтров. Полный контракт и рабочий пример см. в документации Custom Backend.
Изменения поведения, которые стоит проверить
- Часовые пояса: значения datetime по умолчанию отображаются в локальном часовом поясе пользователя (см. примечание о
timezone_configв разделе «Конструктор Admin»). - Состояние в URL: состояние списка теперь хранится в URL. Сохранённые в закладки URL админки из предыдущих версий будут открываться с состоянием списка по умолчанию, поскольку сохранённые состояния DataTables не переносятся.
- Валидация email:
EmailFieldтеперь валидируется на сервере, если установленemail-validator. - Защита CSRF: защита CSRF встроена и основана на cookie. Если вы ранее оборачивали админку собственным CSRF-middleware, его можно безопасно удалить. Убедитесь, что задан
secret_key, чтобы токены переживали перезапуск сервера. - Размер загрузки FileField:
FileField.max_sizeтеперь по умолчанию равен 50 МБ вместо неограниченного размера. Передайтеmax_size=None, чтобы вернуть прежнее поведение без ограничений, или задайте явное значение, чтобы изменить лимит.
Удалено без замены
AdminConfig(см. раздел Аутентификация).datatables_options,responsive_tableиsave_state(см. раздел Удаление DataTables).- Backend Odmantic (см. раздел Требования).
Как получить помощь
Если вы столкнулись с проблемой миграции, не описанной в этом руководстве, пожалуйста, создайте issue. Приложите минимальный пример воспроизведения проблемы и укажите версию, с которой выполняется обновление. Запуск с Admin(debug=True) часто сразу выявляет причину, а полученные логи станут отличным дополнением к вашему отчёту.