在Django中获取模型的字段

176
给定一个Django模型,我试图列出它的所有字段。我看到了一些使用_meta模型属性来做这件事情的例子,但是下划线在meta前面不是表示_meta属性是私有属性,不应该直接访问吗?因为例如,_meta的布局未来可能会更改,不再是稳定的API?
_meta是否是此规则的例外?它是否稳定且可用,还是将其视为不良实践?或者是否有函数或其他方式可以内省模型的字段而不使用_meta属性?以下是一些链接列表,显示如何使用_meta属性进行此操作。 任何建议都将不胜感激。 django object get/set field

http://www.djangofoo.com/80/get-list-model-fields

如何反省 Django 模型字段?


可能是重复的问题:Django:获取模型字段列表? - Anto
12个回答

203

_meta是私有的,但它相对稳定。有人正在努力正式化、文档化并移除下划线,这可能会在1.3或1.4之前发生。我想会努力确保向后兼容性,因为许多人仍在使用它。

如果你特别关心兼容性,可以编写一个函数,接受模型并返回字段。这意味着如果将来有所改变,你只需要更改一个函数。

def get_model_fields(model):
    return model._meta.fields
我相信这将返回一个Field对象列表。要从实例中获取每个字段的值,请使用getattr(instance,field.name)。 更新:Django贡献者正在开发API来替换_Meta_对象作为Google Summer of Code的一部分。请参见:
- https://groups.google.com/forum/#!topic/django-developers/hD4roZq0wyk
- https://code.djangoproject.com/wiki/new_meta_api

54
请注意,如果您还需要访问多对多关系字段,您需要访问model._meta.many_to_many - Bernhard Vallant
1
def get_model_fields(self): return self._meta.fields使用此方法轻松返回所有模型字段... 非常感谢... - garmoncheg
3
django/core/management/commands/loaddata.py使用_meta来遍历应用程序、模型和字段的树形结构。这是一个不错的模式可以学习...而且你可以确定这是“官方方式”。 - hobs
2
如果您正在使用通用外键,还应检查 _meta.virtual_fields - andrei1089
7
_meta现在是一个正式支持的API(Django 1.8中新增)。 - mgalgs
显示剩余3条评论

154

9
这实际上是适用于较新版本Django的正确答案。 - chhantyal
2
1.10 get_fields() https://docs.djangoproject.com/en/1.10/ref/models/meta/#django.db.models.options.Options.get_fields - Risadinha
7
一个问题:如果这些是“公开和官方的”,为什么它们还在“_meta”下面? - krubo
1
@krubo 这可以像 NamedTuple_replace 一样,下划线是为了防止命名冲突,而不是表示它是私有的。如果你有一个 meta 字段,就会发生冲突。 - Carcigenicate

27

get_fields() 返回一个元组,每个元素都是一个 Model field 类型,不能直接用作字符串。所以,field.name 将返回字段名称。

my_model_fields = [field.name for field in MyModel._meta.get_fields()]
上面的代码将返回一个包含所有字段名称的列表。

示例

In [11]: from django.contrib.auth.models import User

In [12]: User._meta.get_fields()
Out[12]: 
(<ManyToOneRel: admin.logentry>,
 <django.db.models.fields.AutoField: id>,
 <django.db.models.fields.CharField: password>,
 <django.db.models.fields.DateTimeField: last_login>,
 <django.db.models.fields.BooleanField: is_superuser>,
 <django.db.models.fields.CharField: username>,
 <django.db.models.fields.CharField: first_name>,
 <django.db.models.fields.CharField: last_name>,
 <django.db.models.fields.EmailField: email>,
 <django.db.models.fields.BooleanField: is_staff>,
 <django.db.models.fields.BooleanField: is_active>,
 <django.db.models.fields.DateTimeField: date_joined>,
 <django.db.models.fields.related.ManyToManyField: groups>,
 <django.db.models.fields.related.ManyToManyField: user_permissions>)

In [13]: [field.name for field in User._meta.get_fields()]
Out[13]: 
['logentry',
 'id',
 'password',
 'last_login',
 'is_superuser',
 'username',
 'first_name',
 'last_name',
 'email',
 'is_staff',
 'is_active',
 'date_joined',
 'groups',
 'user_permissions']

16

现在有一种特殊的方法 - get_fields()

    >>> from django.contrib.auth.models import User
    >>> User._meta.get_fields()

它接受两个参数,可用于控制返回哪些字段:

  • include_parents

    默认为True。递归包括在父类上定义的字段。如果设置为False,则get_fields()将仅搜索当前模型直接声明的字段。从抽象模型或代理类直接继承的模型中的字段被认为是本地字段,不在父级上。

  • include_hidden

    默认为False。如果设置为True,则get_fields()将包括用于支持其他字段功能的字段。这还将包括任何具有以“+”开头的related_name(例如ManyToManyField或ForeignKey)的字段。


11

fields = [f"{f.name}_id" if f.is_relation else f.name for f in model._meta.fields]

该代码用于从模型中获取字段列表,如果字段是关系字段,则在字段名称后面添加"_id"。

10

这是 Django 本身从模型构建表单时所进行的操作。它使用 _meta 属性,但正如 Bernhard 所指出的那样,它同时使用了 _meta.fields 和 _meta.many_to_many。通过查看 django.forms.models.fields_for_model,您可以这样做:

opts = model._meta
for f in sorted(opts.fields + opts.many_to_many):
    print '%s: %s' % (f.name, f)

6

_meta中包含的模型字段在多个位置以相应字段对象的列表形式列出。将它们作为字典处理并使用字段名称作为键可能更容易操作。

在我看来,这是收集和组织模型字段对象的最简洁和最具表现力方式:

def get_model_fields(model):
  fields = {}
  options = model._meta
  for field in sorted(options.concrete_fields + options.many_to_many + options.virtual_fields):
    fields[field.name] = field
  return fields

(请参见在django.forms.models.fields_for_model中的此示例用法。)

2
你可能想要在代码旁边附上一个简短的描述,说明它正在做什么。 - Bas van Dijk

3
这个怎么样?
fields = Model._meta.fields

1
我想你可能想要访问字段的属性。此外,正如之前的答案所指出的那样,还有“many_to_many”和“virtual_fields”。 - Jocke
@Jocke,你说得对。可能需要访问另一个属性。回答已编辑。 - JDE876

2
根据Django 2.2文档,您可以使用以下方法:
获取所有字段:Model._meta.get_fields() 获取单个字段:Model._meta.get_field('字段名') 例如:Session._meta.get_field('expire_date') "最初的回答"

2
如果您需要在管理站点上使用此功能,还可以使用ModelAdmin.get_fields方法(docs),它返回一个字段名称字符串list
例如:
class MyModelAdmin(admin.ModelAdmin):
    # extending change_view, just as an example
    def change_view(self, request, object_id=None, form_url='', extra_context=None):
        # get the model field names
        field_names = self.get_fields(request)
        # use the field names
        ...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接