Как заставить Django, аннотации типов и Flake8 работать вместе?

Я использую Джанго/ДРФ. Я хочу написать Python с проверкой типов. Я тоже хочу линтинг.

Структура модели Django включает создание класса менеджера для каждой модели и назначение экземпляра класса менеджера objects члену модели.

Поскольку я использую аннотации типов, методы класса менеджера теперь также включают ссылку на модель. Это приводит к тому, что flake8 сообщает об ошибке в одном или другом.

Вот пример:

class UserManager:

    def create_user(email: str) -> User:
        ...                        ^^^^ [flake8] F821: undefined name 'User'

class User:

    objects = UserManager()

И если я переверну их, я получу:

class User:

    objects = UserManager()
              ^^^^^^^^^^^ [flake8] F821: undefined name 'UserManager'

class UserManager:

    def create_user(email: str) -> User:
        ...                        

Что люди делают, чтобы написать типизированный код Django?


person user606006    schedule 07.09.2018    source источник


Ответы (2)


В Python 3.7 вы можете исправить это, используя from __future__ import annotations, что позволит использовать аннотации типов, содержащие прямые ссылки. Это предпочтительнее использования строковых аннотаций (как предлагается в другом ответе) для более простого рефакторинга кода и анализа. Использование кавычек — это просто обходной путь для более старых версий Python, которые не поддерживают аннотации с прямой ссылкой. В Python 4 (если не раньше) это будет поведение по умолчанию.

person goodside    schedule 05.12.2018

Обратите внимание, помимо ввода: модели Django не требуют наличия явного менеджера для каждой модели! Django генерирует Manager, используя свойство objects для каждой модели. Вам нужно только добавить пользовательский ModelManager для конкретных целей. Переходим к типам...

Я думаю, вы должны поставить UserManager перед User, если вы действительно используете этот класс. Вы можете заключить тип в кавычки для имен, которые еще не определены, это описано в документации для PEP 484 в разделе "Переадресованные ссылки".

class UserManager:

    def create_user(email: str) -> "User":
        ...                        

class User:

    objects = UserManager()

Другой вариант — определить подсказки типов с использованием синтаксиса Python 2 в комментариях, подсказки типов в Python 2 показывает аналогичный ответ.

class UserManager:

    def create_user(email: str):
        # type: (str) -> User
        ...
person A. J. Parr    schedule 07.09.2018
comment
Благодарю за ваш ответ. Из любопытства, есть ли какая-либо документация, в которой говорится, что перенос классов подсказок типа в строку также приемлем? Это первый раз, когда я слышу это. - person user606006; 07.09.2018
comment
python.org/dev/peps/pep-0484/#forward- references имеет раздел Forward References для подсказок типов, содержащих имена, которые еще не определены. Я добавил ссылку на это в свой ответ. - person A. J. Parr; 07.09.2018
comment
Если у меня есть менеджеры в другом файле, и я пытаюсь импортировать модель User, возникает ошибка: ImportError: cannot import name 'User' from partially initialized module 'my_app.users.models' (most likely due to a circular import). С другой стороны, если я использую модель User без импорта, ошибка mypy rainse: Name 'User' is not defined. - person Cristian Flórez; 10.07.2020