Миграция плагина на InfraVision v4.0
Этот документ служит руководством для разработчиков плагинов, написанных до выхода InfraVision v4.0. Он содержит все рекомендуемые изменения для обеспечения совместимости плагина с InfraVision v4.0 и более поздними версиями.
Общее
Поддержка Python
InfraVision v4.0 прекращает поддержку Python 3.8 и 3.9 и вводит поддержку Python 3.12. Вам может потребоваться обновить процессы CI/CD и/или упаковку для отражения этого.
Перемещение ресурсов плагинов
Все Python-ресурсы плагинов были перемещены из extras.plugins в netbox.plugins в InfraVision v3.7 (см. #14036), и поддержка импорта этих ресурсов из старых расположений была удалена.
from extras.plugins import PluginConfig
from netbox.plugins import PluginConfig
ContentType переименован в ObjectType
Прокси-модель InfraVision для модели ContentType Django была переименована в ObjectType для ясности. В общем случае плагины должны использовать прокси ObjectType при ссылках на типы контента, так как он включает несколько пользовательских методов менеджера. Единственное исключение — при определении обобщённых внешних ключей: поле ForeignKey, используемое для GFK, должно указывать на нативный ContentType Django.
Кроме того, разработчикам плагинов настоятельно рекомендуется принять терминологию "object type" для имён полей и фильтров, где это возможно, для согласованности с ядром InfraVision (однако это не является обязательным требованием для совместимости).
content_types = models.ManyToManyField(
to='contenttypes.ContentType',
related_name='event_rules'
)
object_types = models.ManyToManyField(
to='core.ObjectType',
related_name='event_rules'
)
Представления
Действия представлений должны быть словарями
Формат объявления действий и разрешений представлений был обновлён в InfraVision v3.7 (см. #13550), и InfraVision v4.0 прекращает поддержку старого формата. Представления, наследующие ActionsMixin, должны объявлять единый словарь actions.
actions = ('add', 'import', 'export', 'bulk_edit', 'bulk_delete')
action_perms = defaultdict(set, **{
'add': {'add'},
'import': {'add'},
'bulk_edit': {'change'},
'bulk_delete': {'delete'},
})
actions = {
'add': {'add'},
'import': {'add'},
'export': set(),
'bulk_edit': {'change'},
'bulk_delete': {'delete'},
}
Формы
Удаление BootstrapMixin
Класс BootstrapMixin больше не доступен и не нужен, его можно удалить из всех форм.
from django import forms
from utilities.forms import BootstrapMixin
class MyForm(BootstrapMixin, forms.Form):
from django import forms
class MyForm(forms.Form):
Обновление определений Fieldset
InfraVision v4.0 вводит несколько новых классов для расширенной отрисовки форм, включая FieldSet. Определения fieldset в формах должны использовать этот новый класс вместо кортежа или списка.
Примечательно, что имя fieldset теперь необязательно и передаётся как именованный аргумент, а не как первый элемент набора.
from django.utils.translation import gettext_lazy as _
from netbox.forms import NetBoxModelForm
class CircuitForm(NetBoxModelForm):
...
fieldsets = (
(_('Circuit'), ('cid', 'type', 'status', 'description', 'tags')),
(_('Service Parameters'), ('install_date', 'termination_date', 'commit_rate')),
(_('Tenancy'), ('tenant_group', 'tenant')),
)
from django.utils.translation import gettext_lazy as _
from netbox.forms import NetBoxModelForm
from utilities.forms.rendering import FieldSet
class CircuitForm(NetBoxModelForm):
...
fieldsets = (
FieldSet('cid', 'type', 'status', 'description', 'tags', name=_('Circuit')),
FieldSet('install_date', 'termination_date', 'commit_rate', name=_('Service Parameters')),
FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
)
Навигация
Удаление цветов кнопок
InfraVision больше не применяет цвет к кнопкам внутри элементов меню навигации. Хотя эта функциональность всё ещё поддерживается, вы можете захотеть удалить цвет с любых кнопок для обеспечения согласованности с обновлённым дизайном.
PluginMenuButton(
link='myplugin:foo_add',
title='Add a new Foo',
icon_class='mdi mdi-plus-thick',
color=ButtonColorChoices.GREEN
)
PluginMenuButton(
link='myplugin:foo_add',
title='Add a new Foo',
icon_class='mdi mdi-plus-thick'
)
Макет интерфейса
Переименованные блоки шаблонов
Следующие блоки шаблонов были переименованы или удалены:
| Шаблон | Старое имя | Новое имя |
|---|---|---|
| generic/object.html | header |
page-header |
| generic/object.html | controls |
control-buttons |
| base/layout.html | content-wrapper |
Удалён (используйте content) |
Использование flex-элементов управления
Откажитесь от устаревших элементов управления "float" (например, float-end) в пользу новых flex-поведений Bootstrap для управления расположением и размером элементов по горизонтали. Например, следующий код выровняет два элемента по левому и правому краям родительского элемента:
<div class="d-flex justify-content-between">
<h3>Title text</h3>
<i class="mdi mdi-close"></i>
</div>
Проверка смещений колонок
При использовании смещений колонок (например, class="col-offset-3") обязательно также устанавливайте ширину колонки (например, class="col-9 col-offset-3"), чтобы избежать горизонтальной прокрутки.
Таблицы внутри карточек
Таблицы внутри карточек должны быть встроены напрямую, а не вложены в элемент card-body.
<div class="card">
<div class="card-body">
<table class="table table-hover attr-table">
...
</table>
</div>
</div>
<div class="card">
<table class="table table-hover attr-table">
...
</table>
</div>
Удаление класса btn-sm с кнопок
Класс btn-sm (маленький) обычно больше не нужен для кнопок общего назначения.
<a href="#" class="btn btn-sm btn-primary">Text</a>
<a href="#" class="btn btn-primary">Text</a>
Обновление классов bg-$color
Цвет переднего плана (текста) больше не настраивается автоматически классами bg-$color. Чтобы обеспечить достаточный контраст с цветом фона, используйте форму класса text-bg-$color или задайте цвет текста отдельно с помощью text-$color.
<span class="badge bg-primary">Text</span>
<span class="badge text-bg-primary">Text</span>
Устаревшие пользовательские CSS-классы
Следующие пользовательские CSS-классы были удалены:
object-subtitle(используйте вместо негоtext-secondary)
REST API
Расширение сериализатора для краткого режима
InfraVision теперь использует единый сериализатор API как для обычного, так и для "краткого" режимов (т.е. GET /api/dcim/sites/?brief=true); вложенные классы сериализаторов больше не требуются. Для поддержки краткого режима необходимы два изменения в сериализаторах API:
- Определите
brief_fieldsв классеMeta. Это поля, которые будут включены при использовании краткого режима. - Для любых вложенных объектов переключитесь на использование основного сериализатора и передайте
nested=True.
Все вложенные сериализаторы, которые больше не нужны, можно удалить.
class SiteSerializer(NetBoxModelSerializer):
region = NestedRegionSerializer(required=False, allow_null=True)
class Meta:
model = Site
fields = ('id', 'url', 'display', 'name', 'slug', 'status', 'region', 'time_zone', ...)
class SiteSerializer(NetBoxModelSerializer):
region = RegionSerializer(nested=True, required=False, allow_null=True)
class Meta:
model = Site
fields = ('id', 'url', 'display', 'name', 'slug', 'status', 'region', 'time_zone', ...)
brief_fields = ('id', 'url', 'display', 'name', 'description', 'slug')
Включение полей description в краткий режим
InfraVision теперь включает поле description в "кратком" режиме для всех моделей, которые его имеют. Это не является обязательным для плагинов, но вы можете сделать то же самое для согласованности.
GraphQL
InfraVision заменил Graphene-Django на Strawberry, что требует обновления любого кода GraphQL.
Изменение schema.py
Strawberry использует типизацию Python и в целом требует лишь небольшого рефакторинга определения схемы для обновления:
import graphene
from netbox.graphql.fields import ObjectField, ObjectListField
from utilities.graphql_optimizer import gql_query_optimizer
class CircuitsQuery(graphene.ObjectType):
circuit = ObjectField(CircuitType)
circuit_list = ObjectListField(CircuitType)
def resolve_circuit_list(root, info, **kwargs):
return gql_query_optimizer(models.Circuit.objects.all(), info)
import strawberry
import strawberry_django
@strawberry.type
class CircuitsQuery:
@strawberry.field
def circuit(self, id: int) -> CircuitType:
return models.Circuit.objects.get(pk=id)
circuit_list: list[CircuitType] = strawberry_django.field()
Изменение types.py
Преобразование типов также довольно простое, но Strawberry требует явного определения ссылок FK и M2M для правильной типизации.
- Параметры
class Metaнужно переместить в декоратор Strawberry - Добавьте определения
@strawberry_django.fieldдля любых ссылок FK и M2M в модели
import graphene
class CircuitType(NetBoxObjectType, ContactsMixin):
class Meta:
model = models.Circuit
fields = '__all__'
filterset_class = filtersets.CircuitFilterSet
from typing import Annotated
import strawberry
import strawberry_django
@strawberry_django.type(
models.CircuitType,
fields='__all__',
filters=CircuitTypeFilter
)
class CircuitTypeType(OrganizationalObjectType):
color: str
@strawberry_django.field
def circuits(self) -> list[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]]:
return self.circuits.all()
Изменение filters.py
Strawberry в настоящее время не поддерживает django-filter напрямую, поэтому необходимо создать явный файл filters.py. InfraVision включает новый autotype_decorator, используемый для автоматического обёртывания FilterSets и минимизации необходимого кода.
import strawberry
import strawberry_django
from circuits import filtersets, models
from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
__all__ = (
'CircuitFilter',
)
@strawberry_django.filter(models.Circuit, lookups=True)
@autotype_decorator(filtersets.CircuitFilterSet)
class CircuitFilter(BaseFilterMixin):
pass