Lý do mà ModelChoiceField
đặc biệt tạo ra một hit khi tạo sự lựa chọn - bất kể QuerySet đã được dân cư trước đó - nằm trong dòng này
for obj in self.queryset.all():
trong django.forms.models.ModelChoiceIterator
. Khi Django documentation on caching of QuerySets nổi bật,
callable attributes cause DB lookups every time.
Vì vậy, tôi muốn chỉ cần sử dụng
for obj in self.queryset:
mặc dù tôi không chắc chắn 100% về tất cả những tác động của việc này (tôi biết tôi không có lớn kế hoạch với queryset sau đó, vì vậy tôi nghĩ rằng tôi là tốt mà không có bản sao .all()
tạo ra). Tôi đang bị cám dỗ để thay đổi điều này trong mã nguồn, nhưng kể từ khi tôi sẽ quên nó ở bên cạnh cài đặt (và đó là phong cách xấu để bắt đầu với) tôi đã kết thúc bằng văn bản tùy chỉnh của tôi ModelChoiceField
:
class MyModelChoiceIterator(forms.models.ModelChoiceIterator):
"""note that only line with # *** in it is actually changed"""
def __init__(self, field):
forms.models.ModelChoiceIterator.__init__(self, field)
def __iter__(self):
if self.field.empty_label is not None:
yield (u"", self.field.empty_label)
if self.field.cache_choices:
if self.field.choice_cache is None:
self.field.choice_cache = [
self.choice(obj) for obj in self.queryset.all()
]
for choice in self.field.choice_cache:
yield choice
else:
for obj in self.queryset: # ***
yield self.choice(obj)
class MyModelChoiceField(forms.ModelChoiceField):
"""only purpose of this class is to call another ModelChoiceIterator"""
def __init__(*args, **kwargs):
forms.ModelChoiceField.__init__(*args, **kwargs)
def _get_choices(self):
if hasattr(self, '_choices'):
return self._choices
return MyModelChoiceIterator(self)
choices = property(_get_choices, forms.ModelChoiceField._set_choices)
Điều này không giải quyết được vấn đề chung của bộ nhớ đệm cơ sở dữ liệu, nhưng vì bạn đang hỏi về ModelChoiceField
nói riêng và đó chính là điều khiến tôi nghĩ về bộ nhớ đệm đó ngay từ đầu, nghĩ rằng điều này có thể hữu ích.
Đây là một giải pháp tuyệt vời và hoạt động hoàn hảo trong Django 1.8. Hai gợi ý nhỏ có thể làm cho mã hơi sạch hơn: 1) Bạn có thể loại bỏ '__init __()' khỏi cả hai lớp, vì chúng không có op. 2) 'cache_choices' bị xóa trong Django 1.9, vì vậy bạn có thể loại bỏ toàn bộ đoạn mã đó ra. – Chad
Xin chào, tôi không sử dụng Django tại thời điểm này vì vậy tôi không có Django hiện tại thiết lập và do đó cách để xác minh điều này. Bất đắc dĩ để thay đổi mã này thành mã tôi không thể kiểm tra - bạn nghĩ gì về việc tạo câu trả lời bằng mã và tôi chỉnh sửa bài đăng này ở cuối để liên kết với câu trả lời của bạn? – Nicolas78