Nhận các trường của mô hình trong Django


121

Với một mô hình Django, tôi đang cố gắng liệt kê tất cả các trường của nó. Tôi đã xem một số ví dụ về việc này bằng cách sử dụng thuộc tính mô hình _meta, nhưng không phải gạch dưới phía trước thẻ meta chỉ ra rằng thuộc tính _meta là thuộc tính riêng tư và không nên được truy cập trực tiếp? ... Ví dụ: bởi vì bố cục của _meta có thể thay đổi trong tương lai và không phải là một API ổn định?

_Meta có phải là một ngoại lệ cho quy tắc này không? Nó có ổn định và sẵn sàng để sử dụng không hay việc truy cập nó bị coi là hành vi xấu? Hoặc có một hàm hoặc một số cách khác để xem xét các trường của một mô hình mà không cần sử dụng thuộc tính _meta không? Dưới đây là danh sách một số liên kết chỉ ra cách thực hiện việc này bằng thuộc tính _meta

Bất cứ lời khuyên được nhiều đánh giá cao.

trường lấy / đặt đối tượng django

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

Làm thế nào để xem xét các trường mô hình django?


Câu trả lời:


144

_metalà riêng tư, nhưng nó tương đối ổn định. Có những nỗ lực để chính thức hóa nó, ghi lại nó và xóa dấu gạch dưới, có thể xảy ra trước 1.3 hoặc 1.4. Tôi tưởng tượng sẽ phải nỗ lực để đảm bảo mọi thứ tương thích ngược, vì dù sao thì rất nhiều người đã sử dụng nó.

Nếu bạn đặc biệt lo lắng về khả năng tương thích, hãy viết một hàm nhận mô hình và trả về các trường. Điều này có nghĩa là nếu có điều gì đó thay đổi trong tương lai, bạn chỉ phải thay đổi một chức năng.

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

Tôi tin rằng điều này sẽ trả về một danh sách các Fieldđối tượng. Để lấy giá trị của từng trường từ phiên bản, hãy sử dụng getattr(instance, field.name).

Cập nhật: Các cộng tác viên của Django đang làm việc trên một API để thay thế đối tượng _Meta như một phần của Google Summer of Code. Xem:
- https://groups.google.com/forum/#!topic/django-developers/hD4roZq0wyk
- https://code.djangoproject.com/wiki/new_meta_api


45
Bạn cũng nên biết về thực tế của anh ấy, rằng nếu bạn cũng cần nhiều trường bạn cần truy cập model._meta.many_to_many!
Bernhard Vallant

1
Cảm ơn bạn Will. Thật vui khi biết rằng những người khác cũng đang sử dụng _meta. Tôi thích ý tưởng có một chức năng trình bao bọc. Lazerscience, cảm ơn bạn cũng có. Thật tốt khi biết rằng có một phương pháp hay để lấy trường many_to_many. Joe
Joe J

3
django/core/management/commands/loaddata.pysử dụng _meta để đi qua cây ứng dụng, mô hình và trường. Mô hình tuyệt vời để làm theo ... và bạn có thể đặt cược đó là "cách chính thức".
hobs

2
Nếu bạn đang sử dụng các phím nước ngoài chung bạn cũng nên kiểm tra_meta.virtual_fields
andrei1089


97

Tôi biết bài đăng này khá cũ, nhưng tôi chỉ muốn nói với bất kỳ ai đang tìm kiếm thứ tương tự rằng có một API công khai và chính thức để thực hiện việc này: get_fields()get_field()

Sử dụng:

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

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


6
Đây thực sự là câu trả lời đúng cho phiên bản mới hơn của django.
chhantyal


3
Một câu hỏi đặt ra: nếu đây là "công khai và chính thức", tại sao chúng vẫn còn dưới quyền _meta?
krubo

9

Bây giờ có một phương thức đặc biệt - get_fields ()

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

Nó chấp nhận hai tham số có thể được sử dụng để kiểm soát trường nào được trả về:

  • bao gồm

    Đúng theo mặc định. Đệ quy bao gồm các trường được xác định trên các lớp cha. Nếu được đặt thành False, get_fields () sẽ chỉ tìm kiếm các trường được khai báo trực tiếp trên mô hình hiện tại. Các trường từ các mô hình kế thừa trực tiếp từ các mô hình trừu tượng hoặc các lớp proxy được coi là cục bộ, không thuộc trường cha.

  • include_hiised

    Sai theo mặc định. Nếu được đặt thành True, get_fields () sẽ bao gồm các trường được sử dụng để hỗ trợ chức năng của trường khác. Điều này cũng sẽ bao gồm bất kỳ trường nào có tên_các_cấp (chẳng hạn như ManyToManyField hoặc ForeignKey) bắt đầu bằng dấu “+”


9

get_fields()trả về a tuplevà mỗi phần tử là một Model fieldkiểu, không thể được sử dụng trực tiếp như một chuỗi. Vì vậy, field.namesẽ trả về tên trường

my_model_fields = [field.name for field in MyModel._meta.get_fields()]
Đoạn mã trên sẽ trả về một danh sách chứa tất cả tên trường

Ví dụ

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']

Câu trả lời chính xác! Cảm ơn!
Tms91

8

Đây là điều do chính Django thực hiện khi xây dựng một biểu mẫu từ một mô hình. Nó đang sử dụng thuộc tính _meta, nhưng như Bernhard đã lưu ý, nó sử dụng cả _meta.fields và _meta.many_to_many. Nhìn vào django.forms.models.fields_for_model, đây là cách bạn có thể làm điều đó:

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

4

Các trường mô hình do _meta chứa được liệt kê ở nhiều vị trí dưới dạng danh sách các đối tượng trường tương ứng. Có thể dễ dàng hơn khi làm việc với chúng như một từ điển trong đó các khóa là tên trường.

Theo tôi, đây là cách không hợp lý và dễ hiểu nhất để thu thập và sắp xếp các đối tượng trường mô hình:

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

(Xem Cách sử dụng ví dụ này trong django.forms.models.fields_for_model.)


2
Bạn có thể muốn kèm theo mã của mình với một mô tả ngắn gọn về những gì nó đang làm.
Bas van Dijk

2

Còn cái này thì sao.

fields = Model._meta.fields

1
Tôi tưởng tượng một người có thể muốn truy cập các thuộc tính của một trường. Ngoài ra, như đã chỉ ra trong các câu trả lời trước, có many_to_manyvirtual_fields.
Jocke

@Jocke đúng bạn đang có. Có thể nảy sinh nhu cầu truy cập thuộc tính khác. Đã chỉnh sửa câu trả lời.
JDE876

2

Theo tài liệu django 2.2, bạn có thể sử dụng:

Để nhận tất cả các trường: Model._meta.get_fields()

Để có một trường riêng lẻ: Model._meta.get_field('field name')

Ví dụ. Session._meta.get_field('expire_date')


1

Nếu bạn cần điều này cho trang quản trị của mình , thì cũng có ModelAdmin.get_fieldsphương thức ( docs ), trả về một listtên trường strings.

Ví dụ:

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
        ...

0

Một cách khác là thêm các hàm vào mô hình và khi bạn muốn ghi đè ngày tháng, bạn có thể gọi hàm.

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()
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.