Django通过MultiSelectField值过滤QueryString?

时间:2014-07-28 14:46:20

标签: python django django-queryset

我正在使用MultiSelectField来存储主题/主题的对话

我的模型看起来有点像这样:

class Conversation(models.Model):
    (...)
    note= models.CharField(max_lenght=250)
    TOPIC_CHOICES =(
        (1,"about cats"),
        (2, "about dogs"),
        (3, "about fluffy little things"),
    )
    topic =MultiSelectField(choices=TOPIC_CHOICES)

我正在使用ListView并通过get_queryset内的GET参数进行过滤:

表单提取:

class SearchForm(Form):
    (...)
    topic = MultipleChoiceField(choices=Conversation.TOPIC_CHOICES, required=False)

get_queryset extract:

(...)
if form.cleaned_data['topic']:
                search_params.update({'topic__in': form.cleaned_data['topic']})
(...)
return qs.filter(**search_params)

此方法适用于单值选择字段。

但在这种情况下,如果我为前。选择形式“关于猫”我只获得了主题设置为猫(“关于猫”和其他任何东西 - 单值)的对象。

我想要的是所有对象,其中一个主题值是1-“关于猫”。这意味着如果某个对象有topic = 1,3(猫和蓬松的东西)它也应该出现

第二种情况:我在表单中选择“关于猫”和“关于狗” - 我想要所有对象将猫作为主题的和所有对象之一有一只狗作为主题之一 现在,当我为前者选择多个选项时。猫和狗我得到的只有只有猫和所有只有 狗作为主题

是否有其他字段查找字符串而不是__in才能实现这一目标? 如果不是最麻烦的方式那么做什么?

3 个答案:

答案 0 :(得分:2)

如何使用Django Q设置,所以你的过滤器看起来像:

2016-01-21 16:58:01.042  INFO 6650 --- [ost-startStop-1] o.s.s.web.DefaultSecurityFilterChain     : Creating filter chain: Ant [pattern='/ping'], []
2016-01-21 16:58:01.043  INFO 6650 --- [ost-startStop-1] o.s.s.web.DefaultSecurityFilterChain     : Creating filter chain: Ant [pattern='/api/**'], []
2016-01-21 16:58:01.098  INFO 6650 --- [ost-startStop-1] o.s.s.web.DefaultSecurityFilterChain     : Creating filter chain: org.springframework.security.web.util.matcher.AnyRequestMatcher@1, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@695fd199, org.springframework.security.web.context.SecurityContextPersistenceFilter@39df299d, org.springframework.security.web.header.HeaderWriterFilter@5f43f2cc, org.springframework.security.web.authentication.logout.LogoutFilter@23a4d0ad, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@31fa8663, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@35e52479, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@5d39a5fc, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@28a18a3d, org.springframework.security.web.session.SessionManagementFilter@3dffe958, org.springframework.security.web.access.ExceptionTranslationFilter@40c2a999, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@43f059e6]

答案 1 :(得分:1)

MultiSelectField基本上是一个CharField,它将多个选项值存储为逗号分隔的字符串。

因此,您需要全表扫描才能在MultiSelectField上执行此类QuerySet过滤。您可以使用__regex field lookup过滤QuerySet以匹配给定的选择值:

(...)
searched_topics = form.cleaned_data['topic']
if searched_topics:
    search_topic_regexp = "(^|,)%s(,|$)" % "|".join(searched_topics)
    search_params.update({'topic__regex': search_topic_regexp})
(...)

为了获得更好的性能(当你有很多Conversation条目时,为了避免在主题字段上进行全表扫描),你不应该使用MultiSelectField而是使用ManyToMany relationship(使用单独的连接表)。 / p>

答案 2 :(得分:0)

MultiSelectField将值作为逗号分隔的字符串存储在CharField中,因此您基本上希望使用.topic__contains=topic而不是.topic__in=topic

但是,根据您的搜索表单,您将希望使用不同的布尔运算符加入条件以缩小或扩大结果。根据您的需要,我将执行以下操作之一:

  • 使用Q通过OR联接来构建复杂的查询(更复杂,但用途更多)
  • 将字典中的命名参数解压缩到.filter()中(简单但受限制)

动态地用Q构建查询。

这是更好的方法。它使您可以在查询中使用ORANDNOTXOR来优化搜索选项。

# ...
search_params = Q()

# Let's say we have other search parameters also
search_params = search_params | Q(note__contains="example note")

if form.cleaned_data['topic']:
    search_params = search_params | Q(topic__in=form.cleaned_data['topic'])

# ...
return qs.filter(search_params)

生成的MYSQL查询将如下所示:

SELECT * FROM `search_form`
    WHERE (
        `note` LIKE '%example note%' OR
        `topic` LIKE '%about cats%'
    )

从字典中解压缩命名参数

此方法只能用于在查询中使用AND来缩小搜索结果。

# ...

search_params = {}

# let's say there are other items in the query
search_params['note__contains'] = 'example note'

if form.cleaned_data['topic']:
    search_params['topic__contains'] = form.cleaned_data['topic']

# ...
# unpack the search_params into the named parameters
return qs.filter(**search_params)

这将有效地构建类似于Django的查询:

SearchForm.objects.filter(
    note__contains="example note",
    topic__contains="about cats"
)

结果数据库查询将类似于:

SELECT * FROM `search_form`
    WHERE (
        `note` LIKE '%example note%' AND 
        `topic` LIKE '%about cats%'
    )