django.db.models.fields.Field.name参数的用途

vis*_*ell 6 python django django-models

最近我发现没有记录的django.db.models.fields.Field.name选项:

@total_ordering
class Field(RegisterLookupMixin):  #   here we have it
    ...                                   ?????????
    def __init__(self, verbose_name=None, name=None, primary_key=False,
                 max_length=None, unique=False, blank=False, null=False,
                 db_index=False, rel=None, default=NOT_PROVIDED, editable=True,
                 serialize=True, unique_for_date=None, unique_for_month=None,
                 unique_for_year=None, choices=None, help_text='', db_column=None,
                 db_tablespace=None, auto_created=False, validators=(),
                 error_messages=None):
        ...
Run Code Online (Sandbox Code Playgroud)

提到在DOC路吧:

# A guide to Field parameters:
#
#   * name:      The name of the field specified in the model.
#   * attname:   The attribute to use on the model object. This is the same as
#                "name", except in the case of ForeignKeys, where "_id" is
#                appended.
#   * db_column: The db_column specified in the model (or None).
#   * column:    The database column for this field. This is the same as
#                "attname", except if db_column is specified.
#
# Code that introspects values, or does other dynamic things, should use
# attname. For example, this gets the primary key value of object "obj":
#
#     getattr(obj, opts.pk.attname)
Run Code Online (Sandbox Code Playgroud)

上面的描述与#683([patch]使用自定义db_column保存失败)票证有关.

因此,如果我们查看整个django.db.models.fields.Field类,这似乎是name选项设置属性名称,这使得变量的真实名称无效:

假设我们有我们的模型:

# models.py
from django.db import models


class SomeModel(models.Model):
    first = models.CharField(max_length=50, verbose_name='first', name='second')
    third = models.CharField(max_length=50, verbose_name='third')
Run Code Online (Sandbox Code Playgroud)

什么django-admin shell告诉我们:

In[2]: from app.models import SomeModel
In[3]: SomeModel.objects.create(first='first', third='third')
Traceback (most recent call last):
  File "/Users/ailove/Home/personal/untitled/venv/lib/python3.6/site-packages/IPython/core/interactiveshell.py", line 2963, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-3-08e446dfd6e3>", line 1, in <module>
    SomeModel.objects.create(first='first', third='third')
  File "/Users/ailove/Home/personal/untitled/venv/lib/python3.6/site-packages/django/db/models/manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/Users/ailove/Home/personal/untitled/venv/lib/python3.6/site-packages/django/db/models/query.py", line 415, in create
    obj = self.model(**kwargs)
  File "/Users/ailove/Home/personal/untitled/venv/lib/python3.6/site-packages/django/db/models/base.py", line 495, in __init__
    raise TypeError("'%s' is an invalid keyword argument for this function" % kwarg)
TypeError: 'first' is an invalid keyword argument for this function
In[4]: obj = SomeModel.objects.create(second='second', third='third')
In[5] obj.third
Out[5]: 'third'
In[6]: obj.second
Out[6]: 'second'
In[7]: obj.first
Traceback (most recent call last):
  File "/Users/ailove/Home/personal/untitled/venv/lib/python3.6/site-packages/IPython/core/interactiveshell.py", line 2963, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-7-f0deaec10795>", line 1, in <module>
    obj.first
AttributeError: 'SomeModel' object has no attribute 'first'
Run Code Online (Sandbox Code Playgroud)

问题有点广泛,但我也很好奇.

这个name选项是一个只能帮助开发的东西django,还是普通的开发人员也可以利用它?如果可以的话,为什么?

Lou*_*uis 6

name如果我希望模型的字段具有getter和setter并隐藏getter/setter从Django ORM和数据库引入的命名约定,我发现它很有用.

Python中一个相当常见的模式是让getter和setter以字段的公共名称命名,并且保持字段值的字段以下划线开头,下划线按惯例表示它是私有的.例如,你有一个名字的setter和getter foo以及它的"private"字段命名_foo:

class Something(object):
    _foo = "some default value"

    @property
    def foo(self):
        return self._foo

    @foo.setter
    def foo(self, val):
        self._foo = val
Run Code Online (Sandbox Code Playgroud)

上面的代码是准系统.据推测,在实际场景中,您可以在getter或setter中添加其他代码来执行其他工作.(否则,没有理由使用getter和setter.)假设上面的类的实例已命名instance,您可以访问instance.foo并且不要触摸,instance._foo因为该_foo字段不是公共API的一部分.

如果你想采用上面的模式并在Django模型上实现它,你可以这样做:

class MyModel(models.Model):
    _foo = models.TextField()

    @property
    def foo(self):
        return self._foo

    @foo.setter
    def foo(self, val):
        self._foo = val
Run Code Online (Sandbox Code Playgroud)

但是,最终结果是您的字段对于Django ORM而言是已知的,_foo并且它存储在_foo数据库中命名的列中.有些人会对此感到满意,但在我的项目中,我更喜欢Python中getter/setter的存在不会影响其他地方的字段名称.为了在Django ORM和列名中使用相同的名称,您可以:

_foo = models.TextField(name="foo")
Run Code Online (Sandbox Code Playgroud)

执行此操作将设置Django ORM中显示的字段名称,因此这有效:

MyModels.objects.get(foo=...)
Run Code Online (Sandbox Code Playgroud)

否则,你必须使用下划线并做MyModels.objects.get(_foo=...).它还设置了数据库列的名称,因此在原始SQL中您可以访问该列foo.如果您碰巧需要不同的列名,则必须使用db_column参数来设置名称:models.TextField(name="foo", db_column="something_else")