代码之家  ›  专栏  ›  技术社区  ›  Michael Scott Asato Cuthbert

在模型中指定django字段的类型(对于pylint)

  •  0
  • Michael Scott Asato Cuthbert  · 技术社区  · 7 年前

    我已经创建了基于CharField的自定义Django模型字段子类,但它使用python来确保返回的模型对象具有更复杂的对象(有些是列表,有些是具有特定格式的dict等等)——我使用的是MySQL,因此有些PostGreSql字段类型不可用。

    一切都很好,但是Pylint相信这些字段中的所有值都是字符串,因此我在使用这些模型的代码中收到了很多“不支持的成员资格测试”和“取消订阅对象”警告。我可以单独禁用它们,但我更希望让Pylint知道这些模型返回某些对象类型。类型提示没有帮助,例如:

    class MealPrefs(models.Model):
        user = ...foreign key...
        prefs: dict = custom_fields.DictOfListsExtendsCharField(
                default={'breakfast': ['cereal', 'toast'], 'lunch': []},
                )
    

    我知道某些内置的Django字段为Pylint(CharField,IntegerField)返回了正确的类型,而某些其他扩展已经找到了指定其类型的方法,因此Pylint很高兴(MultiSelectField),但是深入到它们的代码中,我不知道指定返回类型的“魔力”在哪里。

    谢谢!

    0 回复  |  直到 6 年前
        1
  •  4
  •   Samuel Dion-Girardeau    6 年前

    我好奇地看了一眼,我想大部分的“魔法”其实是为了 pytest-django

    在Django源代码中,例如 CharField ,没有什么能真正让类型暗示这是一个字符串的概念。因为类只继承自 Field

    另一方面,在深入研究pylint django的源代码之后,我发现最有可能发生这种情况的地方是:

    在里面 pylint_django.transforms.fields ,几个字段以类似的方式硬编码:

    _STR_FIELDS = ('CharField', 'SlugField', 'URLField', 'TextField', 'EmailField',
                   'CommaSeparatedIntegerField', 'FilePathField', 'GenericIPAddressField',
                   'IPAddressField', 'RegexField', 'SlugField')
    

    下面是一个名字可疑的函数 apply_type_shim

    此附加信息传递给 inference_tip according to the astroid docs ,用于添加推断信息(强调挖掘):

    astroid不仅可以用作AST库,它还提供了一些 基本的推理支持,它可以推断出 给定上下文, 类层次结构 等。我们通常称这种机制为推断

    astroid 是Pylint用来表示Python代码的底层库,所以我很确定信息就是这样传递给Pylint的。如果您跟踪导入插件时发生的情况,您将在 pylint_django/.plugin ,它实际上在其中导入 transforms ,有效地将推理提示添加到AST节点。

    1. 直接从另一个Django模型类派生,该类已经具有您要查找的关联类型。
        2
  •  4
  •   hynekcer    6 年前

    我最初以为你用的是一个插件 pylint-django ,但也许您显式地使用 prospector

    检查人 pylint 它的插件都没有使用Python类型注释中的信息检查代码( PEP 484 ). 它可以在不理解注释的情况下解析带有注释的代码,例如,如果仅在注释中使用名称,则不警告“未使用的导入”。信息 unsupported-membership-test 在表达式的行中报告 something in object_A 简单地说如果班级 A() 没有办法 __contains__ unsubscriptable-object 与方法有关 __getitem__


    你可以修补 pylint django酒店 对于自定义字段,请执行以下操作:
    添加函数:

    def my_apply_type_shim(cls, _context=None):  # noqa
        if cls.name == 'MyListField':
            base_nodes = scoped_nodes.builtin_lookup('list')
        elif cls.name == 'MyDictField':
            base_nodes = scoped_nodes.builtin_lookup('dict')
        else:
            return apply_type_shim(cls, _context)
        base_nodes = [n for n in base_nodes[1] if not isinstance(n, nodes.ImportFrom)]
        return iter([cls] + base_nodes)
    
    

    进入之内 pylint_django/transforms/fields.py

    同时替换 apply_type_shim 通过 my_apply_type_shim

    def add_transforms(manager):
        manager.register_transform(nodes.ClassDef, inference_tip(my_apply_type_shim), is_model_or_form_field)
    

    如果基类list或dict用于 Model FormView .


    我还考虑了一个插件存根解决方案,它也能做到这一点,但是“prospector”的替代方案看起来太复杂了,所以我更喜欢在安装后简单地修补源代码。

    MyPy ,在这里的一些注释中引用,还有一个插件mypy django用于django,但仅用于FormView,因为为 django.db 比处理属性更复杂。-我试着做了一个星期。