Django: Obter lista de campos de modelo?

196

Eu defini uma Userclasse que (em última análise) herda models.Model. Quero obter uma lista de todos os campos definidos para este modelo. Por exemplo phone_number = CharField(max_length=20),. Basicamente, quero recuperar qualquer coisa que herda da Fieldclasse.

Eu pensei que seria capaz de recuperá-los aproveitando inspect.getmembers(model), mas a lista que ele retorna não contém nenhum desses campos. Parece que o Django já conseguiu a classe e adicionou todos os seus atributos mágicos e retirou o que realmente foi definido. Então ... como posso obter esses campos? Eles provavelmente têm uma função para recuperá-los para seus próprios fins internos?

mpen
fonte
Isso também pode ajudar pypi.python.org/pypi/django-inspect-model/0.5
Paolo
possível duplicata de campos do modelo Get em Django
markpasc

Respostas:

46

Como a maioria das respostas está desatualizada, tentarei atualizá-lo no Django 2.2. Aqui postagens - seu aplicativo (postagens, blog, loja, etc.)

1) No link do modelo : https://docs.djangoproject.com/en/2.2/ref/models/meta/

from posts.model import BlogPost

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

Observe que:

all_fields=BlogPost._meta.get_fields()

Também terá alguns relacionamentos, que por ex. você não pode exibir em uma exibição.
Como no meu caso:

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

e

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

2) Da instância

from posts.model import BlogPost

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

3) Do modelo pai

Vamos supor que temos Post como modelo pai e você deseja ver todos os campos em uma lista e que os campos pai sejam somente leitura no modo de edição.

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




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

    list_display = all_fields
    read_only = parent_fields
Maks
fonte
1
Vou assumir que isso está correto e aceitá-lo como a nova resposta. Obrigado pela atualização!
MPEN
@mpen Não reivindicá-lo-á da melhor maneira ou da forma mais pitônica, mas abaixo receberá / deve obter os valores que você deseja exibir em uma exibição, portanto os cabeçalhos de uma tabela HTML, se desejar. Como get_fields () retorna uma tupla, você pode iterar sobre ela e obter os valores que se parecem com appname.Model.field_name, abaixo limpa os valores do segundo ponto, inclui o caso em que um sublinhado foi usado no nome do campo como além de torná-los um título, modifique conforme necessário para cada situação única. clean_field_names = [str(h).split('.')[2].replace("_", " ").title() for h in all_fields]
Alejandro Suarez
1
O exemplo de exemplo deve ser:all_fields = bp._meta.fields
George Kettleborough
@GeorgeKettleborough eu vejo o ponto. Também não funciona diretamente da classe? Não tem o exemplo para testar. De qualquer forma o exemplo é de cerca de exemplo, por isso, deve ser de, pelo menos, pb ou pb .__ class__
Maks
294

Django versões 1.8 e posteriores:

Você deve usar get_fields():

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

O get_all_field_names()método foi descontinuado a partir do Django 1.8 e será removido na 1.10 .

A página de documentação vinculada acima fornece uma implementação totalmente compatível com versões anteriores get_all_field_names(), mas, na maioria dos casos, o exemplo anterior deve funcionar bem.


Versões do Django anteriores à 1.8:

model._meta.get_all_field_names()

Isso deve fazer o truque.

Isso requer uma instância de modelo real. Se tudo o que você tem é uma subclasse de django.db.models.Model, então você deve chamarmyproject.myapp.models.MyModel._meta.get_all_field_names()

rossipedia
fonte
11
Também queria os objetos, não apenas seus nomes. Isso parece estar disponível no model._meta.fieldsentanto, e seus nomes são recuperáveis ​​com o field.nameque parece. Eu só espero que esta é a maneira mais estável para recuperar esta informação :)
MPEN
2
não totalmente certo. O sublinhado parece indicar que é uma API interna, seria legal se os caras do Django promovessem isso até uma chamada de método realmente pública django.db.models.Model. Eu vou cavar-lo e ver o que posso encontrar
rossipedia
2
Eu acho que fazê-lo através do _metaatributo é a única maneira ... Além disso, procure _meta.many_to_manypor campos ManyToMany!
Bernhard Vallant
3
É uma boa solução, mas esse método inclui os campos de relação inversa, como uma ForeignKey reversa e esses não são exatamente "campos". Alguém sabe como distinguir os campos reais?
Viridis
2
@rossipedia: Apenas observando que a API ._meta é pública (embora costumava ser privada, provavelmente quando esta resposta foi escrita pela primeira vez): docs.djangoproject.com/en/1.8/ref/models/meta/…
Nick S
76

O get_all_related_fields()método mencionado aqui foi preterido em 1.8 . De agora em diante é get_fields().

>> from django.contrib.auth.models import User
>> User._meta.get_fields()
Wil
fonte
1
Agora, essa deve ser a resposta aceita. Conciso e direto ao ponto.
Arnaud P
Sim, esta é a resposta.
eric
55

Acho que adicionar isso aos modelos de django é bastante útil:

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

Isso permite que você faça:

for field, val in object:
    print field, val
bjw
fonte
2
E o ForeignKey? Eu tenho erros como estedjango.db.models.fields.related.RelatedObjectDoesNotExist: CustomModel has no custom_attribute.
SAKrisT
ForeignKeyfunciona bem para mim. Embora, capturar silenciosamente todas as exceções seja um antipadrão. Muito melhor pegar AttributeError, ou pelo menos registrar que alguma exceção foi silenciosamente engolida.
Sardathrion - contra abuso de SE 4/15
1
self._meta.get_all_field_names()foi depreciado e removido. Você pode usar algo como for field in self._meta.get_fields()e depoisyield (field.name, field.value_from_object(self))
MackM
13

Isso faz o truque. Eu só testei no Django 1.7.

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

Model._meta.local_fieldsnão contém campos muitos para muitos. Você deveria usá-los Model._meta.local_many_to_many.

Rockallite
fonte
10

Não está claro se você tem uma instância da classe ou da própria classe e está tentando recuperar os campos, mas, de qualquer forma, considere o código a seguir

Usando uma Instância

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

Usando uma classe

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'
Nader Alexan
fonte
10
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)

Isso funcionou para mim em django==1.11.8

Vivek Anand
fonte
8

MyModel._meta.get_all_field_names()foi preterido várias versões de volta e removida no Django 1.10.

Aqui está a sugestão compatível com versões anteriores dos documentos :

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)
)))
aboutaaron
fonte
6

Só para adicionar, eu estou usando auto-objeto, isso funcionou para mim:

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

Pelo menos com o Django 1.9.9 - a versão que estou usando atualmente -, observe que, .get_fields()na verdade, também "considera" qualquer modelo estrangeiro como um campo, o que pode ser problemático. Digamos que você tenha:

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

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

Segue que

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

enquanto, como mostrado por @Rockallite

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

Portanto, antes de encontrar este post, achei que funcionava com êxito.

Model._meta.fields

Funciona igualmente como

Model._meta.get_fields()

Não tenho certeza de qual é a diferença nos resultados, se houver. Eu executei esse loop e obtive a mesma saída.

for field in Model._meta.fields:
    print(field.name)
Carl Brubaker
fonte
-2

Por que não usar isso:

manage.py inspectdb

Exemplo de saída:

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'),)
Adam Pawluczuk
fonte
1
Esta não é uma resposta para a pergunta.
Shayne