Django: รับรายชื่อเขตข้อมูลโมเดล?


198

ฉันได้กำหนดUserระดับที่ (ในที่สุด) models.Modelที่สืบทอดจาก ฉันต้องการรับรายการเขตข้อมูลทั้งหมดที่กำหนดไว้สำหรับรุ่นนี้ ตัวอย่างเช่นphone_number = CharField(max_length=20). โดยทั่วไปฉันต้องการดึงสิ่งที่สืบทอดมาจากFieldชั้นเรียน

ฉันคิดว่าฉันสามารถดึงข้อมูลเหล่านี้ได้โดยการใช้ประโยชน์จากinspect.getmembers(model)แต่รายการที่ส่งคืนจะไม่มีฟิลด์ใด ๆ เหล่านี้ ดูเหมือนว่า Django จะได้ครอบครองชั้นเรียนแล้วและได้เพิ่มคุณสมบัติเวทมนตร์ทั้งหมดและตัดสิ่งที่กำหนดจริง ดังนั้น ... ฉันจะหาสาขาเหล่านี้ได้อย่างไร พวกเขาอาจมีฟังก์ชั่นสำหรับเรียกพวกเขาเพื่อวัตถุประสงค์ภายในของตัวเอง?


สิ่งนี้อาจช่วยได้เช่นกันpypi.python.org/pypi/django-inspect-model/0.5
เปาโล

เป็นไปได้ที่ซ้ำกันของรับฟิลด์ของโมเดลใน Django
markpasc

คำตอบ:


48

เนื่องจากคำตอบส่วนใหญ่ล้าสมัยฉันจะพยายามอัปเดตคุณในDjango 2.2 ที่ นี่โพสต์ - แอปของคุณ (โพสต์บล็อกร้านค้า ฯลฯ )

1) จากลิงก์โมเดล : 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()

โปรดทราบว่า:

all_fields=BlogPost._meta.get_fields()

จะได้รับความสัมพันธ์บางอย่างเช่น: คุณไม่สามารถแสดงในมุมมอง
เช่นเดียวกับในกรณีของฉัน:

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

และ

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

2) จากตัวอย่าง

from posts.model import BlogPost

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

3) จากรุ่นแม่

สมมติว่าเรามีโพสต์เป็นโมเดลหลักและคุณต้องการเห็นฟิลด์ทั้งหมดในรายการและให้ฟิลด์หลักเป็นแบบอ่านอย่างเดียวในโหมดแก้ไข

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
ฉันจะถือว่านี่ถูกต้องและยอมรับว่าเป็นคำตอบใหม่ ขอบคุณสำหรับการอัพเดท!
mpen

@mpen จะไม่อ้างว่าเป็นวิธีที่ดีที่สุดหรือ pythonic ที่สุด แต่ด้านล่างจะ / ควรได้รับค่าที่คุณต้องการแสดงในมุมมองดังนั้นส่วนหัวของตาราง HTML ถ้าคุณจะ เนื่องจาก get_fields () ส่งคืน tuple คุณสามารถวนซ้ำและรับค่าที่ดูเหมือน appname.Model.field_name ด้านล่างล้างค่าจากจุดที่สองรวมถึงกรณีที่มีการใช้ขีดเส้นใต้ในชื่อเขตข้อมูลเป็น รวมทั้งตั้งชื่อให้ปรับเปลี่ยนตามความจำเป็นสำหรับแต่ละสถานการณ์ที่ไม่ซ้ำกัน clean_field_names = [str(h).split('.')[2].replace("_", " ").title() for h in all_fields]
Alejandro Suarez

1
ตัวอย่างจากตัวอย่างควรเป็น:all_fields = bp._meta.fields
George Kettleborough

@ GeorgeKettleborough ฉันเห็นจุด มันไม่ทำงานจากชั้นเรียนโดยตรงหรือไม่ ไม่มีตัวอย่างที่จะทดสอบ อย่างไรก็ตามตัวอย่างเป็นเรื่องเกี่ยวกับอินสแตนซ์ดังนั้นควรมาจาก bp หรืออย่างน้อย bp .__ class__
Maks

294

Django เวอร์ชั่น 1.8 และใหม่กว่า:

คุณควรใช้get_fields():

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

get_all_field_names()วิธีการที่เลิกใช้งานแล้วเริ่มต้นจาก Django 1.8 และจะถูกลบออกใน 1.10

หน้าเอกสารที่เชื่อมโยงด้านบนมีการใช้งานที่เข้ากันได้อย่างสมบูรณ์แบบย้อนหลังget_all_field_names()แต่สำหรับวัตถุประสงค์ส่วนใหญ่ตัวอย่างก่อนหน้านี้ควรใช้งานได้ดี


รุ่น Django ก่อน 1.8:

model._meta.get_all_field_names()

นั่นควรทำเคล็ดลับ

ที่ต้องการอินสแตนซ์ของโมเดลจริง หากสิ่งที่คุณมีคือคลาสย่อยของdjango.db.models.Modelคุณควรจะเรียกmyproject.myapp.models.MyModel._meta.get_all_field_names()


11
ต้องการวัตถุด้วยไม่ใช่แค่ชื่อ นี้ดูเหมือนว่าจะสามารถใช้ได้ในmodel._meta.fieldsแต่และชื่อของพวกเขากลับคืนด้วยfield.nameดูเหมือนว่า ผมแค่หวังว่านี้เป็นวิธีที่มีเสถียรภาพมากที่สุดเพื่อดึงข้อมูลนี้ :)
mpen

2
ไม่แน่ใจทั้งหมด เครื่องหมายขีดล่างดูเหมือนจะบ่งบอกว่าเป็น API ภายในจะเจ๋งถ้าคน Django เลื่อนขั้นนี้ไปสู่การเรียกใช้แบบสาธารณะdjango.db.models.Modelอย่างแท้จริง ฉันจะขุดลงไปและดูสิ่งที่ฉันสามารถหาได้
rossipedia

2
ฉันเดาว่าทำผ่าน_metaแอ็ตทริบิวต์เป็นวิธีเดียว ... นอกจากนี้ให้ดูที่_meta.many_to_manyฟิลด์ ManyToMany ด้วย!
Bernhard Vallant

3
เป็นวิธีแก้ปัญหาที่ดี แต่วิธีนี้รวมถึงเขตข้อมูลความสัมพันธ์ย้อนกลับเช่น ForeignKey แบบย้อนกลับและเขตข้อมูลเหล่านั้นไม่ใช่ "เขตข้อมูล" อย่างแน่นอน ใครทราบวิธีแยกแยะเขตข้อมูลจริง
viridis

2
@rossipedia: เพียงแค่สังเกตว่า API ._meta เป็นแบบสาธารณะ (แม้ว่ามันจะเคยเป็นส่วนตัวอาจจะเมื่อคำตอบนี้ถูกเขียนขึ้นก่อน): docs.djangoproject.com/en/1.8/ref/models/meta/...
นิค S

76

get_all_related_fields()วิธีการที่กล่าวถึงนี้ได้รับการคัดค้านใน 1.8 จากนี้get_fields()ไป

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

1
ตอนนี้ควรเป็นคำตอบที่ยอมรับได้ Terse และตรงประเด็น
Arnaud P

ใช่นี่คือคำตอบ
eric

55

ฉันคิดว่าการเพิ่มสิ่งนี้ลงในโมเดล django มีประโยชน์มาก:

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

วิธีนี้ช่วยให้คุณ:

for field, val in object:
    print field, val

2
ForeignKey เกี่ยวกับอะไร ฉันมีข้อผิดพลาดเช่นนี้django.db.models.fields.related.RelatedObjectDoesNotExist: CustomModel has no custom_attribute.
SAKrisT

ForeignKeyทำงานได้ดีสำหรับฉัน แม้ว่าการติดตามข้อยกเว้นทั้งหมดอย่างเงียบ ๆ เป็นการต่อต้านรูปแบบ จับได้ดีกว่ามากAttributeErrorหรืออย่างน้อยก็บันทึกว่ามีข้อยกเว้นบางอย่างถูกกลืนหายไปในความเงียบ
Sardathrion - ต่อต้านการล่วงละเมิดทาง SE

1
self._meta.get_all_field_names()ถูกคิดค่าเสื่อมราคาและลบแล้ว คุณสามารถใช้สิ่งที่ชอบfor field in self._meta.get_fields()แล้วyield (field.name, field.value_from_object(self))
MackM

13

นี่เป็นการหลอกลวง ฉันทดสอบเฉพาะใน Django 1.7

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

Model._meta.local_fieldsไม่มีฟิลด์หลายต่อหลายฟิลด์ Model._meta.local_many_to_manyคุณควรจะได้รับพวกเขาโดยใช้


10

ไม่ชัดเจนว่าคุณมีอินสแตนซ์ของคลาสหรือคลาสเองและพยายามดึงฟิลด์ แต่ไม่ว่าด้วยวิธีใดให้พิจารณารหัสต่อไปนี้

ใช้อินสแตนซ์

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

ใช้คลาส

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'


8

MyModel._meta.get_all_field_names()ถูกเลิกใช้หลายรุ่นกลับมาและเอาออกใน Django 1.10

นี่คือคำแนะนำที่เข้ากันได้ย้อนหลังจากเอกสาร :

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

เพียงเพิ่มฉันใช้วัตถุของตัวเองสิ่งนี้ได้ผลกับฉัน:

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

5

อย่างน้อยกับDjango 1.9.9 - รุ่นที่ฉันใช้อยู่ - โปรดทราบว่า.get_fields()จริงๆแล้ว "พิจารณา" โมเดลต่างประเทศใด ๆ เป็นฟิลด์ซึ่งอาจเป็นปัญหาได้ บอกว่าคุณมี:

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

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

มันติดตามว่า

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

ในขณะที่แสดงโดย @Rockallite

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

4

ดังนั้นก่อนที่ฉันจะพบโพสต์นี้ฉันพบว่ามันใช้งานได้

Model._meta.fields

มันทำงานได้อย่างเท่าเทียมกันเป็น

Model._meta.get_fields()

ฉันไม่แน่ใจว่าความแตกต่างคืออะไรในผลลัพธ์ถ้ามี ฉันวิ่งวนรอบนี้และได้ผลลัพธ์เดียวกัน

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

-2

ทำไมไม่ใช้เพียง:

manage.py inspectdb

ตัวอย่างผลลัพธ์:

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
นี่ไม่ใช่คำตอบสำหรับคำถาม
เชน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.