Django: Liste der Modellfelder abrufen?

196

Ich habe eine UserKlasse definiert , von der (letztendlich) geerbt wird models.Model. Ich möchte eine Liste aller für dieses Modell definierten Felder erhalten. Zum Beispiel phone_number = CharField(max_length=20). Grundsätzlich möchte ich alles abrufen, was von der FieldKlasse erbt .

Ich dachte, ich könnte diese abrufen, indem ich sie ausnütze inspect.getmembers(model), aber die zurückgegebene Liste enthält keines dieser Felder. Es sieht so aus, als hätte Django die Klasse bereits in den Griff bekommen und all ihre magischen Attribute hinzugefügt und herausgenommen, was tatsächlich definiert wurde. Also ... wie kann ich diese Felder bekommen? Sie haben wahrscheinlich eine Funktion, um sie für ihre eigenen internen Zwecke abzurufen?

mpen
quelle
Dies könnte auch helfen pypi.python.org/pypi/django-inspect-model/0.5
Paolo
Mögliches Duplikat der Felder
Markpasc

Antworten:

46

Da die meisten Antworten veraltet sind, werde ich versuchen, Sie auf Django 2.2 zu aktualisieren. Hier Beiträge - Ihre App (Beiträge, Blog, Shop usw.)

1) Vom Modelllink : https://docs.djangoproject.com/de/2.2/ref/models/meta/

from posts.model import BlogPost

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

Beachten Sie, dass:

all_fields=BlogPost._meta.get_fields()

Wird auch einige Beziehungen bekommen, die zum Beispiel. Sie können nicht in einer Ansicht anzeigen.
Wie in meinem Fall:

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

und

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

2) Von der Instanz

from posts.model import BlogPost

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

3) Vom übergeordneten Modell

Nehmen wir an, wir haben Post als übergeordnetes Modell und Sie möchten, dass alle Felder in einer Liste angezeigt werden und die Felder der übergeordneten Felder im Bearbeitungsmodus schreibgeschützt sind.

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
quelle
1
Ich gehe davon aus, dass dies korrekt ist, und akzeptiere es als neue Antwort. Danke für das Update!
Mpen
@mpen Wird nicht den besten oder pythonischsten Weg beanspruchen, aber unten werden / sollten die Werte angezeigt werden, die Sie in einer Ansicht anzeigen möchten, also die Überschriften einer HTML-Tabelle, wenn Sie möchten. Wenn get_fields () ein Tupel zurückgibt, können Sie darüber iterieren und die Werte abrufen, die wie appname.Model.field_name aussehen. Bereinigt die Werte ab dem zweiten Punkt und enthält den Fall, in dem ein Unterstrich im Feldnamen als verwendet wurde Machen Sie sie zu einem Titel, und ändern Sie sie nach Bedarf für jede einzelne Situation. clean_field_names = [str(h).split('.')[2].replace("_", " ").title() for h in all_fields]
Alejandro Suarez
1
Das Beispiel aus der Instanz sollte sein:all_fields = bp._meta.fields
George Kettleborough
@ GeorgeKettleborough Ich sehe den Punkt. Funktioniert es nicht auch direkt aus dem Unterricht? Ich habe kein Beispiel zum Testen. Wie auch immer, das Beispiel handelt von einer Instanz, also sollte es von bp oder zumindest von bp .__ class__ stammen
Maks
294

Django-Versionen 1.8 und höher:

Sie sollten verwenden get_fields():

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

Die get_all_field_names()Methode ist ab Django 1.8 veraltet und wird in 1.10 entfernt .

Die oben verlinkte Dokumentationsseite bietet eine vollständig abwärtskompatible Implementierung von get_all_field_names(), aber für die meisten Zwecke sollte das vorherige Beispiel einwandfrei funktionieren.


Django-Versionen vor 1.8:

model._meta.get_all_field_names()

Das sollte den Trick machen.

Das erfordert eine tatsächliche Modellinstanz. Wenn Sie nur eine Unterklasse von haben django.db.models.Model, sollten Sie anrufenmyproject.myapp.models.MyModel._meta.get_all_field_names()

rossipedia
quelle
11
Wollte auch die Objekte, nicht nur ihre Namen. Dies scheint jedoch verfügbar zu sein model._meta.fields, und ihre Namen sind damit abrufbar field.name. Ich hoffe nur, dass dies der stabilste Weg ist, um diese Informationen abzurufen :)
mpen
2
nicht ganz sicher. Der Unterstrich scheint darauf hinzudeuten, dass es sich um eine interne API handelt. Es wäre cool, wenn die Django-Leute dies bis zu einem tatsächlich öffentlichen Methodenaufruf bewerben würden django.db.models.Model. Ich werde mich damit
befassen
2
Ich denke, dies über das _metaAttribut zu tun , ist der einzige Weg ... _meta.many_to_manySuchen Sie zusätzlich nach ManyToMany-Feldern!
Bernhard Vallant
3
Es ist eine gute Lösung, aber diese Methode enthält die umgekehrten Beziehungsfelder wie einen umgekehrten ForeignKey und diese sind nicht genau "Felder". Weiß jemand, wie man die tatsächlichen Felder unterscheidet?
Viridis
2
@rossipedia: Ich stelle nur fest, dass die ._meta-API öffentlich ist (obwohl sie früher privat war, wahrscheinlich als diese Antwort zum ersten Mal geschrieben wurde): docs.djangoproject.com/de/1.8/ref/models/meta/…
Nick S
76

Die get_all_related_fields()hier erwähnte Methode ist in 1.8 veraltet . Von jetzt an ist es get_fields().

>> from django.contrib.auth.models import User
>> User._meta.get_fields()
Wil
quelle
1
Dies sollte nun die akzeptierte Antwort sein. Knapp und auf den Punkt.
Arnaud P
Ja das ist die Antwort.
Eric
55

Ich finde es sehr hilfreich, dies zu Django-Modellen hinzuzufügen:

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

So können Sie:

for field, val in object:
    print field, val
bjw
quelle
2
Was ist mit ForeignKey? Ich habe Fehler wie diesendjango.db.models.fields.related.RelatedObjectDoesNotExist: CustomModel has no custom_attribute.
SAKrisT
ForeignKeyArbeit gut für mich. Das stille Fangen aller Ausnahmen ist jedoch ein Anti-Muster. Viel besser zu fangen AttributeErroroder zumindest zu protokollieren, dass eine Ausnahme lautlos verschluckt wurde.
Sardathrion - gegen SE-Missbrauch
1
self._meta.get_all_field_names()wurde abgeschrieben und entfernt. Sie können so etwas wie for field in self._meta.get_fields()und dannyield (field.name, field.value_from_object(self))
MackM
13

Das macht den Trick. Ich teste es nur in Django 1.7.

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

Model._meta.local_fieldsenthält keine Viele-zu-Viele-Felder. Sie sollten sie mit bekommen Model._meta.local_many_to_many.

Rockallite
quelle
10

Es ist nicht klar, ob Sie eine Instanz der Klasse oder der Klasse selbst haben und versuchen, die Felder abzurufen, aber in beiden Fällen sollten Sie den folgenden Code berücksichtigen

Verwenden einer Instanz

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

Eine Klasse benutzen

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

Das hat bei mir funktioniert django==1.11.8

Vivek Anand
quelle
8

MyModel._meta.get_all_field_names()wurde als veraltet mehrere Versionen zurück und entfernt in Django 1.10.

Hier ist der abwärtskompatible Vorschlag aus den Dokumenten :

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

Nur um hinzuzufügen, ich benutze Selbstobjekt, das hat bei mir funktioniert:

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

Beachten Sie zumindest bei Django 1.9.9 - der Version, die ich derzeit verwende -, dass .get_fields()tatsächlich auch jedes fremde Modell als Feld "betrachtet" wird, was problematisch sein kann. Angenommen, Sie haben:

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

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

Es folgt dem

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

während, wie von @Rockallite gezeigt

>>> map(lambda field:field.name, Parent._model._meta.local_fields)
['id']
bleib am Leben
quelle
4

Bevor ich diesen Beitrag gefunden habe, habe ich erfolgreich festgestellt, dass er funktioniert.

Model._meta.fields

Es funktioniert genauso wie

Model._meta.get_fields()

Ich bin mir nicht sicher, was der Unterschied in den Ergebnissen ist, wenn es einen gibt. Ich habe diese Schleife ausgeführt und die gleiche Ausgabe erhalten.

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

Warum nicht einfach das benutzen:

manage.py inspectdb

Beispielausgabe:

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
quelle
1
Dies ist keine Antwort auf die Frage.
Shayne