Django: ¿Obtener una lista de campos modelo?

196

He definido una Userclase que (en última instancia) hereda de models.Model. Quiero obtener una lista de todos los campos definidos para este modelo. Por ejemplo, phone_number = CharField(max_length=20). Básicamente, quiero recuperar cualquier cosa que herede de la Fieldclase.

Pensé que podría recuperarlos aprovechando inspect.getmembers(model), pero la lista que devuelve no contiene ninguno de estos campos. Parece que Django ya se ha apoderado de la clase y ha agregado todos sus atributos mágicos y ha eliminado lo que realmente se ha definido. Entonces ... ¿cómo puedo obtener estos campos? ¿Probablemente tienen una función para recuperarlos para sus propios fines internos?

mpen
fuente
Esto también podría ayudar pypi.python.org/pypi/django-inspect-model/0.5
Paolo
posible duplicado de los campos Obtener modelo en Django
markpasc

Respuestas:

46

Como la mayoría de las respuestas están desactualizadas, intentaré actualizarlo en Django 2.2 Aquí publica su aplicación (publicaciones, blog, tienda, etc.)

1) Desde el enlace del 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()

Tenga en cuenta que:

all_fields=BlogPost._meta.get_fields()

También obtendrá algunas relaciones, que, por ejemplo: no puede mostrar en una vista.
Como en mi caso:

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

y

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

2) De instancia

from posts.model import BlogPost

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

3) Del modelo principal

Supongamos que tenemos Publicar como modelo principal y desea ver todos los campos en una lista, y que los campos principales sean de solo lectura en el modo Edición.

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
Maks
fuente
1
Asumiré que esto es correcto y lo aceptaré como la nueva respuesta. ¡Gracias por la actualización!
mpen
@mpen No lo reclamará de la mejor manera o la más pitónica, pero a continuación obtendrá / debería obtener los valores que desea mostrar en una vista, por lo que los encabezados de una tabla HTML si lo desea. Como get_fields () devuelve una tupla, puede iterar sobre ella y obtener los valores que se parecen a appname.Model.field_name, a continuación, limpia los valores del segundo punto, incluye el caso en el que se utilizó un guión bajo en el nombre del campo como así como convertirlos en un título, así que modifíquelos según sea necesario para cada situación única. clean_field_names = [str(h).split('.')[2].replace("_", " ").title() for h in all_fields]
Alejandro Suárez
1
El ejemplo de ejemplo debería ser:all_fields = bp._meta.fields
George Kettleborough
@ GeorgeKettleborough veo el punto. ¿No funciona directamente desde la clase también? No tengo el ejemplo para probar. De todos modos, el ejemplo es sobre instancia, por lo que debe ser de bp o al menos bp .__ class__
Maks
294

Django versiones 1.8 y posteriores:

Debes usar get_fields():

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

El get_all_field_names()método está en desuso a partir de Django 1.8 y se eliminará en 1.10 .

La página de documentación vinculada anteriormente proporciona una implementación totalmente compatible con versiones anteriores get_all_field_names(), pero para la mayoría de los propósitos, el ejemplo anterior debería funcionar bien.


Versiones de Django anteriores a la 1.8:

model._meta.get_all_field_names()

Eso debería hacer el truco.

Eso requiere una instancia de modelo real. Si todo lo que tiene es una subclase de django.db.models.Model, entonces debe llamarmyproject.myapp.models.MyModel._meta.get_all_field_names()

rossipedia
fuente
11
También quería los objetos, no solo sus nombres. Sin model._meta.fieldsembargo, esto parece estar disponible , y sus nombres se pueden recuperar con field.nameél. Solo espero que esta sea la forma más estable de recuperar esta información :)
mpen
2
No del todo seguro. El guión bajo parece indicar que es una API interna, sería genial si los chicos de Django promovieran esto a una llamada de método realmente pública django.db.models.Model. Lo
investigaré
2
Supongo que hacerlo a través del _metaatributo es la única forma ... ¡Además, busca los _meta.many_to_manycampos ManyToMany!
Bernhard Vallant
3
Es una buena solución, pero este método incluye los campos de relación inversa como una ForeignKey inversa y esos no son exactamente "campos". ¿Alguien sabe distinguir los campos reales?
viridis
2
@rossipedia: Solo notando que la API ._meta es pública (aunque solía ser privada, probablemente cuando esta respuesta se escribió por primera vez): docs.djangoproject.com/en/1.8/ref/models/meta/…
Nick S
76

El get_all_related_fields()método mencionado aquí ha quedado en desuso en 1.8 . De ahora en adelante lo es get_fields().

>> from django.contrib.auth.models import User
>> User._meta.get_fields()
Wil
fuente
1
Esta debería ser ahora la respuesta aceptada. Conciso, y al grano.
Arnaud P
Sí, esta es la respuesta.
eric
55

Me parece útil agregar esto a los modelos de django:

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

Esto te permite hacer:

for field, val in object:
    print field, val
bjw
fuente
2
¿Qué pasa con ForeignKey? Tengo errores como estedjango.db.models.fields.related.RelatedObjectDoesNotExist: CustomModel has no custom_attribute.
SAKrisT
ForeignKeyfunciona bien para mi Aunque, capturar en silencio todas las excepciones es un antipatrón. Mucho mejor para atrapar AttributeError, o al menos registrar que alguna excepción se tragó en silencio.
Sardathrion - contra el abuso SE
1
self._meta.get_all_field_names()ha sido depreciado y eliminado. Puede usar algo como for field in self._meta.get_fields()y luegoyield (field.name, field.value_from_object(self))
MackM
13

Esto hace el truco. Solo lo pruebo en Django 1.7.

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

Model._meta.local_fieldsno contiene campos de muchos a muchos. Deberías usarlos Model._meta.local_many_to_many.

Rockallita
fuente
10

No está claro si tiene una instancia de la clase o la clase en sí misma e intenta recuperar los campos, pero de cualquier manera, considere el siguiente código

Usando una instancia

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 una clase

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
fuente
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)

Esto funcionó para mí en django==1.11.8

Vivek Anand
fuente
8

MyModel._meta.get_all_field_names()Se desaprobó varias versiones y se eliminó en Django 1.10.

Aquí está la sugerencia de los documentos compatible con versiones anteriores :

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
fuente
6

Solo para agregar, estoy usando un objeto propio, esto funcionó para mí:

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

Al menos con Django 1.9.9 , la versión que estoy usando actualmente, tenga en cuenta que en .get_fields()realidad también "considera" cualquier modelo extraño como un campo, lo que puede ser problemático. Digamos que tienes:

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

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

Resulta que

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

mientras, como lo muestra @Rockallite

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

Entonces, antes de encontrar esta publicación, encontré que funcionaba con éxito.

Model._meta.fields

Funciona igualmente como

Model._meta.get_fields()

No estoy seguro de cuál es la diferencia en los resultados, si hay uno. Ejecuté este bucle y obtuve el mismo resultado.

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

¿Por qué no solo usar eso?

manage.py inspectdb

Salida de ejemplo:

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
fuente
1
Esta no es una respuesta a la pregunta.
Shayne