Tôi đang cố gắng tối ưu hóa truy vấn cơ sở dữ liệu cho một ứng dụng Django. Dưới đây là một ví dụ đơn giản:trường nhiều người django: chỉ tìm nạp trước các khóa chính
class Label(models.Model):
name = models.CharField(max_length=200)
# ... many other fields ...
class Thing(models.Model):
name = models.CharField(max_length=200)
labels = models.ManyToManyField(Label)
Tôi có một chức năng mà lấy về tất cả Label
s và Thing
s và đặt chúng vào một cấu trúc dữ liệu JSON, trong đó Thing
s tham khảo Label
s sử dụng id
s của họ (khóa chính). Một cái gì đó như thế này:
{
'labels': [
{ 'id': 123, 'name': 'label foo' },
...
],
'things': [
{ 'id': 45, 'name': 'thing bar', 'labels': [ 123, ... ] },
...
]
}
Cách hiệu quả nhất để có được cấu trúc dữ liệu như vậy bằng Django là gì? Giả sử tôi có LLabel
s và TThing
s, và tỷ lệ trung bình Thing
có xLabel
s.
Phương pháp 1:
data = {}
data['labels'] = [model_to_dict(label) for label in Label.objects.all()]
data['things'] = [model_to_dict(thing) for thing in Thing.objects.all()]
Điều này làm cho (1 + 1 + T) truy vấn cơ sở dữ liệu, vì model_to_dict(thing)
nhu cầu để lấy Label
s cho mỗi Thing
riêng.
Cách 2:
data = {}
data['labels'] = [model_to_dict(label) for label in Label.objects.all()]
data['things'] = [model_to_dict(thing) for thing in
Thing.objects.prefetch_related('labels').all()]
Điều này làm cho (1 + 1 + 1) cơ sở dữ liệu truy vấn mà thôi, kể từ khi Thing
s lấy bây giờ có họ Label
s tìm nạp trước trong một truy vấn bổ sung duy nhất.
Điều này vẫn không thỏa đáng.prefetch_related('labels')
sẽ tìm nạp nhiều bản sao của cùng một Label
, trong khi tôi chỉ cần id
s của mình. Có cách nào để tìm nạp trước các số id
s chỉ trong số Label
không? Tôi đã thử prefetch_related('labels__id')
nhưng điều đó không hiệu quả. Tôi cũng lo ngại rằng vì T lớn (hàng trăm), prefetch_related('labels')
dẫn đến truy vấn SQL với mệnh đề IN
lớn. L là nhỏ hơn nhiều (< 10), vì vậy tôi có thể làm điều này thay vì:
Phương pháp 3:
data = {}
data['labels'] = [model_to_dict(label) for label in
Label.objects.prefetch_related('thing_set').all()]
things = list(Thing.objects.all())
# plug in label ids by hand, and also fetch things that have zero labels
# somehow
Điều này dẫn đến một IN
khoản nhỏ hơn, nhưng vẫn không thỏa đáng vì prefetch_related('thing_set')
fetches trùng lặp Thing
s, nếu Thing
có nhiều Label
s.
Tóm tắt:
Label
và Thing
được nối với nhau bằng một ManyToManyField
. Tôi đang tìm nạp tất cảLabel
s và Thing
s. Vậy làm thế nào để tôi cũng lấy mối quan hệ nhiều-nhiều của họ một cách hiệu quả?
Có thể thử sử dụng mô hình trung gian cho m2m? Lược đồ DB và bất kỳ thứ gì khác sẽ vẫn giữ nguyên nhưng bạn sẽ chỉ có thể 'fetch_related' mô hình này và lấy ID của nhãn từ nó. Nếu bạn sẽ liên kết nó với đối số 'đến' với M2M, một số phương thức như' add() 'sẽ bị hỏng, nhưng bạn có thể cung cấp' db_table' theo cách thủ công cho nó và không chạm vào trường m2m, vì vậy nó sẽ hoạt động. – ilvar
Cảm ơn @ilvar, nhận xét của bạn đã dẫn tôi đến câu trả lời bên dưới. – cberzan