Django 对象.filter的使用及其在数据库服务器上的开销

112 阅读3分钟

在 Django 中,可以使用 objects.filter() 方法来对模型对象进行过滤。在本文中,我们遇到一个场景,需要根据几个条件对 Soknad 模型对象进行过滤,包括发布年份、城市名称、类型名称和文本内容。我们使用以下代码来实现过滤:

```python
soknad_list = Soknad.objects.all()
if var1:
    soknad_list = soknad_list.filter(pub_date__year=var1)

if var2:
    soknad_list = soknad_list.filter(muncipality__name__exact=var2)

if var3:
    soknad_list = soknad_list.filter(genre__name__exact=var3)

# TEXT SEARCH
stop_word_list = re.compile(STOP_WORDS, re.IGNORECASE)
search_term = '%s' % request.GET['q']
cleaned_search_term = stop_word_list.sub('', search_term)
cleaned_search_term = cleaned_search_term.strip()
if len(cleaned_search_term) != 0:
    soknad_list = soknad_list.filter(Q(dream__icontains=cleaned_search_term) | Q(tags__icontains=cleaned_search_term) | Q(name__icontains=cleaned_search_term) | Q(school__name__icontains=cleaned_search_term))
```

然而,我们担心这种过滤方式的效率问题,因为它可能导致对数据库服务器发出多次查询。因此,我们希望找到一种更有效的方法来实现相同的过滤功能。

  1. 解决方案 方法一:使用查询集的切片操作 我们可以使用查询集的切片操作来实现过滤功能。切片操作可以让我们只获取查询集中的部分对象,而无需将所有对象都加载到内存中。这种方法可以减少数据库服务器的开销,提高查询效率。

     soknad_list = Soknad.objects.all()
     if var1:
         soknad_list = soknad_list[var1]
    
     if var2:
         soknad_list = soknad_list[var2]
    
     if var3:
         soknad_list = soknad_list[var3]
    
     # TEXT SEARCH
     stop_word_list = re.compile(STOP_WORDS, re.IGNORECASE)
     search_term = '%s' % request.GET['q']
     cleaned_search_term = stop_word_list.sub('', search_term)
     cleaned_search_term = cleaned_search_term.strip()
     if len(cleaned_search_term) != 0:
         soknad_list = soknad_list.filter(Q(dream__icontains=cleaned_search_term) | Q(tags__icontains=cleaned_search_term) | Q(name__icontains=cleaned_search_term) | Q(school__name__icontains=cleaned_search_term))
    

    方法二:使用预取查询 预取查询可以让我们在执行查询时,同时加载相关联的对象。这种方法可以减少查询次数,提高查询效率。

     soknad_list = Soknad.objects.all().prefetch_related('pub_date', 'municipality', 'genre')
     if var1:
         soknad_list = soknad_list.filter(pub_date__year=var1)
    
     if var2:
         soknad_list = soknad_list.filter(municipality__name__exact=var2)
    
     if var3:
         soknad_list = soknad_list.filter(genre__name__exact=var3)
    
     # TEXT SEARCH
     stop_word_list = re.compile(STOP_WORDS, re.IGNORECASE)
     search_term = '%s' % request.GET['q']
     cleaned_search_term = stop_word_list.sub('', search_term)
     cleaned_search_term = cleaned_search_term.strip()
     if len(cleaned_search_term) != 0:
         soknad_list = soknad_list.filter(Q(dream__icontains=cleaned_search_term) | Q(tags__icontains=cleaned_search_term) | Q(name__icontains=cleaned_search_term) | Q(school__name__icontains=cleaned_search_term))
    

    方法三:使用自定义查询 自定义查询可以让我们直接编写 SQL 查询语句来实现过滤功能。这种方法可以让我们更加灵活地控制查询的执行过程,提高查询效率。

     query = """
         SELECT * FROM soknad
         WHERE pub_date_year = %s
         AND municipality_name = %s
         AND genre_name = %s
         AND (dream LIKE %s OR tags LIKE %s OR name LIKE %s OR school_name LIKE %s)
     """
     params = [var1, var2, var3, cleaned_search_term, cleaned_search_term, cleaned_search_term, cleaned_search_term]
     soknad_list = Soknad.objects.raw(query, params)
    

    方法四:使用索引 索引可以帮助数据库服务器更快地找到要查询的数据,从而提高查询效率。我们可以为 Soknad 模型的 pub_date、municipality 和 genre 字段创建索引。

     class Soknad(models.Model):
         pub_date = models.DateField(db_index=True)
         municipality = models.ForeignKey(Municipality, on_delete=models.CASCADE, db_index=True)
         genre = models.ForeignKey(Genre, on_delete=models.CASCADE, db_index=True)
         ...