Django: Dapatkan daftar bidang model?

196

Saya telah mendefinisikan Userkelas yang (akhirnya) mewarisi dari models.Model. Saya ingin mendapatkan daftar semua bidang yang ditentukan untuk model ini. Sebagai contoh phone_number = CharField(max_length=20),. Pada dasarnya, saya ingin mengambil apa pun yang diwarisi dari Fieldkelas.

Saya pikir saya dapat mengambil ini dengan mengambil keuntungan dari inspect.getmembers(model), tetapi daftar yang dikembalikan tidak mengandung bidang-bidang ini. Sepertinya Django sudah menguasai kelas dan menambahkan semua atribut sihirnya dan menghapus apa yang sebenarnya telah didefinisikan. Jadi ... bagaimana saya bisa mendapatkan bidang ini? Mereka mungkin memiliki fungsi untuk mengambil mereka untuk keperluan internal mereka sendiri?

Mpen
sumber
Ini mungkin membantu, juga pypi.python.org/pypi/django-inspect-model/0.5
Paolo
kemungkinan duplikat bidang Dapatkan model di Django
markpasc

Jawaban:

46

Karena sebagian besar jawaban sudah usang, saya akan mencoba memperbarui Anda di Django 2.2 Di sini posting - aplikasi Anda (posting, blog, toko, dll)

1) Dari tautan model : 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()

Perhatikan bahwa:

all_fields=BlogPost._meta.get_fields()

Juga akan mendapatkan beberapa hubungan, yang untuk mantan. Anda tidak dapat ditampilkan dalam tampilan.
Seperti dalam kasus saya:

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

dan

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

2) Dari contoh

from posts.model import BlogPost

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

3) Dari model induk

Anggaplah kita memiliki Post sebagai model induk dan Anda ingin melihat semua bidang dalam daftar, dan memiliki bidang induk untuk hanya-baca dalam mode Edit.

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
Maks
sumber
1
Saya akan menganggap ini benar dan menerimanya sebagai jawaban baru. Terima kasih atas pembaruannya!
mpen
@mpen Tidak akan mengklaimnya sebagai cara terbaik atau paling pythonic tetapi di bawah ini akan / harus mendapatkan nilai-nilai yang ingin Anda tampilkan dalam tampilan, jadi header dari tabel HTML jika Anda mau. Saat get_fields () mengembalikan tuple, Anda dapat mengulanginya dan mendapatkan nilai yang terlihat seperti appname.Model.field_name, di bawah ini membersihkan nilai dari titik kedua, termasuk kasus di mana garis bawah digunakan dalam nama bidang sebagai serta menjadikannya judul, jadi modifikasi sesuai kebutuhan untuk setiap situasi unik. clean_field_names = [str(h).split('.')[2].replace("_", " ").title() for h in all_fields]
Alejandro Suarez
1
Contoh dari contoh harus:all_fields = bp._meta.fields
George Kettleborough
@ GeorgeKettleborough saya mengerti intinya. Tidakkah ini bekerja dari kelas secara langsung juga? Tidak punya contoh untuk diuji. Pokoknya contohnya adalah tentang contoh, jadi harus dari bp atau setidaknya bp .__ class__
Maks
294

Versi Django 1.8 dan yang lebih baru:

Anda harus menggunakan get_fields():

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

The get_all_field_names()Metode ini usang mulai dari Django 1.8 dan akan dihapus dalam 1.10 .

Halaman dokumentasi yang ditautkan di atas memberikan implementasi yang sepenuhnya kompatibel dengan mundur get_all_field_names(), tetapi untuk sebagian besar tujuan contoh sebelumnya harus bekerja dengan baik.


Versi Django sebelum 1.8:

model._meta.get_all_field_names()

Itu harus melakukan trik.

Itu membutuhkan contoh model yang sebenarnya. Jika semua yang Anda miliki adalah subkelas dari django.db.models.Model, maka Anda harus meneleponmyproject.myapp.models.MyModel._meta.get_all_field_names()

rossipedia
sumber
11
Menginginkan benda juga, bukan hanya nama mereka. Ini tampaknya tersedia model._meta.fieldsmeskipun, dan nama mereka dapat dipulihkan dengan field.nametampaknya. Saya hanya berharap ini adalah cara paling stabil untuk mengambil info ini :)
mpen
2
tidak sepenuhnya yakin. Garis bawah tampaknya mengindikasikan itu adalah API internal, akan lebih baik jika orang-orang Django mempromosikan ini ke panggilan metode yang sebenarnya publik django.db.models.Model. Saya akan menggali dan melihat apa yang bisa saya temukan
rossipedia
2
Saya kira melakukannya melalui _metaatribut adalah satu-satunya cara ... Selain mencari _meta.many_to_manybidang ManyToMany!
Bernhard Vallant
3
Ini solusi yang bagus tetapi metode ini mencakup bidang hubungan terbalik seperti ForeignKey terbalik dan itu bukan "bidang". Adakah yang tahu bagaimana membedakan Fields yang sebenarnya?
viridis
2
@rossipedia: Hanya mencatat bahwa ._meta API bersifat publik (meskipun dulunya bersifat pribadi, mungkin ketika jawaban ini pertama kali ditulis): docs.djangoproject.com/en/1.8/ref/models/meta/…
Nick S
76

The get_all_related_fields()Metode yang disebutkan di sini telah usang dalam 1,8 . Mulai sekarang get_fields().

>> from django.contrib.auth.models import User
>> User._meta.get_fields()
Wil
sumber
1
Sekarang ini seharusnya jawaban yang diterima. Terse, dan to the point.
Arnaud P
Ya ini jawabannya.
eric
55

Saya menemukan menambahkan ini ke model Django cukup membantu:

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

Ini memungkinkan Anda melakukan:

for field, val in object:
    print field, val
bjw
sumber
2
Bagaimana dengan ForeignKey? Saya memiliki kesalahan seperti inidjango.db.models.fields.related.RelatedObjectDoesNotExist: CustomModel has no custom_attribute.
SAKrisT
ForeignKeybekerja dengan baik untukku. Meskipun demikian, secara diam-diam menangkap semua pengecualian adalah anti-pola. Jauh lebih baik untuk ditangkap AttributeError, atau setidaknya mencatat bahwa beberapa pengecualian ditelan diam-diam.
Sardathrion - terhadap penyalahgunaan SE
1
self._meta.get_all_field_names()telah disusutkan dan dihapus. Anda dapat menggunakan sesuatu seperti for field in self._meta.get_fields()dan kemudianyield (field.name, field.value_from_object(self))
MackM
13

Ini caranya. Saya hanya mengujinya di Django 1.7.

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

Model._meta.local_fieldstidak mengandung bidang banyak ke banyak. Anda harus menggunakannya Model._meta.local_many_to_many.

Rockallite
sumber
10

Tidak jelas apakah Anda memiliki turunan dari kelas atau kelas itu sendiri dan mencoba untuk mengambil bidang, tetapi bagaimanapun, pertimbangkan kode berikut

Menggunakan contoh

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

Menggunakan kelas

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'
Nader Alexan
sumber
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)

Ini berhasil untuk saya django==1.11.8

Vivek Anand
sumber
8

MyModel._meta.get_all_field_names()itu ditinggalkan beberapa versi kembali dan dihapus di Django 1.10.

Berikut saran yang kompatibel dari belakang :

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)
)))
tentang Harun
sumber
6

Hanya untuk menambahkan, saya menggunakan objek mandiri, ini bekerja untuk saya:

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

Setidaknya dengan Django 1.9.9 - versi yang saya gunakan saat ini -, perhatikan bahwa .get_fields()sebenarnya juga "menganggap" model asing sebagai bidang, yang mungkin bermasalah. Katakanlah Anda memiliki:

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

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

Karena itu

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

sementara, seperti yang ditunjukkan oleh @Rockallite

>>> map(lambda field:field.name, Parent._model._meta.local_fields)
['id']
berusaha agar hidup
sumber
4

Jadi sebelum saya menemukan posting ini, saya berhasil menemukan ini berfungsi.

Model._meta.fields

Ini berfungsi sama seperti

Model._meta.get_fields()

Saya tidak yakin apa bedanya dalam hasil, jika ada. Saya menjalankan loop ini dan mendapatkan output yang sama.

for field in Model._meta.fields:
    print(field.name)
Carl Brubaker
sumber
-2

Mengapa tidak menggunakannya saja:

manage.py inspectdb

Contoh output:

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'),)
Adam Pawluczuk
sumber
1
Ini bukan jawaban untuk pertanyaan itu.
Shayne