153

给定一个 Django 模型,我试图列出它的所有字段。我已经看到了一些使用 _meta 模型属性执行此操作的示例,但是 meta 前面的下划线不是表示 _meta 属性是私有属性,不应该直接访问吗?...因为,例如,_meta 的布局可能会在未来发生变化并且不是一个稳定的 API?

_meta 是这个规则的例外吗?它是否稳定且可以使用,或者访问它是否被认为是不好的做法?或者是否有一个函数或其他方法可以在不使用 _meta 属性的情况下自省模型的字段?下面是一些链接的列表,显示了如何使用 _meta 属性执行此操作

非常感谢任何建议。

django 对象获取/设置字段

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

如何自省 django 模型字段?

4

11 回答 11

187

_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

于 2010-09-05T21:44:55.417 回答
128

我知道这篇文章已经很老了,但我只是想告诉任何正在搜索相同内容的人,有一个公共和官方的 API 可以做到这一点:get_fields()get_field()

用法:

fields = model._meta.get_fields()
my_field = model._meta.get_field('my_field')

https://docs.djangoproject.com/en/3.2/ref/models/meta/#retrieving-all-field-instances-of-a-model

于 2015-04-02T23:07:03.110 回答
20

get_fields()返回a tuple,每个元素都是一个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']
于 2018-07-31T06:29:32.743 回答
14

现在有一个特殊的方法——get_fields()

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

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

  • 包括父母

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

  • 包含隐藏

    默认为假。如果设置为 True,get_fields() 将包含用于支持其他字段功能的字段。这还将包括任何具有以“+”开头的相关名称(例如 ManyToManyField 或 ForeignKey)的字段</p>

于 2016-12-01T06:13:14.057 回答
9

这是 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)
于 2013-01-16T18:58:09.420 回答
5

_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 中的此示例用法。)

于 2013-12-08T21:39:16.577 回答
2

这个怎么样。

fields = Model._meta.fields
于 2016-08-03T15:31:55.407 回答
2

如果您的管理站点需要这个,还有ModelAdmin.get_fields方法 ( docs ),它返回一个listof field name strings

例如:

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
        ...
于 2019-01-16T09:06:23.480 回答
2

根据 django 文档 2.2,您可以使用:

获取所有字段:Model._meta.get_fields()

要获取单个字段:Model._meta.get_field('field name')

前任。Session._meta.get_field('expire_date')

于 2019-04-07T16:00:49.630 回答
0

另一种方法是将函数添加到模型中,当您想要覆盖日期时,您可以调用该函数。

class MyModel(models.Model):
    name = models.CharField(max_length=256)
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    def set_created_date(self, created_date):
        field = self._meta.get_field('created')
        field.auto_now_add = False
        self.created = created_date

    def set_modified_date(self, modified_date):
        field = self._meta.get_field('modified')
        field.auto_now = False
        self.modified = modified_date

my_model = MyModel(name='test')
my_model.set_modified_date(new_date)
my_model.set_created_date(new_date)
my_model.save()
于 2019-09-26T11:48:34.777 回答
0

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

于 2021-12-23T05:22:51.977 回答