django-models template - Django:Liste der Modellfelder abrufen?




if safe (9)

Ich habe eine User definiert, die (letztendlich) von models.Model erbt. Ich möchte eine Liste aller für dieses Modell definierten Felder erhalten. Beispiel: phone_number = CharField(max_length=20) . Grundsätzlich möchte ich alles abrufen, das von der Field Klasse erbt.

Ich dachte, dass ich diese abrufen könnte, indem ich inspect.getmembers(model) ausnutze, aber die Liste, die es zurückgibt, enthält keines dieser Felder. Es sieht so aus, als ob Django die Klasse bereits erobert und alle seine magischen Attribute hinzugefügt und alles entfernt hat, 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.


Answers

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

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

Für Django-Versionen 1.8 und höher:

Die Methode get_all_field_names() wird ab Django 1.8 als veraltet betrachtet und 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 [f.name for f in MyModel._meta.get_fields()] funktionieren.

Für Django-Versionen vor 1.8:

model._meta.get_all_field_names()

Das sollte den Trick machen.

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


Nur um hinzuzufügen, ich benutze Selbst-Objekt, das hat für mich funktioniert:

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

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

Hier ist der rückwärtskompatible Vorschlag der Dokumentation :

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

Zumindest mit Django 1.9.9 - der Version, die ich gerade benutze - beachte, dass .get_fields() auch jedes fremde Modell als ein Feld betrachtet, was problematisch sein kann. Sag du hast:

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 @Rockallite gezeigt

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

Dies 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_fields enthält keine Viele-zu-Viele-Felder. Sie sollten sie mit Model._meta.local_many_to_many .


Es ist nicht klar, ob Sie eine Instanz der Klasse oder der Klasse selbst haben und versuchen, die Felder abzurufen, aber denken Sie in jedem Fall an den folgenden Code

Eine Instanz verwenden

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 verwenden

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'

Ich finde das Hinzufügen zu Django-Modellen ziemlich hilfreich:

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 Folgendes tun:

for field, val in object:
    print field, val

Wie wäre es mit.....

Definieren Sie auf Ihrem MemberSerializer ein Feld wie:

groups = MembershipSerializer(source='membership_set', many=True)

und dann auf Ihrem Mitgliedschaft Serialisierer können Sie dies erstellen:

class MembershipSerializer(serializers.HyperlinkedModelSerializer):

    id = serializers.Field(source='group.id')
    name = serializers.Field(source='group.name')

    class Meta:
        model = Membership

        fields = ('id', 'name', 'join_date', )

Dies hat den allgemeinen Effekt, dass Sie einen serialisierten Wert, Gruppen, erstellen, der die gewünschte Mitgliedschaft als Quelle hat, und dann verwendet er einen benutzerdefinierten Serializer, um die auszugebenden Bits herauszuziehen.

EDIT: wie von @ Bryanph, serializers.field wurde in serializers.ReadOnlyField in DRF 3.0 umbenannt, so sollte dies lesen:

class MembershipSerializer(serializers.HyperlinkedModelSerializer):

    id = serializers.ReadOnlyField(source='group.id')
    name = serializers.ReadOnlyField(source='group.name')

    class Meta:
        model = Membership

        fields = ('id', 'name', 'join_date', )

für jede moderne Implementierung





django django-models