英文:
Optimizing Django queries - reducing database requests & proper queryset access
问题
以下是您要翻译的内容:
我的视图
def home(request):
q = ''
if request.GET.get('q'):
q = request.GET.get('q')
rooms = Room.objects.select_related('host', 'topic').prefetch_related('participants').filter(
Q(topic__name__icontains=q) |
Q(name__icontains=q) |
Q(description__icontains=q)
)
topics = Topic.objects.select_related('user')
profile = Profile.objects.filter(user=request.user)
r_messages = Message.objects.select_related('user', 'room').filter(Q(room__topic__name__icontains=q))
return render(request, 'base/home-page.html', context={
'rooms': rooms,
'topics': topics,
'profile': profile,
'r_messages': r_messages,
})
我的模型
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
profile_image = models.FileField(default='default.jpg', upload_to='profile_image', null=True)
name = models.CharField(max_length=100, null=True, blank=True)
bio = models.TextField(max_length=5000, null=True, blank=True)
email = models.EmailField()
username = models.CharField(max_length=100)
def __str__(self):
return f'{self.user}'
def get_absolute_url(self):
return reverse('user-profile', args=[str(self.user)])
class Topic(models.Model):
name = models.CharField(max_length=200)
user = models.ForeignKey(Profile, on_delete=models.CASCADE)
def __str__(self):
return f'{self.name}'
class Room(models.Model):
host = models.ForeignKey(Profile, on_delete=models.SET_NULL, null=True)
topic = models.ForeignKey(Topic, on_delete=models.SET_NULL, null=True)
name = models.CharField(max_length=200)
description = models.TextField(null=True, blank=True)
participants = models.ManyToManyField(Profile, related_name='participants', blank=True)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-updated', '-created']
def __str__(self):
return f'{self.name}'
class Message(models.Model):
user = models.ForeignKey(Profile, on_delete=models.CASCADE)
room = models.ForeignKey(Room, on_delete=models.CASCADE)
body = models.TextField()
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'{self.body[:50]}'
class Meta:
ordering = ['-updated', '-created']
我的模板
{% for room in rooms %}
<li>
{{ room.topic.host }}
<a href="{% url 'home' %}?q={{ room.topic.name }}">{{ room.topic.name }}<span>{{ room.topic.room_set.all.count }}</span></a>
</li>
{% endfor %}
查询
当我尝试使用{{ room.topic.room_set.all.count }}时,如果有3个主题,ORM将查询3次DB以查找每个主题的房间计数。
我知道问题出现在模板中的此部分{{ room.topic.room_set.all.count }},但不知道如何修复它。
英文:
My Views
def home(request):
q = ''
if request.GET.get('q'):
q = request.GET.get('q')
rooms = Room.objects.select_related('host', 'topic').prefetch_related('participants').filter(
Q(topic__name__icontains=q) |
Q(name__icontains=q) |
Q(description__icontains=q)
)
topics = Topic.objects.select_related('user')
profile = Profile.objects.filter(user=request.user)
r_messages = Message.objects.select_related('user', 'room').filter(Q(room__topic__name__icontains=q))
return render(request, 'base/home-page.html', context={
'rooms': rooms,
'topics': topics,
'profile': profile,
'r_messages': r_messages,
})
My Models
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
profile_image = models.FileField(default='default.jpg', upload_to='profile_image', null=True)
name = models.CharField(max_length=100, null=True, blank=True)
bio = models.TextField(max_length=5000, null=True, blank=True)
email = models.EmailField()
username = models.CharField(max_length=100)
def __str__(self):
return f'{self.user}'
def get_absolute_url(self):
return reverse('user-profile', args=[str(self.user)])
class Topic(models.Model):
name = models.CharField(max_length=200)
user = models.ForeignKey(Profile, on_delete=models.CASCADE)
def __str__(self):
return f'{self.name}'
class Room(models.Model):
host = models.ForeignKey(Profile, on_delete=models.SET_NULL, null=True)
topic = models.ForeignKey(Topic, on_delete=models.SET_NULL, null=True)
name = models.CharField(max_length=200)
description = models.TextField(null=True, blank=True)
participants = models.ManyToManyField(Profile, related_name='participants', blank=True)
updated = models.DateTimeField(auto_now=True) # Save every time when we update
created = models.DateTimeField(auto_now_add=True) # Only save when room was created
class Meta:
ordering = ['-updated', '-created']
def __str__(self):
return f'{self.name}'
class Message(models.Model):
user = models.ForeignKey(Profile, on_delete=models.CASCADE)
room = models.ForeignKey(Room, on_delete=models.CASCADE)
body = models.TextField()
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'{self.body[:50]}'
class Meta:
ordering = ['-updated', '-created']
My Template
{% for room in rooms %}
<li>
{{ room.topic.host }}
<a href="{% url 'home' %}?q={{ room.topic.name}}">{{ room.topic.name}}<span>{{ room.topic.room_set.all.count}}</span></a>
</li>
{% endfor %}
queries
When I try to use {{ room.topic.room_set.all.count}} and if there are 3 topics, the ORM will query 3 times to the DB to find each topic.rooms.count.
I know that the problem in this paro of code in template {{ room.topic.room_set.all.count}} but don't know how to fix it.
答案1
得分: 0
这里是翻译好的部分:
"it is very simple all you need is to use select_related and prefetch_related in your quires to get all the data you need at once so your modified query in your view will be look like this:
def home(request):
....
rooms = Room.objects.select_related('host', 'topic').prefetch_related('participants').filter(
Q(topic__name__icontains=q) |
Q(name__icontains=q) |
Q(description__icontains=q)
) # this is a new query
topics = Topic.objects.select_related('user').prefetch_related('room_set') # this is a new query
profile = Profile.objects.filter(user=request.user)
r_messages = Message.objects.select_related('user', 'room').filter(Q(room__topic__name__icontains=q)) # this is a new query
....
注意:我已经将原文中的 HTML 实体编码转换为普通字符。
英文:
it is very simple all you need is to use select_related and prefetch_related in your quires to get all the data you need at once so your modified query in your view will be look like this:
def home(request):
....
rooms = Room.objects.select_related('host', 'topic').prefetch_related('participants').filter(
Q(topic__name__icontains=q) |
Q(name__icontains=q) |
Q(description__icontains=q)
) # this is a new query
topics = Topic.objects.select_related('user').prefetch_related('room_set') # this is a new query
profile = Profile.objects.filter(user=request.user)
r_messages = Message.objects.select_related('user', 'room').filter(Q(room__topic__name__icontains=q)) # this is a new query
.....
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论