【问题标题】:How to define a django model field with the same name as a Python keyword如何定义与 Python 关键字同名的 django 模型字段
【发布时间】:2016-03-27 06:43:17
【问题描述】:

我需要定义一个名称为in 的Django 模型字段,这是一个Python 语言关键字。这是一个语法错误:

class MyModel(models.Model):
    in = jsonfield.JSONField()

我怎样才能做到这一点?

我需要这个名字的原因是当我使用django-rest-framework的ModelSerializer类时,字段名被用作序列化输出的键,我认为操作django的Model类可能更容易而不是ModelSerializer 类得到我想要的输出。

【问题讨论】:

  • 这真的不是一个好主意。即使您设法以某种方式定义它,您也将难以访问它。
  • in 无论如何都是一个可怕的变量名称,in 与什么有关?
  • 避免这种情况,即使它对你有用,它也会导致你到处发生冲突。命名变量时要记住的三个主要规则:避免关键字名称,制作重要名称,遵循框架/语言命名约定UpperCamelCase , lowerCamelCase, ...)

标签: python django django-models django-rest-framework


【解决方案1】:

一般来说,你不会。避免在标识符中使用关键字。一般的 Python 约定是为这些名称添加下划线;这里是in_:

class MyModel(models.Model):
    in_ = jsonfield.JSONField()

然而,Django 禁止名称以下划线结尾,因为下划线与其过滤器命名约定冲突,因此您仍然必须想出一个不同的名称;选择一个仍能描述您的情况的;我选择了 contained in 而不是 in,作为对你想在这里做什么的猜测:

class MyModel(models.Model):
    contained_in = jsonfield.JSONField()

如果您尝试匹配现有的数据库架构,请使用db_column attribute

class MyModel(models.Model):
    contained_in = jsonfield.JSONField(db_column='in')

如果你想固执一点,在普通类中你可以在创建类后使用setattr()来使用字符串而不是标识符:

class Foo:
    pass

setattr(Foo, 'in', 'some value')

但您必须在代码中的任何位置使用setattr()getattr()delattr() 和/或vars() 才能访问它。

在 Django 中,您将添加复杂性,即 models.Model 子类使用元类将您的类成员解析为其他结构,而使用 setattr() 添加额外字段不会无需(大量)额外工作即可重新执行元类所做的工作。您可以改用 field.contribute_to() 方法,在 Django 准备好类后调用它(技术取自 this blog post):

from django.db.models.signals import class_prepared

def add_field(sender, **kwargs):
    if sender.__name__ == "MyModel":
        field = jsonfield.JSONField('in')
        field.contribute_to_class(sender, 'in')

class_prepared.connect(add_field)

但您必须确保此钩子在创建模型类之前已注册。

【讨论】:

  • in_ 在 Django 1.8 中不起作用,你会看到 Field names must not end with an underscore 错误。
  • 惯例实际上是重新评估应该调用什么变量
  • @alexanderlukanin13:在这种情况下,您可以选择一个符合 Django 约定的不同名称。
【解决方案2】:

没有办法让它工作,无论如何这是一个坏主意。选择其他名称。

如果出于某种原因,您希望列名与某个保留关键字匹配,请为该字段使用 db_column 参数。

in_something = models.CharField(db_column='in', max_length=100)

【讨论】:

    【解决方案3】:

    你提到了django rest框架的使用。以下是如何使其在序列化程序层上工作。使用的关键字是from。如果您希望将其映射到不同的名称,to 只是一个非关键字示例。

    from django.db import models
    from rest_framework import serializers
    
    
    SP_FIELD_MAP = {
        'from': 'sender'
    }
    
    
    # would be in models.py
    class Transaction(models.Model):
        recipient = models.CharField(max_length=16)
        sender = models.CharField(max_length=64)
    
    
    # would be in serializers.py
    class TransactionSerializer(serializers.ModelSerializer):
        to = serializers.CharField(source='recipient')
    
        class Meta:
            model = Transaction
            fields = ('id', 'to', 'from')
            # `from` is a python keyword hence this
            extra_kwargs = {'from': {'source': 'sender'}}
    
        def build_field(self, field_name, info, model_class, nested_depth):
            # Catches python keywords like `from` and maps to its proper field
            field_name = SP_FIELD_MAP.get(field_name, field_name)
            return super(TransactionSerializer, self).build_field(
                field_name, info, model_class, nested_depth)
    

    仅使用 POST 和 GET 方法在 CharField 上进行了测试,但我看不出它在其他方法上如何不起作用。不过,您可能需要其他字段类型的特殊内容。我建议进入源。 DRF 的源代码中有很多有趣的东西。

    【讨论】:

      【解决方案4】:

      您应该为所有变量提供描述性名称,清楚地说明它们的用途,并且在可能的情况下,应该很容易断言变量的类型它是。

      in,对我来说,乍一看似乎是一个布尔值,所以为了在我自己的代码扩展中使用这个变量,我需要在知道如何使用之前找到它的其他用法它。

      因此,只是不要尝试将某些东西拼凑在一起,以便将这个可怕的变量名放入模型中,这样做对您没有任何价值,它并没有真正更快类型,因为智能感知在大多数地方都可用。弄清楚“in”与什么相关,然后制定一个具有描述性的专有名称。

      【讨论】:

        猜你喜欢
        • 2011-12-07
        • 1970-01-01
        • 1970-01-01
        • 2015-06-13
        • 1970-01-01
        • 2013-11-09
        • 2012-12-24
        • 2014-02-06
        • 1970-01-01
        相关资源
        最近更新 更多