Pobierz pola modelu w Django

122

Biorąc pod uwagę model Django, próbuję wymienić wszystkie jego pola. Widziałem kilka przykładów robienia tego przy użyciu atrybutu modelu _meta, ale czy podkreślenie przed meta nie wskazuje, że atrybut _meta jest atrybutem prywatnym i nie powinien być dostępny bezpośrednio? ... Bo na przykład układ _meta mógłby się zmienić w przyszłości i nie być stabilnym API?

Czy _meta jest wyjątkiem od tej reguły? Czy jest stabilny i gotowy do użycia, czy też dostęp do niego jest uważany za złą praktykę? A może istnieje funkcja lub inny sposób introspekcji pól modelu bez użycia atrybutu _meta? Poniżej znajduje się lista niektórych linków pokazujących, jak to zrobić za pomocą atrybutu _meta

Wszelkie porady są mile widziane.

obiekt django get / set field

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

Jak przeprowadzić introspekcję pól modelu django?

Joe J
źródło
możliwy duplikat Django: Pobierz listę pól modelu?
Anto

Odpowiedzi:

146

_metajest prywatny, ale jest stosunkowo stabilny. Podejmowane są próby jego sformalizowania, udokumentowania i usunięcia podkreślenia, co może mieć miejsce przed 1.3 lub 1.4. Wyobrażam sobie, że zostanie podjęty wysiłek, aby zapewnić zgodność wsteczną, ponieważ i tak wiele osób go używało.

Jeśli szczególnie martwisz się o kompatybilność, napisz funkcję, która pobiera model i zwraca pola. Oznacza to, że jeśli coś się zmieni w przyszłości, wystarczy zmienić tylko jedną funkcję.

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

Wierzę, że to zwróci listę Fieldobiektów. Aby uzyskać wartość każdego pola z instancji, użyj getattr(instance, field.name).

Aktualizacja: autorzy Django pracują nad interfejsem API, który ma zastąpić obiekt _Meta w ramach Google Summer of Code. Zobacz:
- https://groups.google.com/forum/#!topic/django-developers/hD4roZq0wyk
- https://code.djangoproject.com/wiki/new_meta_api

Will Hardy
źródło
45
Powinieneś również zdawać sobie sprawę z faktu, że jeśli potrzebujesz również pól wiele do wielu, musisz mieć dostęp model._meta.many_to_many!
Bernhard Vallant,
1
Dziękuję Will. Dobrze wiedzieć, że inne osoby również używają _meta. Podoba mi się pomysł posiadania funkcji opakowania. Lazerscience, również dziękuję. Warto wiedzieć, że istnieje fajna metoda na uzyskanie pól many_to_many. Joe
Joe J
3
django/core/management/commands/loaddata.pyużywa _meta do poruszania się po drzewie aplikacji, modeli i pól. Niezły wzór do naśladowania ... i można się założyć, że to „oficjalny sposób”.
płyty kuchenne
2
Jeśli używasz ogólnych kluczy obcych, powinieneś również sprawdzić_meta.virtual_fields
andrei1089
97

Wiem, że ten post jest dość stary, ale chciałem tylko powiedzieć każdemu, kto szuka tego samego, że istnieje publiczny i oficjalny interfejs API, który to robi: get_fields()iget_field()

Stosowanie:

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

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

PirosB3
źródło
6
To jest właściwie prawidłowa odpowiedź dla nowszej wersji django.
chhantyal
3
Jedno pytanie: jeśli są to „publiczne i oficjalne”, dlaczego nadal podlegają _meta?
krubo
9

Teraz jest specjalna metoda - get_fields ()

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

Akceptuje dwa parametry, których można użyć do kontrolowania zwracanych pól:

  • include_parents

    Domyślnie prawda. Rekurencyjnie obejmuje pola zdefiniowane w klasach nadrzędnych. Jeśli ustawiona na False, get_fields () będzie szukać tylko pól zadeklarowanych bezpośrednio w bieżącym modelu. Pola z modeli, które bezpośrednio dziedziczą z modeli abstrakcyjnych lub klas proxy, są uważane za lokalne, a nie nadrzędne.

  • include_hidden

    Domyślnie fałszywe. Jeśli ustawiona na True, get_fields () będzie zawierać pola używane do przywrócenia funkcjonalności innych pól. Obejmuje to również wszystkie pola, które mają powiązaną nazwę (np. ManyToManyField lub ForeignKey), które zaczynają się od „+”

Pecos Pest
źródło
9

get_fields()zwraca a, tuplea każdy element jest Model fieldtypem, którego nie można użyć bezpośrednio jako łańcucha. Więc field.namezwróci nazwę pola

my_model_fields = [field.name for field in MyModel._meta.get_fields()]
Powyższy kod zwróci listę zawierającą nazwy wszystkich pól

Przykład

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
źródło
Świetna odpowiedź! Dzięki!
Tms91
8

To jest coś, co robi sam Django podczas budowania formularza z modelu. Używa atrybutu _meta, ale jak zauważył Bernhard, używa zarówno _meta.fields, jak i _meta.many_to_many. Patrząc na django.forms.models.fields_for_model, możesz to zrobić w następujący sposób:

opts = model._meta
for f in sorted(opts.fields + opts.many_to_many):
    print '%s: %s' % (f.name, f)
ecstaticpeon
źródło
4

Pola modelu zawarte w _meta są wymienione w wielu lokalizacjach jako listy odpowiednich obiektów pól. Może być łatwiej pracować z nimi jako słownikiem, w którym kluczami są nazwy pól.

Moim zdaniem jest to najbardziej zbędny i wyrazisty sposób zbierania i porządkowania obiektów pola modelu:

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

(Zobacz ten przykład użycia w django.forms.models.fields_for_model.)

jxqz
źródło
2
Możesz dołączyć do swojego kodu krótki opis tego, co robi.
Bas van Dijk
2

A co z tym?

fields = Model._meta.fields
JDE876
źródło
1
Wyobrażam sobie, że ktoś mógłby chcieć uzyskać dostęp do właściwości pola. Ponadto, jak wskazano w poprzednich odpowiedziach, istnieją many_to_manyi virtual_fields.
Jocke,
@Jocke racja, jesteś. Może zaistnieć potrzeba uzyskania dostępu do innego atrybutu. Odpowiedź edytowana.
JDE876,
2

Zgodnie z dokumentacją django 2.2 możesz użyć:

Aby uzyskać wszystkie pola: Model._meta.get_fields()

Aby uzyskać indywidualne pole: Model._meta.get_field('field name')

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

Devang Padhiyar
źródło
1

Jeśli potrzebujesz tego dla swojej witryny administracyjnej , istnieje również ModelAdmin.get_fieldsmetoda ( dokumentacja ), która zwraca listnazwę pola strings.

Na przykład:

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
źródło
0

Innym sposobem jest dodanie funkcji do modelu, a gdy chcesz nadpisać datę, możesz wywołać funkcję.

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
źródło