Django: Model alanlarının listesini al?


198

User(Nihayetinde) miras kalan bir sınıf tanımladım models.Model. Bu model için tanımlanan tüm alanların bir listesini almak istiyorum. Örneğin phone_number = CharField(max_length=20),. Temel olarak, Fieldsınıftan miras alan her şeyi almak istiyorum .

Bunlardan yararlanarak bunları alabileceğimi düşündüm inspect.getmembers(model), ancak döndürdüğü liste bu alanlardan hiçbirini içermiyor. Görünüşe göre Django zaten sınıfı tuttu ve tüm sihirli özelliklerini ekledi ve gerçekte tanımlanmış olanı çıkardı. Peki ... bu alanları nasıl alabilirim? Muhtemelen onları kendi iç amaçları için geri getirme işlevleri var mı?



Yanıtlar:


48

Cevapların çoğu eski olduğu için sizi Django 2.2'de güncellemeye çalışacağım. İşte mesajlar- uygulamanız (mesajlar, blog, mağaza vb.)

1) Model bağlantısından: 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()

Bunu not et:

all_fields=BlogPost._meta.get_fields()

Ayrıca, örneğin: bir görünümde görüntüleyemediğiniz bazı ilişkiler elde edersiniz.
Benim durumumda olduğu gibi:

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

ve

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

2) Örnekten

from posts.model import BlogPost

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

3) Ana modelden

Üst model olarak Post'a sahip olduğumuzu ve bir listedeki tüm alanları görmek istediğinizi ve üst alanların Düzenleme modunda salt okunur olmasını istediğinizi varsayalım.

from django.contrib import admin
from posts.model import BlogPost 

@admin.register(BlogPost)
class BlogPost(admin.ModelAdmin):
    all_fields = [f.name for f in Organisation._meta.fields]
    parent_fields = BlogPost.get_deferred_fields(BlogPost)

    list_display = all_fields
    read_only = parent_fields

1
Bunun doğru olduğunu varsayacağım ve yeni cevap olarak kabul edeceğim. Güncelleme için teşekkürler!
19:08

@mpen En iyi yol veya en pitonik iddia olmaz ama aşağıda u bir görünümde görüntülemek istediğiniz değerleri alacak / alacak, böylece u bir HTML tablosunun başlıkları olacaktır. Get_fields () bir tuple döndürdüğü için, u üzerinde yineleme yapabilir ve appname.Model.field_name gibi görünen değerleri elde edebilir, aşağıdaki değerleri ikinci noktadan temizler, alan adında bir alt çizginin kullanıldığı durumu içerir. onları bir başlık haline getirin. clean_field_names = [str(h).split('.')[2].replace("_", " ").title() for h in all_fields]
Alejandro Suarez

1
Örneğin örneği şöyle olmalıdır:all_fields = bp._meta.fields
George Kettleborough

@GeorgeKettleborough noktayı görüyorum. Doğrudan sınıftan da çalışmıyor mu? Test edilecek örneğe sahip değilsiniz. O .__ class__ bp arasında ya da en azından bp olmalıdır böylece Neyse örneği, örneğin hakkındadır
Maks

294

Django 1.8 ve sonraki sürümleri:

Kullanmalısınız get_fields():

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

get_all_field_names()Yöntemdir Django'ya 1.8 başlayarak kullanımdan kaldırıldı ve 1.10 kaldırılacaktır .

Yukarıda bağlantılı olan dokümantasyon sayfası, geriye dönük olarak tam uyumlu bir uygulama sağlar get_all_field_names(), ancak çoğu durumda önceki örneğin iyi çalışması gerekir.


1.8 öncesi Django sürümleri:

model._meta.get_all_field_names()

Hile yapmalı.

Bu gerçek bir model örneği gerektirir. Sahip olduğunuz tüm bir alt sınıf ise django.db.models.Model, o zaman aramalısınızmyproject.myapp.models.MyModel._meta.get_all_field_names()


11
Sadece isimlerini değil nesneleri de istedim. Bu model._meta.fieldsolsa da kullanılabilir gibi görünüyor ve isimleri ile alınabilir field.namegibi görünüyor. Ben sadece bu bu bilgi almak için en kararlı yolu olduğunu umuyoruz :)
mpen

2
tamamen emin değilim. Alt çizgi, bunun dahili bir API olduğunu gösteriyor gibi görünüyor, eğer Django adamları bunu gerçekten halka açık bir yöntem çağrısına yükseltirse harika olurdu django.db.models.Model. Ben içine kazıp ne bulabileceğimi göreceğim
rossipedia

2
Bu _metaözellik üzerinden yapmak sanırım tek yoldur ... Ayrıca _meta.many_to_manyManyToMany alanları için bak !
Bernhard Vallant

3
Bu iyi bir çözümdür ancak bu yöntem, ters bir ForeignKey gibi ters ilişki alanlarını içerir ve bunlar tam olarak "alan" değildir. Gerçek Alanları nasıl ayırt edebileceğini bilen var mı?
viridis

2
@rossipedia: ._meta API'sinin herkese açık olduğunu belirtmek (özel olarak olsa da, muhtemelen bu cevap ilk yazıldığında): docs.djangoproject.com/en/1.8/ref/models/meta/…
Nick S


55

Bunu django modellerine eklemeyi oldukça yararlı buluyorum:

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

Bu şunları yapmanızı sağlar:

for field, val in object:
    print field, val

2
ForeignKey ne olacak? Bunun gibi hatalar vardjango.db.models.fields.related.RelatedObjectDoesNotExist: CustomModel has no custom_attribute.
SAKrisT

ForeignKeybenim için iyi çalış. Bununla birlikte, tüm istisnaları sessizce yakalamak bir anti-modeldir. Yakalamak AttributeErrorveya en azından bazı istisnaların sessizce yutulduğunu kaydetmek çok daha iyidir .
Sardathrion - SE kötüye karşı

1
self._meta.get_all_field_names()amortismana tabi tutuldu ve kaldırıldı. Sen gibi bir şey kullanabilirsiniz for field in self._meta.get_fields()sonra veyield (field.name, field.value_from_object(self))
MackM

13

Bu hile yapar. Sadece Django 1.7'de test ediyorum.

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

Model._meta.local_fieldsçoktan çoka alan içermiyor. Onları kullanmalısın Model._meta.local_many_to_many.


10

Sınıfın veya sınıfın kendisinin bir örneğine sahip olup olmadığınız ve alanları almaya çalıştığınız açık değildir, ancak her iki durumda da aşağıdaki kodu göz önünde bulundurun

Bir örnek kullanma

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

Sınıf kullanma

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)

Bu benim için çalıştı django==1.11.8


8

MyModel._meta.get_all_field_names()oldu kaldırılmış birkaç sürümleri geri ve kaldırıldı Django 1.10 yılında.

İşte dokümanlardan geriye dönük uyumlu öneri :

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

Sadece eklemek için, ben kendi nesnesini kullanıyorum, bu benim için çalıştı:

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

5

En azından şu anda kullandığım sürüm Django 1.9.9 ile , .get_fields()aslında herhangi bir yabancı modeli bir alan olarak "dikkate" aldığını unutmayın ki bu sorunlu olabilir. Diyelim ki:

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

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

Bunu takip eder

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

@Rockallite tarafından gösterildiği gibi

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

4

Bu gönderiyi bulmadan önce, bunu başarılı bir şekilde buldum.

Model._meta.fields

Aynı şekilde çalışır

Model._meta.get_fields()

Varsa, sonuçlardaki farkın ne olduğundan emin değilim. Bu döngüyü çalıştırdım ve aynı çıktıyı aldım.

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

-2

Neden sadece bunu kullanmıyorsunuz?

manage.py inspectdb

Örnek çıktı:

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
Bu sorunun cevabı değil.
Shayne
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.