Dapatkan bidang model di Django

121

Diberikan model Django, saya mencoba mendaftar semua bidangnya. Saya telah melihat beberapa contoh melakukan ini menggunakan atribut model _meta, tetapi bukankah garis bawah di depan meta menunjukkan bahwa atribut _meta adalah atribut pribadi dan tidak boleh diakses secara langsung? ... Karena, misalnya, tata letak _meta dapat berubah di masa mendatang dan bukan API yang stabil?

Apakah _meta merupakan pengecualian untuk aturan ini? Apakah stabil dan siap digunakan atau dianggap praktik yang buruk untuk mengaksesnya? Atau apakah ada fungsi atau cara lain untuk melakukan introspeksi bidang model tanpa menggunakan atribut _meta? Di bawah ini adalah daftar beberapa tautan yang menunjukkan cara melakukan ini menggunakan atribut _meta

Saran apa pun sangat dihargai.

bidang get / set objek django

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

Bagaimana cara introspeksi bidang model django?

Joe J
sumber
kemungkinan duplikat dari Django: Dapatkan daftar bidang model?
Anto

Jawaban:

144

_metabersifat pribadi, tetapi relatif stabil. Ada upaya untuk memformalkannya, mendokumentasikannya, dan menghapus garis bawah, yang mungkin terjadi sebelum 1.3 atau 1.4. Saya membayangkan upaya akan dilakukan untuk memastikan segala sesuatunya kompatibel ke belakang, karena banyak orang telah menggunakannya.

Jika Anda sangat khawatir tentang kompatibilitas, tulis fungsi yang menggunakan model dan mengembalikan bidang. Artinya jika ada sesuatu yang berubah di masa depan, Anda hanya perlu mengubah satu fungsi.

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

Saya yakin ini akan mengembalikan daftar Fieldobjek. Untuk mendapatkan nilai setiap bidang dari instance, gunakan getattr(instance, field.name).

Pembaruan: Kontributor Django sedang mengerjakan API untuk menggantikan objek _Meta sebagai bagian dari Google Summer of Code. Lihat:
- https://groups.google.com/forum/#!topic/django-developers/hD4roZq0wyk
- https://code.djangoproject.com/wiki/new_meta_api

Will Hardy
sumber
45
Anda juga harus menyadari fakta, bahwa jika Anda juga membutuhkan bidang banyak-ke-banyak yang perlu Anda akses model._meta.many_to_many!
Bernhard Vallant
1
Terima kasih Will. Sebaiknya Anda tahu bahwa orang lain juga menggunakan _meta. Saya suka ide memiliki fungsi pembungkus. Lazerscience, terima kasih juga. Senang mengetahui bahwa ada metode yang bagus untuk mendapatkan bidang many_to_many. Joe
Joe J
3
django/core/management/commands/loaddata.pymenggunakan _meta untuk menjalankan pohon aplikasi, model, dan bidang. Pola yang bagus untuk diikuti ... dan Anda bisa bertaruh itu adalah "cara resmi".
kompor
2
Jika Anda menggunakan kunci asing umum, Anda juga harus memeriksa_meta.virtual_fields
andrei1089
7
_metasekarang menjadi API resmi yang didukung (baru di Django 1.8)
mgalgs
97

Saya tahu posting ini cukup lama, tetapi saya hanya ingin memberi tahu siapa pun yang mencari hal yang sama bahwa ada API publik dan resmi untuk melakukan ini: get_fields()danget_field()

Pemakaian:

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

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

PirosB3
sumber
6
Ini sebenarnya jawaban yang tepat untuk versi django yang lebih baru.
chhantyal
3
Satu pertanyaan: jika ini "publik dan resmi", mengapa mereka masih di bawah _meta?
krubo
9

Sekarang ada metode khusus - get_fields ()

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

Ini menerima dua parameter yang dapat digunakan untuk mengontrol bidang mana yang dikembalikan:

  • include_parents

    Benar secara default. Secara rekursif menyertakan bidang yang ditentukan pada kelas induk. Jika disetel ke False, get_fields () hanya akan mencari bidang yang dideklarasikan secara langsung pada model saat ini. Bidang dari model yang secara langsung mewarisi dari model abstrak atau kelas proxy dianggap lokal, bukan induknya.

  • include_hidden

    Salah secara default. Jika disetel ke True, get_fields () akan menyertakan bidang yang digunakan untuk mendukung fungsionalitas bidang lain. Ini juga akan menyertakan kolom apa pun yang memiliki related_name (seperti ManyToManyField, atau ForeignKey) yang dimulai dengan "+"

hama pecos
sumber
9

get_fields()mengembalikan a tupledan setiap elemen adalah Model fieldtipe, yang tidak dapat digunakan secara langsung sebagai string. Jadi, field.nameakan mengembalikan nama field

my_model_fields = [field.name for field in MyModel._meta.get_fields()]
Kode di atas akan mengembalikan daftar yang menghubungkan semua nama bidang

Contoh

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
sumber
Jawaban yang bagus! Terima kasih!
Tms91
8

Ini adalah sesuatu yang dilakukan oleh Django sendiri saat membangun bentuk dari model. Ini menggunakan atribut _meta, tetapi seperti yang dicatat Bernhard, ini menggunakan _meta.fields dan _meta.many_to_many. Melihat django.forms.models.fields_for_model, berikut adalah bagaimana Anda dapat melakukannya:

opts = model._meta
for f in sorted(opts.fields + opts.many_to_many):
    print '%s: %s' % (f.name, f)
perasaan gembira
sumber
4

Bidang model yang dimuat oleh _meta dicantumkan di beberapa lokasi sebagai daftar objek bidang masing-masing. Mungkin lebih mudah untuk bekerja dengan mereka sebagai kamus di mana kuncinya adalah nama bidang.

Menurut pendapat saya, ini adalah cara paling irredundan dan ekspresif untuk mengumpulkan dan mengatur objek bidang model:

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

(Lihat contoh penggunaan di django.forms.models.fields_for_model.)

jxqz.dll
sumber
2
Anda mungkin ingin menyertai kode Anda dengan penjelasan singkat tentang fungsinya.
Bas van Dijk
2

Bagaimana dengan yang ini.

fields = Model._meta.fields
JDE876
sumber
1
Saya membayangkan seseorang mungkin ingin mengakses properti bidang. Juga, seperti yang ditunjukkan dalam jawaban sebelumnya, ada many_to_manydan virtual_fields.
Jocke
@Jocke benar kamu. Mungkin timbul kebutuhan untuk mengakses atribut lain. Jawaban diedit.
JDE876
2

Sesuai dengan dokumentasi django 2.2 Anda dapat menggunakan:

Untuk mendapatkan semua bidang: Model._meta.get_fields()

Untuk mendapatkan bidang individu: Model._meta.get_field('field name')

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

Devang Padhiyar
sumber
1

Jika Anda memerlukan ini untuk situs admin Anda , ada juga ModelAdmin.get_fieldsmetode ( dokumen ), yang mengembalikan listnama bidang strings.

Sebagai contoh:

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
sumber
0

Cara lain adalah menambahkan fungsi ke model dan bila Anda ingin mengganti tanggal, Anda dapat memanggil fungsi tersebut.

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
sumber