Работа с пользовательскими полями
Yandex Tracker позволяет работать с нестандартным набором полей. Мы учли это и добавили возможность передавать в запросах ожидаемую вами модель.
Допустим, у вас есть очередь для службы поддержки (назовём её HELP). Для ведения такой очереди вам могут потребоваться дополнительные поля, например для сохранения идентификатора внешней системы или каких-либо ещё пользовательских данных.
О том, как настроить локальное поле, вы можете узнать из соответствующего раздела документации: https://cloud.yandex.ru/docs/tracker/local-fields
В нашем примере мы рассмотрим добавление двух полей:
- userUsername
- userId
Создаём модель задачи
Создайте дочерний класс задачи из yatracker, чтобы насытить родительский класс нужными полями:
from yatracker.types import FullIssue
class HelpIssue(FullIssue):
user_username: str | None = None
user_id: int | None = None
Обратите внимание, что модели нашей библиотеки автоматически используют со стороны python имена в
стиле snake_case, а при работе с Tracker, они конвертируются в camelCase – как это принято в
самом трекере.
Дополнительные поля лучше объявлять необязательными (со значением по умолчанию None):
локальное поле может отсутствовать в ответе трекера, и тогда задача без него всё равно
корректно разберётся.
Локальные поля очереди
В случае с локальными полями очередей трекера, их названия выглядят вот так:
Работать с таким именем неудобно, не говоря о неудобствах передачи его в kwargs. Авторы стандартной библиотеки предлагают делать это так:
Такой ключ уходит в Tracker как есть: в camelCase преобразуются только имена,
которые являются корректными идентификаторами Python (например, attachment_ids
превратится в attachmentIds), а идентификаторы локальных полей вроде <id>--userId
передаются без изменений.
Мы же предлагаем использовать удобные вам названия, а наша библиотека позаботится о правильном преобразовании:
from yatracker.types import FullIssue, field
class HelpIssue(FullIssue):
user_id: int | None = field(
default=None,
alias="64a51c6d866ea82411abe756--userId",
)
Устаревшее написание field(name="...") тоже продолжает работать – это синоним alias=,
оставленный для совместимости:
class HelpIssue(FullIssue):
user_id: int | None = field(
default=None,
name="64a51c6d866ea82411abe756--userId",
)
Используем модель задачи
Для работы с кастомными моделями мы предусмотрели параметр _type, позволяющий корректно
распознавать набор атрибутов и работать с ними в удобном для вас формате.
issue = await tracker.create_issue(
summary="New Issue",
queue="HELP",
user_id=42,
_type=HelpIssue,
)
print(issue.user_id)
Поле url и ключ self
Ключ self в ответах трекера содержит ссылку на объект. В моделях библиотеки он читается в
поле url. Трекер выставляет self сам, записать его нельзя, поэтому именованный аргумент
url= в create_issue, edit_issue, move_issue и других методах уходит в API как есть –
ключом url, а не self. Модели, вложенные в тело запроса (например, parent=Issue(...)),
сериализуются в том же виде, в каком пришли из API, – с ключом self. Если в вашей очереди
есть пользовательское поле url (например, ссылка на задачу в старой системе при миграции),
достаточно передать его напрямую:
issue = await tracker.create_issue(
summary="New Issue",
queue="HELP",
url="https://old-tracker/ticket/42",
)
Чтобы читать такое поле из ответа, объявите его в дочернем классе под другим python-именем –
url уже занято ссылкой на объект:
from yatracker.types import FullIssue, field
class MigratedIssue(FullIssue):
source_url: str | None = field(default=None, alias="url")
issue = await tracker.get_issue("HELP-1", _type=MigratedIssue)
print(issue.url) # ссылка на задачу в API (ключ self)
print(issue.source_url) # пользовательское поле url
Если передать такую модель в _type при записи, source_url= тоже преобразуется в ключ
url, а одновременная передача url= и source_url= завершится ValueError, чтобы одно
из значений не потерялось молча:
issue = await tracker.edit_issue(
"HELP-1",
source_url="https://old-tracker/ticket/42",
_type=MigratedIssue,
)
Без _type библиотека ничего не знает о source_url и отправит его как обычное поле
sourceUrl.
Модели в теле запроса
Модель, попавшая в тело запроса, отправляется в своей запросной форме, а не дословным
model_dump. По умолчанию это одно и то же, но модели, у которых запрос и ответ выглядят
по-разному, переопределяют её: EntityChecklistItem отдаёт assignee идентификатором и
выбрасывает read-only поля, WidgetBucket переименовывает type в unit, FilterSort
заменяет объект поля его ключом. Благодаря этому объект, прочитанный из API, можно передать
обратно как есть — и он отрисуется одинаково, каким бы методом вы его ни отправили.
Собственная модель ничего для этого делать не обязана: базовый класс уже умеет выгружать себя
в JSON. Переопределять _to_request() имеет смысл, только если ваш эндпоинт ждёт форму,
отличную от ответа; учтите, что хук не рекурсивный — вложенные модели выгружает pydantic,
поэтому переопределение, которое несёт вложенную модель, должно вызвать её хук само.