优化 Django 查询 – 减少数据库请求和正确的查询集访问

huangapple go评论70阅读模式
英文:

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 = &#39;&#39;
if request.GET.get(&#39;q&#39;):
q = request.GET.get(&#39;q&#39;)
rooms = Room.objects.select_related(&#39;host&#39;, &#39;topic&#39;).prefetch_related(&#39;participants&#39;).filter(
Q(topic__name__icontains=q) |
Q(name__icontains=q) |
Q(description__icontains=q)
)
topics = Topic.objects.select_related(&#39;user&#39;)
profile = Profile.objects.filter(user=request.user)
r_messages = Message.objects.select_related(&#39;user&#39;, &#39;room&#39;).filter(Q(room__topic__name__icontains=q))
return render(request, &#39;base/home-page.html&#39;, context={
&#39;rooms&#39;: rooms,
&#39;topics&#39;: topics,
&#39;profile&#39;: profile,
&#39;r_messages&#39;: r_messages,
})

My Models

class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
profile_image = models.FileField(default=&#39;default.jpg&#39;, upload_to=&#39;profile_image&#39;, 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&#39;{self.user}&#39;
def get_absolute_url(self):
return reverse(&#39;user-profile&#39;, 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&#39;{self.name}&#39;
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=&#39;participants&#39;, 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 = [&#39;-updated&#39;, &#39;-created&#39;]
def __str__(self):
return f&#39;{self.name}&#39;
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&#39;{self.body[:50]}&#39;
class Meta:
ordering = [&#39;-updated&#39;, &#39;-created&#39;]

My Template

{% for room in rooms %}
&lt;li&gt;
{{ room.topic.host }}
&lt;a href=&quot;{% url &#39;home&#39; %}?q={{ room.topic.name}}&quot;&gt;{{ room.topic.name}}&lt;span&gt;{{ room.topic.room_set.all.count}}&lt;/span&gt;&lt;/a&gt;
&lt;/li&gt;
{% 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(&#39;host&#39;, &#39;topic&#39;).prefetch_related(&#39;participants&#39;).filter(
Q(topic__name__icontains=q) |
Q(name__icontains=q) |
Q(description__icontains=q)
) # this is a new query
topics = Topic.objects.select_related(&#39;user&#39;).prefetch_related(&#39;room_set&#39;) # this is a new query
profile = Profile.objects.filter(user=request.user)
r_messages = Message.objects.select_related(&#39;user&#39;, &#39;room&#39;).filter(Q(room__topic__name__icontains=q)) # this is a new query
.....

huangapple
  • 本文由 发表于 2023年5月25日 01:58:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/76326271.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定