Django:获取模型字段列表?

279

我定义了一个User类,它最终从models.Model继承。我想获取此模型定义的所有字段列表。例如,phone_number = CharField(max_length=20)。基本上,我想检索从Field类继承的任何内容。

我曾尝试利用inspect.getmembers(model)来检索这些字段,但是它返回的列表不包含任何这些字段。看起来Django已经获取了该类并添加了所有魔术属性,并剥离了实际定义的内容。那么...我该如何获取这些字段呢?他们可能有一个为了内部目的而检索它们的函数吗?


这也许会有所帮助 https://pypi.python.org/pypi/django-inspect-model/0.5 - Paolo
可能是在Django中获取模型字段的重复问题。 - markpasc
16个回答

378

Django 1.8版本及更高版本:

您应该使用get_fields()函数:

[f.name for f in MyModel._meta.get_fields()]

get_all_field_names() 方法自Django 1.8版本开始已被弃用,并将在1.10中删除

上述链接的文档页面提供了一个完全向后兼容的get_all_field_names()实现,但对于大多数情况来说,以前的示例应该能够正常工作。


Django 1.8之前的版本:

model._meta.get_all_field_names()

这应该就可以解决问题了。

那需要一个实际的模型实例。如果您只有 django.db.models.Model 的子类,则应调用 myproject.myapp.models.MyModel._meta.get_all_field_names()


11
希望获取对象本身,而不仅仅是它们的名称。不过好像可以在 model._meta.fields 中找到这些对象,它们的名称似乎可以用 field.name 获取。我只希望这是检索这些信息最稳定的方式 :) - mpen
2
不是很确定。下划线似乎表明这是一个内部API,如果Django团队将其提升为“django.db.models.Model”的公共方法调用的话,那将会非常棒。我会深入挖掘并看看我能找到什么。 - rossipedia
2
我猜通过_meta属性是唯一的方法... 此外,还要查看_meta.many_to_many以获取ManyToMany字段! - Bernhard Vallant
4
这是一个不错的解决方案,但该方法包括反向关系字段,例如反向ForeignKey,而这些并不完全是“字段”。有人知道如何区分实际的字段吗? - viridis
2
@rossipedia:只是笔记一下._meta API是公开的(虽然它曾经是私有的,可能是当这个答案第一次编写时):https://docs.djangoproject.com/en/1.8/ref/models/meta/#django.db.models.options.Options - Nick S
显示剩余4条评论

120

由于大多数答案已经过时,我将尝试更新有关Django 2.2的信息 这里是您的应用程序 (帖子、博客、商店等)

1) 来自模型 链接: https://docs.djangoproject.com/en/stable/ref/models/meta/

from posts.model import BlogPost

all_fields = BlogPost._meta.fields
#or
all_fields = BlogPost._meta.get_fields()

请注意:

all_fields=BlogPost._meta.get_fields()

还会有一些关系,例如:您无法在视图中显示的关系。
就像我的情况一样:

Organisation._meta.fields
(<django.db.models.fields.AutoField: id>, <django.db.models.fields.DateField: created>...

Organisation._meta.get_fields()
(<ManyToOneRel: crm.activity>, <django.db.models.fields.AutoField: id>, <django.db.models.fields.DateField: created>...

2) 例如

from posts.model import BlogPost

bp = BlogPost()
all_fields = bp._meta.fields

3) 从父模型继承

假设我们有一个名为Post的父模型,您想在列表中查看所有字段,并使父字段在编辑模式下只读。

from django.contrib import admin
from posts.model import BlogPost 

@admin.register(BlogPost)
class BlogPost(admin.ModelAdmin):
    all_fields = [f.name for f in Organisation._meta.fields]
    parent_fields = BlogPost.get_deferred_fields(BlogPost)

    list_display = all_fields
    read_only = parent_fields

1
我会认为这是正确的,并接受它作为新答案。感谢更新! - mpen
@mpen 不会声称这是最好的方式或最符合Python风格的方式,但以下代码将/应该获取您想要在视图中显示的值,因此是HTML表的标题。由于get_fields()返回一个元组,您可以迭代它并获取类似于appname.Model.field_name的值,下面的代码从第二个点开始清理值,包括在字段名称中使用下划线的情况,并将它们转换为标题格式,因此请根据每个独特的情况进行修改。clean_field_names = [str(h).split('.')[2].replace("_", " ").title() for h in all_fields] - Alejandro Suarez
1
from实例的示例应该是:all_fields = bp._meta.fields - George Kettleborough
@GeorgeKettleborough 我明白你的意思。不过直接从类中调用也可以吗?我没有测试的例子。无论如何,这个例子是关于实例的,所以应该从bp或者至少bp.__class__中调用。 - Maks

82

这里提到的get_all_related_fields()方法已经在1.8版本中被弃用。从现在开始应该使用get_fields()方法。

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

1
这应该是现在被接受的答案。简洁明了,直截了当。 - Arnaud P
是的,这就是答案。 - eric

59

我发现将这个添加到Django模型中非常有帮助:

def __iter__(self):
    for field_name in self._meta.get_all_field_names():
        value = getattr(self, field_name, None)
        yield (field_name, value)

这让您可以做到:

for field, val in object:
    print field, val

3
ForeignKey是什么?我遇到了这样的错误:django.db.models.fields.related.RelatedObjectDoesNotExist: CustomModel没有custom_attribute属性。 - Volodymyr B.
ForeignKey 对我来说很好用。虽然,静默地捕获所有异常是一种反模式。最好捕获 AttributeError,或者至少记录下某些异常被静默地忽略了。 - Sardathrion - against SE abuse
2
self._meta.get_all_field_names() has been depreciated and removed. You can use something like for field in self._meta.get_fields() and then yield (field.name, field.value_from_object(self)) - MackM

16

这个很管用。我只在Django 1.7中进行了测试。

your_fields = YourModel._meta.local_fields
your_field_names = [f.name for f in your_fields]

Model._meta.local_fields 不包含多对多字段。您应该使用 Model._meta.local_many_to_many 来获取它们。


14

其他人没有提到的细节:

[f.name for f in MyModel._meta.get_fields()]

以获取为例

['id', 'name', 'occupation']

[f.get_attname() for f in MyModel._meta.get_fields()]

获取

['id', 'name', 'occupation_id']

如果

reg = MyModel.objects.first()

然后

reg.occupation

例如,获取

<Occupation: Dev>

reg.occupation_id
获得
1

这正是我想要的(需要qs.values()过滤字段列表),但不幸的是,这段代码失败了:'OneToOneRel' object has no attribute 'get_attname' - Miradil Zeynalli

14

无法确定您是否拥有该类的实例或类本身并尝试检索字段,但无论哪种方式,考虑以下代码

使用实例

instance = User.objects.get(username="foo")
instance.__dict__ # returns a dictionary with all fields and their values
instance.__dict__.keys() # returns a dictionary with all fields
list(instance.__dict__.keys()) # returns list with all fields

使用类

User._meta.__dict__.get("fields") # returns the fields

# to get the field names consider looping over the fields and calling __str__()
for field in User._meta.__dict__.get("fields"):
    field.__str__() # e.g. 'auth.User.id'

12
def __iter__(self):
    field_names = [f.name for f in self._meta.fields]
    for field_name in field_names:
        value = getattr(self, field_name, None)
        yield (field_name, value)

这对我在django==1.11.8中有效。


8

MyModel._meta.get_all_field_names()在几个版本之前被弃用并在Django 1.10中被移除

这里是向后兼容的建议来自文档

from itertools import chain

list(set(chain.from_iterable(
    (field.name, field.attname) if hasattr(field, 'attname') else (field.name,)
    for field in MyModel._meta.get_fields()
    # For complete backwards compatibility, you may want to exclude
    # GenericForeignKey from the results.
    if not (field.many_to_one and field.related_model is None)
)))

7

至少在我目前使用的版本 - Django 1.9.9 中,需要注意的是 .get_fields() 方法也会将任何外键模型视为字段,这可能会引起问题。举个例子:

class Parent(models.Model):
    id = UUIDField(primary_key=True)

class Child(models.Model):
    parent = models.ForeignKey(Parent)

这意味着,接下来的内容是关于IT技术的。
>>> map(lambda field:field.name, Parent._model._meta.get_fields())
['id', 'child']

如@Rockallite所示,同时

>>> map(lambda field:field.name, Parent._model._meta.local_fields)
['id']

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