Obter campos do modelo no Django

121

Dado um modelo do Django, estou tentando listar todos os seus campos. Eu já vi alguns exemplos disso usando o atributo _meta model, mas o sublinhado na frente da meta não indica que o atributo _meta é um atributo privado e não deve ser acessado diretamente? ... Porque, por exemplo, o layout do _meta pode mudar no futuro e não ser uma API estável?

_Meta é uma exceção a esta regra? É estável e pronto para uso ou é considerado uma má prática acessá-lo? Ou existe uma função ou alguma outra maneira de examinar os campos de um modelo sem usar o atributo _meta? Abaixo está uma lista de alguns links mostrando como fazer isso usando o atributo _meta

Qualquer conselho é muito apreciado.

campo get / set do objeto django

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

Como introspectar campos de modelo de django?

Joe J
fonte
possível duplicata do Django: Obter lista de campos de modelo?
Anto 06/01

Respostas:

144

_metaé privado, mas é relativamente estável. Há esforços para formalizá-lo, documentá-lo e remover o sublinhado, o que pode ocorrer antes de 1.3 ou 1.4. Eu imagino que serão feitos esforços para garantir que as coisas sejam compatíveis com versões anteriores, porque muitas pessoas já a usam de qualquer maneira.

Se você estiver particularmente preocupado com a compatibilidade, escreva uma função que use um modelo e retorne os campos. Isso significa que, se algo mudar no futuro, você precisará alterar apenas uma função.

def get_model_fields(model):
    return model._meta.fields

Eu acredito que isso retornará uma lista de Fieldobjetos. Para obter o valor de cada campo da instância, use getattr(instance, field.name).

Atualização: os colaboradores do Django estão trabalhando em uma API para substituir o objeto _Meta como parte de um verão de código do Google. Veja:
- https://groups.google.com/forum/#!topic/django-developers/hD4roZq0wyk
- https://code.djangoproject.com/wiki/new_meta_api

Will Hardy
fonte
45
Você também deve estar ciente do fato de que, se você também precisar dos campos muitos-para-muitos, precisará acessar model._meta.many_to_many!
Bernhard Vallant 05/09/10
1
Obrigado Will. É bom saber que outras pessoas também estão usando o _meta. Eu gosto da ideia de ter uma função de wrapper. Lazerscience, obrigado também. É bom saber que existe um bom método para obter os campos many_to_many. Joe
Joe J
3
django/core/management/commands/loaddata.pyusa _meta para percorrer a árvore de aplicativos, modelos e campos. Bom padrão a seguir ... e você pode apostar que é o "caminho oficial".
27512 hobs
2
Se você estiver usando chaves estrangeiras genéricos você também deve verificar_meta.virtual_fields
andrei1089
7
_metaé agora, a API compatível formais (novo no Django 1.8)
mgalgs
97

Eu sei que este post é muito antigo, mas eu só queria dizer a quem está procurando a mesma coisa que existe uma API pública e oficial para fazer isso: get_fields()eget_field()

Uso:

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

https://docs.djangoproject.com/en/1.8/ref/models/meta/

PirosB3
fonte
6
Esta é realmente a resposta certa para a versão mais recente do django.
chhantyal
3
Uma pergunta: se são "públicos e oficiais", por que ainda estão abaixo _meta?
krubo
9

Agora existe um método especial - get_fields ()

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

Ele aceita dois parâmetros que podem ser usados ​​para controlar quais campos são retornados:

  • include_parents

    True por padrão. Recursivamente inclui campos definidos nas classes pai. Se definido como False, get_fields () procurará apenas campos declarados diretamente no modelo atual. Os campos de modelos que herdam diretamente de modelos abstratos ou classes de proxy são considerados locais, não no pai.

  • include_hidden

    Falso por padrão. Se definido como True, get_fields () incluirá campos usados ​​para apoiar a funcionalidade de outros campos. Isso também inclui todos os campos que possuem um_nome relacionado (como ManyToManyField ou ForeignKey) que começam com um "+"

praga de pecos
fonte
9

get_fields()retorna ae tuplecada elemento é um Model fieldtipo, que não pode ser usado diretamente como uma string. Então, field.nameretornará o nome do campo

my_model_fields = [field.name for field in MyModel._meta.get_fields()]
O código acima retornará uma lista contendo todos os campos nome

Exemplo

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']
JPG
fonte
Ótima resposta! Obrigado!
Tms91
8

Isso é feito pelo próprio Django ao criar um formulário a partir de um modelo. Ele está usando o atributo _meta, mas, como observou Bernhard, ele usa _meta.fields e _meta.many_to_many. Olhando para django.forms.models.fields_for_model, é assim que você pode fazer:

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

Os campos de modelo contidos por _meta são listados em vários locais como listas dos respectivos objetos de campo. Pode ser mais fácil trabalhar com eles como um dicionário em que as chaves sejam os nomes dos campos.

Na minha opinião, esta é a maneira mais irredundante e expressiva de coletar e organizar os objetos de campo do modelo:

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

(Consulte Este exemplo de uso em django.forms.models.fields_for_model.)

jxqz
fonte
2
Você pode acompanhar seu código com uma breve descrição do que está fazendo.
Bas van Dijk
2

Que tal este.

fields = Model._meta.fields
JDE876
fonte
1
Eu imagino que alguém queira acessar as propriedades de um campo. Além disso, como apontado nas respostas anteriores, existem many_to_manye virtual_fields.
Jocke
@Jocke você está certo. Pode ser necessário acessar outro atributo. Resposta editada.
JDE876
2

Conforme a documentação do django 2.2, você pode usar:

Para obter todos os campos: Model._meta.get_fields()

Para obter um campo individual: Model._meta.get_field('field name')

ex. Session._meta.get_field('expire_date')

Devang Padhiyar
fonte
1

Se você precisar disso para o seu site de administração , também existe o ModelAdmin.get_fieldsmétodo ( docs ), que retorna o listnome do campostrings .

Por exemplo:

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
        ...
djvg
fonte
0

Outra maneira é adicionar funções ao modelo e, quando você deseja substituir a data, pode chamar a função.

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()
Thomas Turner
fonte