Django: ottenere l'elenco dei campi del modello?


196

Ho definito una Userclasse che (alla fine) eredita models.Model. Voglio ottenere un elenco di tutti i campi definiti per questo modello. Ad esempio phone_number = CharField(max_length=20),. Fondamentalmente, voglio recuperare tutto ciò che eredita dalla Fieldclasse.

Ho pensato che sarei stato in grado di recuperarli sfruttandoli inspect.getmembers(model), ma l'elenco che restituisce non contiene nessuno di questi campi. Sembra che Django abbia già preso possesso della classe e aggiunto tutti i suoi attributi magici e messo a nudo ciò che è stato effettivamente definito. Quindi ... come posso ottenere questi campi? Probabilmente hanno una funzione per recuperarli per i loro scopi interni?


Questo potrebbe aiutare, anche pypi.python.org/pypi/django-inspect-model/0.5
Paolo

possibile duplicato dei campi Ottieni modello in Django
markpasc

Risposte:


46

Poiché la maggior parte delle risposte è obsoleta, proverò ad aggiornarti su Django 2.2. Qui post: la tua app (post, blog, negozio, ecc.)

1) Dal link del modello : 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()

Nota che:

all_fields=BlogPost._meta.get_fields()

Otterrà anche alcune relazioni, che, ad esempio: non è possibile visualizzare in una vista.
Come nel mio 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) Dall'istanza

from posts.model import BlogPost

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

3) Dal modello principale

Supponiamo di avere Post come modello principale e che tu voglia vedere tutti i campi in un elenco e che i campi principali siano di sola lettura in modalità Modifica.

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
Presumo che sia corretto e lo accetterò come nuova risposta. Grazie per l'aggiornamento!
aprono il

@mpen Non lo rivendicherai nel modo migliore o più pitonico ma sotto otterrai / dovresti ottenere i valori che vorresti visualizzare in una vista, quindi se vuoi le intestazioni di una tabella HTML. Quando get_fields () restituisce una tupla, puoi iterare su di essa e ottenere i valori che sembrano appname.Model.field_name, sotto pulisce i valori dal secondo punto, include il caso in cui un carattere di sottolineatura è stato usato nel nome del campo come oltre a renderli un titolo, quindi modificali secondo necessità per ogni situazione unica. clean_field_names = [str(h).split('.')[2].replace("_", " ").title() for h in all_fields]
Alejandro Suarez,

1
L'esempio di esempio dovrebbe essere:all_fields = bp._meta.fields
George Kettleborough, il

@GeorgeKettleborough vedo il punto. Non funziona direttamente anche dalla classe? Non ho l'esempio su cui testare. In ogni caso l'esempio è di circa esempio, quindi dovrebbe essere da bp o almeno bp .__ class__
Maks

294

Django versioni 1.8 e successive:

Dovresti usare get_fields():

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

Il get_all_field_names()metodo è obsoleto a partire da Django 1.8 e verrà rimosso in 1.10 .

La pagina della documentazione collegata sopra fornisce un'implementazione pienamente compatibile con le versioni precedenti get_all_field_names(), ma per la maggior parte degli scopi l'esempio precedente dovrebbe funzionare bene.


Versioni di Django precedenti alla 1.8:

model._meta.get_all_field_names()

Questo dovrebbe fare il trucco.

Ciò richiede un'istanza del modello effettiva. Se tutto ciò che hai è una sottoclasse di django.db.models.Model, allora dovresti chiamaremyproject.myapp.models.MyModel._meta.get_all_field_names()


11
Volevo anche gli oggetti, non solo i loro nomi. Questo sembra essere disponibile in model._meta.fieldssebbene, e i loro nomi sono recuperabili con field.nameesso sembra. Spero solo che questo sia il modo più stabile per recuperare queste informazioni :)
mpen

2
non del tutto sicuro. Il carattere di sottolineatura sembra indicare che è un'API interna, sarebbe bello se i ragazzi di Django lo promuovessero fino a un appello di metodo effettivamente pubblico django.db.models.Model. Ci approfondirò e vedrò cosa riesco a trovare
rossipedia,

2
Immagino che farlo tramite l' _metaattributo sia l'unico modo ... Inoltre cerca i _meta.many_to_manycampi ManyToMany!
Bernhard Vallant,

3
È una buona soluzione ma questo metodo include i campi di relazione inversa come ForeignKey inversa e quelli non sono esattamente "campi". Qualcuno sa come distinguere i campi attuali?
viridis,

2
@rossipedia: Solo notando che l'API ._meta è pubblica (anche se era privata, probabilmente quando questa risposta è stata scritta per la prima volta): docs.djangoproject.com/en/1.8/ref/models/meta/…
Nick S

76

Il get_all_related_fields()metodo qui menzionato è stato deprecato in 1.8 . Da ora in poi lo è get_fields().

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

1
Questa dovrebbe ora essere la risposta accettata. Terse, e al punto.
Arnaud P,

Sì questa è la risposta.
eric,

55

Trovo molto utile aggiungere questo ai modelli di django:

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

Questo ti permette di fare:

for field, val in object:
    print field, val

2
Che dire di ForeignKey? Ho errori come questodjango.db.models.fields.related.RelatedObjectDoesNotExist: CustomModel has no custom_attribute.
SAKrisT

ForeignKeyfunziona bene per me. Sebbene, catturare silenziosamente tutte le eccezioni sia un anti-schema. Molto meglio catturare AttributeError, o almeno registrare che qualche eccezione è stata ingerita silenziosamente.
Sardathrion - contro l'abuso da SE del

1
self._meta.get_all_field_names()è stato ammortizzato e rimosso. Puoi usare qualcosa del genere for field in self._meta.get_fields()e poiyield (field.name, field.value_from_object(self))
MackM,

13

Questo fa il trucco. L'ho provato solo in Django 1.7.

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

Model._meta.local_fieldsnon contiene campi molti a molti. Dovresti farli usare Model._meta.local_many_to_many.


10

Non è chiaro se si dispone di un'istanza della classe o della classe stessa e si sta tentando di recuperare i campi, ma in entrambi i casi, considerare il codice seguente

Utilizzando un'istanza

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 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'

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)

Questo ha funzionato per me django==1.11.8


8

MyModel._meta.get_all_field_names()è stato deprecato diverse versioni indietro e rimosso in Django 1.10.

Ecco il suggerimento retrocompatibile dei documenti :

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

6

Solo per aggiungere, sto usando self object, questo ha funzionato per me:

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

5

Almeno con Django 1.9.9 - la versione che sto attualmente usando -, nota che in .get_fields()realtà "considera" anche qualsiasi modello straniero come un campo, il che può essere problematico. Dì che hai:

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

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

Ne consegue che

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

mentre, come mostrato da @Rockallite

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

4

Quindi, prima di trovare questo post, l'ho trovato correttamente funzionante.

Model._meta.fields

Funziona allo stesso modo

Model._meta.get_fields()

Non sono sicuro di quale sia la differenza nei risultati, se ce n'è uno. Ho eseguito questo loop e ho ottenuto lo stesso output.

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

-2

Perché non usarlo solo:

manage.py inspectdb

Esempio di output:

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'),)

1
Questa non è una risposta alla domanda.
Shayne,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.