Holen Sie sich die Felder des Modells in Django

121

Bei einem Django-Modell versuche ich, alle seine Felder aufzulisten. Ich habe einige Beispiele dafür mit dem _meta-Modellattribut gesehen, aber zeigt der Unterstrich vor dem Meta nicht an, dass das _meta-Attribut ein privates Attribut ist und nicht direkt aufgerufen werden sollte? ... weil sich beispielsweise das Layout von _meta in Zukunft ändern könnte und keine stabile API sein könnte?

Ist _meta eine Ausnahme von dieser Regel? Ist es stabil und gebrauchsfertig oder wird es als schlechte Praxis angesehen, darauf zuzugreifen? Oder gibt es eine Funktion oder eine andere Möglichkeit, die Felder eines Modells ohne Verwendung des Attributs _meta zu überprüfen? Unten finden Sie eine Liste einiger Links, die zeigen, wie dies mit dem Attribut _meta durchgeführt wird

Jeder Rat wird sehr geschätzt.

django Objekt get / set Feld

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

Wie kann man Django-Modellfelder überprüfen?

Joe J.
quelle
Mögliches Duplikat von Django: Liste der Modellfelder abrufen?
Anto

Antworten:

144

_metaist privat, aber es ist relativ stabil. Es wird versucht, es zu formalisieren, zu dokumentieren und den Unterstrich zu entfernen, was vor 1.3 oder 1.4 geschehen kann. Ich kann mir vorstellen, dass Anstrengungen unternommen werden, um sicherzustellen, dass die Dinge abwärtskompatibel sind, da viele Leute sie sowieso benutzt haben.

Wenn Sie besonders über die Kompatibilität besorgt sind, schreiben Sie eine Funktion, die ein Modell verwendet und die Felder zurückgibt. Das heißt, wenn sich in Zukunft etwas ändert, müssen Sie nur eine Funktion ändern.

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

Ich glaube, dies wird eine Liste von FieldObjekten zurückgeben. Verwenden Sie, um den Wert jedes Felds aus der Instanz abzurufen getattr(instance, field.name).

Update: Django-Mitarbeiter arbeiten an einer API, um das _Meta-Objekt als Teil eines Google Summer of Code zu ersetzen. Siehe:
- https://groups.google.com/forum/#!topic/django-developers/hD4roZq0wyk
- https://code.djangoproject.com/wiki/new_meta_api

Will Hardy
quelle
45
Sie sollten sich auch der Tatsache bewusst sein, dass Sie zugreifen müssen, wenn Sie auch die vielen zu vielen Felder benötigen model._meta.many_to_many!
Bernhard Vallant
1
Vielen Dank, Will. Gut zu wissen, dass auch andere Leute _meta verwenden. Ich mag die Idee, eine Wrapper-Funktion zu haben. Lazerscience, danke auch. Gut zu wissen, dass es eine gute Methode gibt, um die Felder many_to_many abzurufen. Joe
Joe J
3
django/core/management/commands/loaddata.pyverwendet _meta, um den Baum von Apps, Modellen und Feldern zu durchsuchen. Schönes Muster zu folgen ... und Sie können wetten, es ist der "offizielle Weg".
Kochfelder
2
Wenn Sie generische Fremdschlüssel verwenden, sollten Sie auch überprüfen_meta.virtual_fields
andrei1089
7
_metaist jetzt eine formale, unterstützte API (neu in Django 1.8)
mgalgs
97

Ich weiß, dass dieser Beitrag ziemlich alt ist, aber ich wollte nur jedem, der nach dem gleichen Thema sucht, mitteilen, dass es eine öffentliche und offizielle API gibt, um dies zu tun: get_fields()undget_field()

Verwendung:

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

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

PirosB3
quelle
6
Dies ist eigentlich die richtige Antwort für eine neuere Version von Django.
Chhantyal
3
Eine Frage: Wenn diese "öffentlich und offiziell" sind, warum sind sie dann noch unter _meta?
Krubo
9

Jetzt gibt es eine spezielle Methode - get_fields ()

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

Es werden zwei Parameter akzeptiert, mit denen gesteuert werden kann, welche Felder zurückgegeben werden:

  • include_parents

    Standardmäßig True. Enthält rekursiv Felder, die für übergeordnete Klassen definiert sind. Bei der Einstellung False sucht get_fields () nur nach Feldern, die direkt im aktuellen Modell deklariert sind. Felder von Modellen, die direkt von abstrakten Modellen oder Proxy-Klassen erben, gelten als lokal und nicht auf dem übergeordneten Modell.

  • include_hidden

    Standardmäßig falsch. Wenn auf True gesetzt, enthält get_fields () Felder, mit denen die Funktionalität anderer Felder unterstützt wird. Dies schließt auch alle Felder mit einem verwandten Namen ein (z. B. ManyToManyField oder ForeignKey), die mit einem „+“ beginnen.

Pecos Schädling
quelle
9

get_fields()gibt a zurück tupleund jedes Element ist ein Model fieldTyp, der nicht direkt als Zeichenfolge verwendet werden kann. Gibt also field.nameden Feldnamen zurück

my_model_fields = [field.name for field in MyModel._meta.get_fields()]
Der obige Code gibt eine Liste zurück, die alle Feldnamen enthält.

Beispiel

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
quelle
Gute Antwort! Vielen Dank!
Tms91
8

Dies wird von Django selbst durchgeführt, wenn ein Formular aus einem Modell erstellt wird. Es verwendet das Attribut _meta, aber wie Bernhard bemerkte, verwendet es sowohl _meta.fields als auch _meta.many_to_many. Wenn Sie sich django.forms.models.fields_for_model ansehen, können Sie dies folgendermaßen tun:

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

Die in _meta enthaltenen Modellfelder werden an mehreren Stellen als Listen der jeweiligen Feldobjekte aufgelistet. Es kann einfacher sein, mit ihnen als Wörterbuch zu arbeiten, wobei die Schlüssel die Feldnamen sind.

Meiner Meinung nach ist dies die irredundanteste und ausdrucksstärkste Art, die Modellfeldobjekte zu sammeln und zu organisieren:

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

(Siehe Verwendung dieses Beispiels in django.forms.models.fields_for_model.)

jxqz
quelle
2
Möglicherweise möchten Sie Ihrem Code eine kurze Beschreibung der Funktionsweise beifügen.
Bas van Dijk
2

Wie wäre es mit diesem.

fields = Model._meta.fields
JDE876
quelle
1
Ich kann mir vorstellen, dass man auf die Eigenschaften eines Feldes zugreifen möchte. Wie bereits in früheren Antworten erwähnt, gibt es auch many_to_manyund virtual_fields.
Jocke
@ Jocke richtig du bist. Es kann erforderlich sein, auf ein anderes Attribut zuzugreifen. Antwort bearbeitet.
JDE876
2

Gemäß der Django-Dokumentation 2.2 können Sie Folgendes verwenden:

So erhalten Sie alle Felder: Model._meta.get_fields()

So erhalten Sie ein individuelles Feld: Model._meta.get_field('field name')

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

Devang Padhiyar
quelle
1

Wenn Sie dies für Ihre Admin- Site benötigen , gibt es auch die ModelAdmin.get_fieldsMethode ( docs ), die einen listFeldnamen zurückgibt strings.

Beispielsweise:

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

Eine andere Möglichkeit besteht darin, dem Modell Funktionen hinzuzufügen. Wenn Sie das Datum überschreiben möchten, können Sie die Funktion aufrufen.

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
quelle