【问题标题】:Forcing Django to use INNER JOIN instead of LEFT OUTER JOIN强制 Django 使用 INNER JOIN 而不是 LEFT OUTER JOIN
【发布时间】:2014-07-24 07:02:00
【问题描述】:

我在我的 Django 应用程序中实现了搜索,以允许通过多个字段进行搜索。这导致 Django 总是使用LEFT OUTER JOIN,在我的例子中会产生错误的结果。但是,当我将生成的 SQL 从LEFT OUTER JOIN 更改为INNER JOIN 时,它会返回正确的结果。

我认为这与我下面代码中的 Q object 方式有关。

from django.db import models, transaction
...
def construct_search(field_name):
    if field_name.startswith('^'):
        return "%s__istartswith" % field_name[1:]
    elif field_name.startswith('='):
        return "%s__iexact" % field_name[1:]
    elif field_name.startswith('@'):
        return "%s__search" % field_name[1:]
    else:
        return "%s__icontains" % field_name


class CoreSearchMixin(object):
    """Subclasses must define search_fields = [field_1, ...field_n]
    where the field is a string, the name of a field, and can contain the following prefix characters:

    '^': the search field must start with the search term, case insensitive
    '=': the search field must exactly equal the search term, case insensitive
    '@': full-text search

    If no prefix is given, any string that contains the search field will match.
    """
    search_fields = None
    search_form_class = SearchForm

    @cachedproperty
    def search_form(self):
        return self.search_form_class(getattr(self.request, self.request.method))

    def get_query_help_message(self):
        """Returns a comma separated list of fields that are used in the search, to help the user
        create a search.
        """
        fields = []
        if self.search_fields:
            for search_field in self.search_fields:
                field = get_field_from_path(self.model, search_field)
                fields.append(field.verbose_name.title())
        return ",".join(fields)

    def get_filtered_queryset(self, queryset):
        if self.search_form.is_valid():
            self.query = self.search_form.cleaned_data['q']
        else:
            self.query = None
        if self.search_fields and self.query:
            orm_lookups = (construct_search(str(search_field).replace('.', '__'))
                           for search_field in self.search_fields)
            chained_or_queries = None
            for bit in self.query.split():
                or_queries = (models.Q(**{orm_lookup: bit})
                              for orm_lookup in orm_lookups)
                if chained_or_queries:
                    chained_or_queries = itertools.chain(chained_or_queries, or_queries)
                else:
                    chained_or_queries = or_queries
            return queryset.filter(reduce(operator.or_, chained_or_queries))
        else:
            return queryset

    def get_context_data(self, **kwargs):
        return super(CoreSearchMixin, self).get_context_data(
            search_form=self.search_form,
            query_help_message=self.get_query_help_message(),
            search_fields=self.search_fields,
            **kwargs
        )

在上述代码的情况下,如何确保使用 INNER JOIN 而不是 LEFT OUTER JOIN?

【问题讨论】:

  • 那你为什么不用Q搜索呢?使用 Q 搜索,您可以搜索多个字段。你能告诉我,这个问题的重点,我不明白你为什么要实施这个。 Q search 已经支持使用多个字段进行搜索。例如 Poll.objects.filter(field1__startswith='Who', Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))
  • 如果您可以包含上面引用的代码生成的 SQL,将会很有帮助。

标签: django


【解决方案1】:

根据您的问题,您想搜索多个字段。但是,按照您的逻辑,在 OR 序列中找到的第一个结果将被返回——而不返回 OR 序列中可能的后续匹配;请记住,OR 运算符停止评估真实结果。

为了将您的 OUTER LEFT JOINs 转换为 INNER JOINs,您需要拥有 AND/OR Q 搜索字段组合的对象排列(最佳?),或者单独查询它们并执行结果的交集(次优),或自己编写 SQL(次优)。

PS:在编写用于 Django 的 Datatables API 包装器之前,我遇到了这个问题。 PS:我会考虑重构,并进一步评论你的代码——特别是get_filtered_queryset;我花了几分钟才明白这里发生了什么。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-06-25
    • 2015-07-28
    • 2016-10-31
    • 2015-01-10
    • 1970-01-01
    • 2013-05-02
    • 1970-01-01
    • 2010-12-03
    相关资源
    最近更新 更多