You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Django视图中使用F表达式进行QuerySet注解时触发TypeError

问题:Django中使用F表达式计算搜索相关性得分时触发TypeError错误

问题场景

我在Django中实现了处理搜索功能的视图,该视图根据用户查询过滤Question和Code数据,并尝试用Django的F表达式计算相关性得分,但在使用F表达式进行QuerySet注解时遇到了TypeError错误。

视图相关代码片段

class SearchResultView(TemplateView):
    template_name = 'codeisc/search_result_page.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['user'] = self.request.user if self.request.user.is_authenticated else None
        query = kwargs.get('query')
        keywords = query.split()

        questions = Question.objects.filter(Q(short_description__icontains=query) | Q(description__icontains=query))
        for keyword in keywords:
            questions = questions.annotate(
                relevance_score=(
                        F('short_description__icontains', keyword) +
                        F('description__icontains', keyword)
                )
            )
        questions = questions.order_by('-relevance_score')
        context['questions'] = questions

        codes = Code.objects.filter(Q(short_description__icontains=query) | Q(code_text__icontains=query))
        for keyword in keywords:
            codes = codes.annotate(
                relevance_score=(
                        F('short_description__icontains', keyword) +
                        F('code_text__icontains', keyword)
                )
            )
        codes = codes.order_by('-relevance_score')
        context['codes'] = codes

        return context

报错信息

Exception Type: TypeError
Exception Value: F.__init__() takes 2 positional arguments but 3 were given

模型定义

class Code(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="Code_Author")
    short_description = models.CharField(max_length=100, null=False, default="No Description Provided")
    code_text = models.TextField(blank=True, null=False)
    created_at = models.DateTimeField(default=timezone.now)
    TYPE_CHOICES = [
        ("TXT", "TextFile"),
        ("PY", "Python"),
        ("JS", "Javascript"),
        ("C", "C"),
        ("CPP", "C++"),
        ("CS", "Csharp"),
        ("JV", "Java"),
    ]
    type = models.CharField(max_length=3, choices=TYPE_CHOICES, default="TXT")

    def __str__(self):
        return f"{self.short_description}:{self.author.username}"


class Question(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="Question_Author", null=True)
    short_description = models.CharField(max_length=100, null=False, default="No Description Provided")
    description = models.TextField(null=False, default="No Description Provided")
    code = models.ManyToManyField(Code)
    created_at = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return f"{self.short_description}:{self.author.username}"

解决方案

错误原因

F()仅接收字段名作为参数,不能直接传入__icontains这类查询谓词和匹配关键词,这种误用导致传入了多余参数,触发TypeError。要实现关键词匹配的相关性得分,需用Case+When来判断字段是否包含关键词,匹配则累加得分。

修改后的视图代码

from django.db.models import Case, When, Value, IntegerField

class SearchResultView(TemplateView):
    template_name = 'codeisc/search_result_page.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['user'] = self.request.user if self.request.user.is_authenticated else None
        query = kwargs.get('query')
        
        # 处理空查询情况
        if not query:
            context['questions'] = []
            context['codes'] = []
            return context
        
        keywords = query.split()

        # 计算Question的相关性得分
        questions = Question.objects.filter(Q(short_description__icontains=query) | Q(description__icontains=query))
        score_expr = Value(0, output_field=IntegerField())
        for keyword in keywords:
            # 匹配短描述加1分,匹配描述加1分
            score_expr += Case(
                When(short_description__icontains=keyword, then=Value(1)),
                default=Value(0),
                output_field=IntegerField()
            ) + Case(
                When(description__icontains=keyword, then=Value(1)),
                default=Value(0),
                output_field=IntegerField()
            )
        questions = questions.annotate(relevance_score=score_expr).order_by('-relevance_score')
        context['questions'] = questions

        # 计算Code的相关性得分
        codes = Code.objects.filter(Q(short_description__icontains=query) | Q(code_text__icontains=query))
        code_score_expr = Value(0, output_field=IntegerField())
        for keyword in keywords:
            code_score_expr += Case(
                When(short_description__icontains=keyword, then=Value(1)),
                default=Value(0),
                output_field=IntegerField()
            ) + Case(
                When(code_text__icontains=keyword, then=Value(1)),
                default=Value(0),
                output_field=IntegerField()
            )
        codes = codes.annotate(relevance_score=code_score_expr).order_by('-relevance_score')
        context['codes'] = codes

        return context

说明

  1. 用Case+When判断每个字段是否包含关键词,匹配则加1分,否则加0分
  2. 对所有关键词的得分进行累加,得到最终的相关性得分
  3. 避免循环中重复annotate的问题,一次性构建完整得分表达式
  4. 增加空查询判断,防止无查询词时引发异常

内容的提问来源于stack exchange,提问作者BGOPC

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 03:52:45