Django: Uzyskaj listę pól modelu?


196

Zdefiniowałem Userklasę, która (ostatecznie) dziedziczy models.Model. Chcę uzyskać listę wszystkich pól zdefiniowanych dla tego modelu. Na przykład phone_number = CharField(max_length=20). Zasadniczo chcę odzyskać wszystko, co dziedziczy po Fieldklasie.

Myślałem, że będę w stanie je odzyskać, korzystając z inspect.getmembers(model), ale lista, którą zwraca, nie zawiera żadnego z tych pól. Wygląda na to, że Django już opanował klasę i dodał wszystkie jej magiczne atrybuty i usunął to, co faktycznie zdefiniowano. Więc ... jak mogę zdobyć te pola? Prawdopodobnie mają funkcję wyszukiwania ich do własnych celów wewnętrznych?



możliwy duplikat pól modelu Get w Django
markpasc

Odpowiedzi:


46

Ponieważ większość odpowiedzi jest nieaktualna, spróbuję zaktualizować Cię w postach Django 2.2 Here - Twoja aplikacja (posty, blog, sklep itp.)

1) Z linku do modelu : 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()

Uwaga:

all_fields=BlogPost._meta.get_fields()

Uzyska także pewne relacje, które np. nie można wyświetlić w widoku.
Jak w moim przypadku:

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

i

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

2) Z instancji

from posts.model import BlogPost

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

3) Z modelu nadrzędnego

Załóżmy, że mamy Post jako model nadrzędny i chcesz zobaczyć wszystkie pola na liście, a pola nadrzędne mają być tylko do odczytu w trybie edycji.

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

1
Zakładam, że jest to poprawne i akceptuję to jako nową odpowiedź. Dziękuję za aktualizację!
mpen

@mpen Nie twierdzę, że jest to najlepszy sposób lub najbardziej pythonowy, ale poniżej otrzyma / powinien otrzymać wartości, które chciałbyś wyświetlać w widoku, więc nagłówki tabeli HTML, jeśli chcesz. Ponieważ get_fields () zwraca krotkę, możesz iterować po nim i uzyskać wartości, które wyglądają jak nazwa_aplikacji. Model.nazwa_pola, poniżej czyści wartości z drugiej kropki, obejmuje przypadek, w którym w nazwie pola użyto podkreślenia nadaj im tytuł, więc modyfikuj odpowiednio do każdej wyjątkowej sytuacji. clean_field_names = [str(h).split('.')[2].replace("_", " ").title() for h in all_fields]
Alejandro Suarez

1
Przykładem instancji powinno być:all_fields = bp._meta.fields
George Kettleborough

@GeorgeKettleborough Widzę sens. Czy to również nie działa bezpośrednio z klasy? Nie masz przykładu do przetestowania. W każdym razie przykład dotyczy instancji, więc powinien pochodzić z bp lub co najmniej bp .__ klasa__
Maks

294

Django w wersji 1.8 i nowszych:

Powinieneś użyć get_fields():

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

get_all_field_names()Sposób jest nieaktualne począwszy od Django 1,8 i zostaną usunięte w 1,10 .

Strona dokumentacji, do której prowadzi powyższy link, zapewnia w pełni kompatybilną wstecz implementację get_all_field_names(), ale w większości przypadków poprzedni przykład powinien działać dobrze.


Wersje Django przed 1.8:

model._meta.get_all_field_names()

To powinno wystarczyć.

To wymaga rzeczywistej instancji modelu. Jeśli masz tylko podklasę django.db.models.Model, powinieneś zadzwonićmyproject.myapp.models.MyModel._meta.get_all_field_names()


11
Chciałem też przedmiotów, a nie tylko ich nazw. Wydaje się, że jest to dostępne model._meta.fields, a ich nazwy można odzyskać field.name. Mam tylko nadzieję, że jest to najbardziej stabilny sposób na odzyskanie tych informacji :)
mpen

2
nie do końca pewny. Podkreślenie wydaje się wskazywać, że jest to wewnętrzny interfejs API, byłoby fajnie, gdyby faceci Django wypromowali to do publicznego wywołania metody django.db.models.Model.
Wkopię się

2
Wydaje mi się, że zrobienie tego za pomocą _metaatrybutu jest jedynym sposobem ... Dodatkowo sprawdź _meta.many_to_manypola ManyToMany!
Bernhard Vallant

3
To dobre rozwiązanie, ale ta metoda obejmuje pola odwrotnej relacji, takie jak odwrotny klucz obcy i nie są to dokładnie „pola”. Czy ktoś wie, jak odróżnić rzeczywiste pola?
viridis

2
@rossipedia: Zwracam uwagę, że interfejs API ._meta jest publiczny (chociaż był prywatny, prawdopodobnie w momencie, gdy ta odpowiedź została napisana po raz pierwszy): docs.djangoproject.com/en/1.8/ref/models/meta/…
Nick S

76

get_all_related_fields()Metodzie opisanej w niniejszym dokumencie została zaniechana na 1,8 . Od teraz to jest get_fields().

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

1
To powinna być teraz zaakceptowana odpowiedź. Terse i do rzeczy.
Arnaud P

Tak, to jest odpowiedź.
eric

55

Uważam, że dodanie tego do modeli django jest bardzo pomocne:

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

Pozwala to na:

for field, val in object:
    print field, val

2
Co z ForeignKey? Mam takie błędydjango.db.models.fields.related.RelatedObjectDoesNotExist: CustomModel has no custom_attribute.
SAKrisT

ForeignKeydziała dobrze dla mnie. Chociaż ciche wychwytywanie wszystkich wyjątków jest anty-wzorem. Znacznie lepiej złapać AttributeError, a przynajmniej odnotować, że jakiś wyjątek został po cichu połknięty.
Sardathrion - przeciwko nadużyciom SE

1
self._meta.get_all_field_names()został amortyzowany i usunięty. Możesz użyć czegoś takiego, for field in self._meta.get_fields()a potemyield (field.name, field.value_from_object(self))
MackM

13

To załatwia sprawę. Testuję to tylko w Django 1.7.

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

Model._meta.local_fieldsnie zawiera pól wiele do wielu. Powinieneś je zdobyć Model._meta.local_many_to_many.


10

Nie jest jasne, czy masz instancję klasy, czy samą klasę i próbujesz pobrać pola, ale tak czy inaczej, rozważ następujący kod

Korzystanie z instancji

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

Korzystanie z klasy

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)

To zadziałało dla mnie w django==1.11.8


8

MyModel._meta.get_all_field_names()zostało wycofane kilka wersji z powrotem i usunięte w Django 1.10.

Oto sugestia zgodna wstecz z dokumentów :

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

Żeby dodać, używam obiektu własnego, to zadziałało dla mnie:

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

5

Przynajmniej w przypadku Django 1.9.9 - wersji, której obecnie używam - zauważ, że .get_fields()tak naprawdę „traktuje” każdy obcy model jako pole, co może być problematyczne. Powiedz, że masz:

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

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

Wynika, że

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

natomiast, jak pokazuje @Rockallite

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

4

Więc zanim znalazłem ten post, udało mi się go znaleźć.

Model._meta.fields

Działa tak samo jak

Model._meta.get_fields()

Nie jestem pewien, jaka jest różnica w wynikach, jeśli taka istnieje. Uruchomiłem tę pętlę i dostałem to samo wyjście.

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

-2

Dlaczego nie skorzystać z tego:

manage.py inspectdb

Przykładowe dane wyjściowe:

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
To nie jest odpowiedź na pytanie.
Shayne
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.