Django: Lấy danh sách các lĩnh vực mô hình?


196

Tôi đã định nghĩa một Userlớp (cuối cùng) kế thừa từ models.Model. Tôi muốn có một danh sách tất cả các trường được xác định cho mô hình này. Ví dụ , phone_number = CharField(max_length=20). Về cơ bản, tôi muốn lấy bất cứ thứ gì kế thừa từ Fieldlớp.

Tôi nghĩ rằng tôi có thể lấy lại những thứ này bằng cách tận dụng inspect.getmembers(model), nhưng danh sách mà nó trả về không chứa bất kỳ trường nào trong số này. Có vẻ như Django đã nắm được lớp và thêm tất cả các thuộc tính ma thuật của nó và loại bỏ những gì thực sự được xác định. Vậy ... làm thế nào tôi có thể có được những lĩnh vực này? Họ có thể có một chức năng để lấy chúng cho mục đích nội bộ của riêng họ?


Điều này cũng có thể giúp ích, quá pypi.python.org/pypi/django-inspect-model/0.5
Paolo

có thể trùng lặp các trường của mô hình trong Django
markpasc

Câu trả lời:


46

Vì hầu hết các câu trả lời đã lỗi thời, tôi sẽ cố gắng cập nhật cho bạn trên Django 2.2 Tại đây các bài đăng - ứng dụng của bạn (bài đăng, blog, cửa hàng, v.v.)

1) Từ liên kết mô hình : 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()

Lưu ý rằng:

all_fields=BlogPost._meta.get_fields()

Cũng sẽ có được một số mối quan hệ, mà cho ex. bạn không thể hiển thị trong một khung nhìn.
Như trong trường hợp của tôi:

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) Từ mô hình cha mẹ

Giả sử rằng chúng tôi có Đăng làm mô hình mẹ và bạn muốn xem tất cả các trường trong danh sách và có các trường của cha mẹ ở chế độ chỉ đọc trong chế độ Chỉnh sửa.

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

1
Tôi sẽ cho rằng điều này là đúng và chấp nhận nó là câu trả lời mới. Cảm ơn các cập nhật!
mở

@mpen Không yêu cầu nó theo cách tốt nhất hoặc pythonic nhất nhưng dưới đây sẽ / sẽ nhận được các giá trị bạn muốn hiển thị trong chế độ xem, vì vậy các tiêu đề của bảng HTML nếu bạn muốn. Khi get_fields () trả về một tuple, bạn có thể lặp lại nó và nhận các giá trị trông giống như appname.Model.field_name, bên dưới dọn sạch các giá trị từ dấu chấm thứ hai, bao gồm cả trường hợp sử dụng dấu gạch dưới trong tên trường như cũng như biến chúng thành một tiêu đề, vì vậy hãy sửa đổi khi cần thiết cho từng tình huống duy nhất. clean_field_names = [str(h).split('.')[2].replace("_", " ").title() for h in all_fields]
Alejandro Suarez

1
Ví dụ từ ví dụ nên là:all_fields = bp._meta.fields
George Kettleborough

@GeorgeKelingborough tôi thấy điểm. Nó cũng không hoạt động từ lớp trực tiếp sao? Đừng có ví dụ để kiểm tra. Dù sao, ví dụ là về ví dụ, vì vậy nó phải từ bp hoặc ít nhất là bp .__ class__
Maks

294

Phiên bản Django 1.8 trở lên:

Bạn nên sử dụng get_fields():

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

Các get_all_field_names()phương pháp được bị phản đối bắt đầu từ Django 1.8 và sẽ được loại bỏ trong 1.10 .

Trang tài liệu được liên kết ở trên cung cấp cách triển khai hoàn toàn tương thích ngược get_all_field_names(), nhưng đối với hầu hết các mục đích, ví dụ trước sẽ hoạt động tốt.


Phiên bản Django trước 1.8:

model._meta.get_all_field_names()

Điều đó sẽ làm các trick.

Điều đó đòi hỏi một ví dụ mô hình thực tế. Nếu tất cả những gì bạn có là một lớp con của django.db.models.Model, thì bạn nên gọimyproject.myapp.models.MyModel._meta.get_all_field_names()


11
Muốn các đối tượng quá, không chỉ tên của họ. Điều này dường như có sẵn trong model._meta.fieldsmặc dù, và tên của họ có thể truy xuất được với field.namenó dường như. Tôi chỉ hy vọng đây là cách ổn định nhất để lấy thông tin này :)
mpen

2
không hoàn toàn chắc chắn Dấu gạch dưới dường như chỉ ra đó là một API nội bộ, sẽ rất tuyệt nếu các anh chàng Django quảng cáo nó lên một cuộc gọi phương thức công khai thực sự django.db.models.Model. Tôi sẽ tìm hiểu sâu về nó và xem những gì tôi có thể tìm thấy
rossipedia

2
Tôi đoán làm điều đó thông qua _metathuộc tính là cách duy nhất ... Ngoài ra hãy xem xét _meta.many_to_manycác trường ManyToMany!
Bernhard Vallant

3
Đó là một giải pháp tốt nhưng phương pháp này bao gồm các trường quan hệ ngược như ForeignKey ngược và những trường không chính xác là "trường". Bất cứ ai cũng biết làm thế nào để phân biệt các lĩnh vực thực tế?
viridis

2
@rossipedia: Chỉ cần lưu ý rằng API ._meta là công khai (mặc dù trước đây nó là riêng tư, có lẽ khi câu trả lời này được viết lần đầu tiên): docs.djangoproject.com/en/1.8/ref/models/meta/ tựa
Nick S

76

Các get_all_related_fields()phương pháp đề cập ở đây đã được tán thành trong 1.8 . Từ giờ trở đi get_fields().

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

1
Điều này bây giờ nên là câu trả lời được chấp nhận. Terse, và đến điểm.
Arnaud P

Vâng, đây là câu trả lời.
eric

55

Tôi thấy việc thêm nó vào các mô hình django khá hữu ích:

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

Điều này cho phép bạn làm:

for field, val in object:
    print field, val

2
Còn ForeignKey thì sao? Tôi có lỗi như thế nàydjango.db.models.fields.related.RelatedObjectDoesNotExist: CustomModel has no custom_attribute.
SAKrisT

ForeignKeylàm việc tốt cho tôi Mặc dù, âm thầm nắm bắt tất cả các ngoại lệ là một mô hình chống. Tốt hơn nhiều để bắt AttributeError, hoặc ít nhất là đăng nhập rằng một số ngoại lệ đã âm thầm nuốt.
Sardathrion - chống lại lạm dụng SE 4/11/2015

1
self._meta.get_all_field_names()đã được khấu hao và loại bỏ. Bạn có thể sử dụng một cái gì đó như for field in self._meta.get_fields()và sau đóyield (field.name, field.value_from_object(self))
MackM

13

Đây là mẹo. Tôi chỉ kiểm tra nó trong Django 1.7.

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

Model._meta.local_fieldskhông chứa nhiều lĩnh vực. Bạn nên lấy chúng bằng cách sử dụng Model._meta.local_many_to_many.


10

Không rõ liệu bạn có một thể hiện của lớp hay chính lớp đó và cố gắng truy xuất các trường, nhưng dù bằng cách nào, hãy xem xét mã sau đây

Sử dụng một ví dụ

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ử dụng một lớp học

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)

Điều này làm việc cho tôi trong django==1.11.8


8

MyModel._meta.get_all_field_names()đã bị phản đối một số phiên bản trở lại và loại bỏ trong Django 1.10.

Đây là gợi ý tương thích ngược từ các tài liệu :

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

Chỉ cần thêm, tôi đang sử dụng đối tượng tự, điều này làm việc cho tôi:

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

5

Ít nhất là với Django 1.9.9 - phiên bản tôi hiện đang sử dụng -, lưu ý rằng .get_fields()thực sự cũng "coi" bất kỳ mô hình nước ngoài nào là một lĩnh vực, có thể có vấn đề. Nói rằng bạn có:

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

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

Nó theo đó

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

trong khi, như được hiển thị bởi @Rockallite

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

4

Vì vậy, trước khi tôi tìm thấy bài đăng này, tôi đã tìm thấy nó thành công.

Model._meta.fields

Nó hoạt động như là

Model._meta.get_fields()

Tôi không chắc sự khác biệt trong kết quả là gì, nếu có. Tôi đã chạy vòng lặp này và có cùng một đầu ra.

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

-2

Tại sao không sử dụng nó:

manage.py inspectdb

Ví dụ đầu ra:

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
Đây không phải là một câu trả lời cho câu hỏi.
Shayne
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.