【问题标题】:Django: Get list of model fields?Django:获取模型字段列表?
【发布时间】:2011-03-07 14:01:52
【问题描述】:

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

我想我可以利用inspect.getmembers(model) 来检索这些,但它返回的列表不包含任何这些字段。看起来 Django 已经掌握了这个类并添加了它所有的魔法属性并剥离了实际定义的内容。那么......我怎样才能得到这些领域?他们可能有一个功能可以为自己的内部目的检索它们?

【问题讨论】:

标签: django django-models


【解决方案1】:

结合给定线程的多个答案(谢谢!)并提出以下通用解决方案:

class ReadOnlyBaseModelAdmin(ModelAdmin):
    def has_add_permission(self, request):
        return request.user.is_superuser

    def has_delete_permission(self, request, obj=None):
        return request.user.is_superuser

    def get_readonly_fields(self, request, obj=None):
        return [f.name for f in self.model._meta.get_fields()]

【讨论】:

    【解决方案2】:

    有时我们也需要 db 列:

    def get_db_field_names(instance):
       your_fields = instance._meta.local_fields
       db_field_names=[f.name+'_id' if f.related_model is not None else f.name  for f in your_fields]
       model_field_names = [f.name for f in your_fields]
       return db_field_names,model_field_names
    

    调用方法获取字段:

    db_field_names,model_field_names=get_db_field_names(Mymodel)
    

    【讨论】:

      【解决方案3】:

      由于大多数答案都已过时,我将尝试在 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
      

      【讨论】:

      • 我认为这是正确的并接受它作为新的答案。感谢您的更新!
      • @mpen 不会声称它是最好的方式或最 Pythonic 但下面将/应该获得您希望在视图中显示的值,因此如果您愿意,则为 HTML 表格的标题。由于 get_fields() 返回一个元组,您可以对其进行迭代并获取类似于 appname.Model.field_name 的值,下面从第二个点清除值,包括在字段名称中使用下划线的情况以及使它们成为标题,因此根据每个独特情况的需要进行修改。 clean_field_names = [str(h).split('.')[2].replace("_", " ").title() for h in all_fields]
      • 来自实例的例子应该是:all_fields = bp._meta.fields
      • @GeorgeKettleborough 我明白了。它不是直接从课堂上工作的吗?没有要测试的示例。无论如何,这个例子是关于实例的,所以它应该来自 bp 或至少 bp.__class__
      【解决方案4】:

      Django 1.8 及更高版本:

      你应该使用get_fields():

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

      get_all_field_names() 方法是deprecated starting from Django 1.8 and will be removed in 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()

      【讨论】:

      • 也想要这些物品,而不仅仅是它们的名字。不过,这似乎在model._meta.fields 中可用,并且它们的名称似乎可以通过field.name 检索。我只是希望这是检索此信息的最稳定方法:)
      • 不完全确定。下划线似乎表明它是一个内部 API,如果 Django 人员将其提升为django.db.models.Model 上的实际公共方法调用,那就太酷了。我会深入研究一下,看看我能找到什么
      • 这是一个很好的解决方案,但是这种方法包括反向关系字段,例如反向 ForeignKey,这些字段并不完全是“字段”。有人知道如何区分实际的字段吗?
      • @rossipedia:只是注意到 ._meta API 是公共的(尽管它曾经是私有的,可能是在第一次编写此答案时):docs.djangoproject.com/en/1.8/ref/models/meta/…
      【解决方案5】:

      所以在我找到这篇文章之前,我已经成功地找到了它的工作原理。

      Model._meta.fields
      

      它的工作原理和

      一样
      Model._meta.get_fields()
      

      我不确定结果有什么不同,如果有的话。我运行了这个循环并得到了相同的输出。

      for field in Model._meta.fields:
          print(field.name)
      

      【讨论】:

        【解决方案6】:

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

        这是向后兼容的建议from the docs

        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】:

          为什么不直接使用它:

          manage.py inspectdb
          

          示例输出:

          class GuardianUserobjectpermission(models.Model):
              id = models.IntegerField(primary_key=True)  # AutoField?
              object_pk = models.CharField(max_length=255)
              content_type = models.ForeignKey(DjangoContentType, models.DO_NOTHING)
              permission = models.ForeignKey(AuthPermission, models.DO_NOTHING)
              user = models.ForeignKey(CustomUsers, models.DO_NOTHING)
          
              class Meta:
                  managed = False
                  db_table = 'guardian_userobjectpermission'
                  unique_together = (('user', 'permission', 'object_pk'),)
          

          【讨论】:

          • 这不是问题的答案。
          【解决方案8】:
          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

          【讨论】:

            【解决方案9】:

            至少对于Django 1.9.9——我目前使用的版本——,请注意.get_fields()实际上也“考虑”任何外国模型作为一个字段,这可能是有问题的。假设你有:

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

            这样

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

            同时,如@Rockallite 所示

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

            【讨论】:

              【解决方案10】:

              不清楚您是否有类的实例或类本身并尝试检索字段,但无论哪种方式,请考虑以下代码

              使用实例

              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'
              

              【讨论】:

                【解决方案11】:

                补充一下,我正在使用 self 对象,这对我有用:

                [f.name for f in self.model._meta.get_fields()]
                

                【讨论】:

                  【解决方案12】:

                  我发现将此添加到 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
                  

                  【讨论】:

                  • ForeignKey 呢?我有这样的错误django.db.models.fields.related.RelatedObjectDoesNotExist: CustomModel has no custom_attribute.
                  • ForeignKey 对我来说很好。虽然,默默地捕获所有异常是一种反模式。更好地捕捉AttributeError,或者至少记录一些异常被默默吞下。
                  • self._meta.get_all_field_names() 已被折旧并删除。您可以使用 for field in self._meta.get_fields() 之类的内容,然后使用 yield (field.name, field.value_from_object(self))
                  【解决方案13】:

                  这里提到的get_all_related_fields()方法一直是deprecated in 1.8。从现在开始是get_fields()

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

                  【讨论】:

                  • 这现在应该是公认的答案。言简意赅。
                  • 是的,这就是答案。
                  【解决方案14】:

                  这可以解决问题。我只在 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 获取它们。

                  【讨论】:

                    猜你喜欢
                    • 2019-08-09
                    • 2011-02-04
                    • 2019-12-04
                    • 2014-04-03
                    • 2018-12-14
                    • 2021-09-18
                    • 1970-01-01
                    • 2016-08-11
                    • 2011-04-08
                    相关资源
                    最近更新 更多