Django过滤器问题多对多

Posted

技术标签:

【中文标题】Django过滤器问题多对多【英文标题】:Django Filter Issue Many to Many 【发布时间】:2021-04-07 18:51:14 【问题描述】:

我遇到了一个问题,即使用多对多查找的链式过滤器未按预期执行。以下是相关细节:

views.py
    def cs_edit_result(request, ctype=None, survey_id=None):
        #if POST request resume answering survey answers from a selected customer survey.
        if request.method == 'GET':
            sid = Survey.objects.get(pk=survey_id)
        if request.method == 'POST':
            sid = Survey.objects.get(pk=survey_id)
            form = CustomerSurveyForm(request.POST, instance=sid)
            #filter placeholders to only form data from customer survey:
            if form.is_valid():
                cs_result = form.save()
                answers = SurveyAnswers.objects.filter(surveyid=sid.id)
                for x in answers:
                    x.status=False
                    x.save()
                print(form)
                citype = form.cleaned_data['institution_type']
                cproducts = form.cleaned_data['products']
                cpurchases = form.cleaned_data['purchase']
                cservices = form.cleaned_data['services']
                colb = form.cleaned_data['olb_vendor']
                cph1 = SurveyAnswers.objects.filter(surveyid=cs_result).filter(
                                                  Q(placeholder__purchase__in=cpurchases),
                                                  Q(placeholder__institution_type__in=citype), 
                                                  Q(placeholder__olb_vendor__in=colb),
                                                  
models.py
class PlaceHolder(models.Model):
    output_choices = [
        ('link','Link'),
        ('plain text', 'Plain Text'),
        ('html', 'HTML')
        ]
    purchase = models.ManyToManyField(Purchase, related_name='purchase_place')
    institution_type = models.ManyToManyField(InstitutionType, related_name='itype_place')
    products = models.ManyToManyField(Product,blank=True, related_name='products_place')
    services = models.ManyToManyField(Service,blank=True, related_name='services_place')
    olb_vendor = models.ManyToManyField(OLB_Vendor,blank=True, related_name='olb_place')
    output_type = models.ManyToManyField(Output_Type)
    question = models.TextField(null=True, verbose_name = 'question/title')
    silvercloud_placeholder = models.CharField(max_length=50)
    output_type = models.CharField(
        max_length=100, 
        choices= output_choices, 
        default='Plain Text')
    answer = models.TextField(blank=True,null=True, verbose_name='url/answer')

    def __str__(self):
        return self.silvercloud_placeholder

class Survey(models.Model):
    customer = models.CharField(max_length=50)
    purchase = models.ManyToManyField(Purchase)
    institution_type = models.ManyToManyField(InstitutionType)
    products = models.ManyToManyField(Product)
    services = models.ManyToManyField(Service,blank=True)
    olb_vendor = models.ManyToManyField(OLB_Vendor)

    def __str__(self):
        return (' Survey '.format(self.customer, str(self.id)))

class SurveyAnswers(models.Model):
    #contributor = admin.
    surveyid = models.ForeignKey(Survey,on_delete=models.CASCADE)
    placeholder = models.ForeignKey(PlaceHolder,on_delete=models.CASCADE)
    answer = models.TextField(blank=True,null=True)
    status = models.BooleanField(default=False)

    class meta:
        constraints = [
            models.UniqueConstraint(fields=['surveyid', 'placeholder'], name='Unique Answer')
            ]
    def __str__(self):
        return (' Survey  '.format(self.surveyid.customer, str(self.surveyid.id),self.placeholder.silvercloud_placeholder))

所以问题是 cph1 不会返回适当的 SurveyAnswer 对象。如果我删除除了一个 Q 对象之外的所有对象,则预期的 SurveyAnswer 将在查询集中返回,但是一旦我添加第二个 Q 对象,过滤器不会满足预期的调查答案。任何帮助将不胜感激。如果有任何遗漏的细节,我可以添加它们。

【问题讨论】:

【参考方案1】:

filter 参数默认与AND 组合。如果您正在寻找OR,您可以使用| 运算符组合Q 对象:

cph1 = SurveyAnswers.objects.filter(surveyid=cs_result).filter(
    Q(placeholder__purchase__in=cpurchases) |
    Q(placeholder__institution_type__in=citype) |
    Q(placeholder__olb_vendor__in=colb) |
)

【讨论】:

感谢您的回复。这不是问题。我需要过滤器才能使用 AND 运算符。问题是 SurveyAnswer[x] 符合 Q(placeholder__purchase__in=cpurchases) 的标准。它也符合 Q(placeholder__institution_type__in=citype) 的标准。但是当我有这样的系列过滤器时,SurveyAnswer[x] 不会返回。 而且还满足placeholder__olb_vendor__in=colb?我认为它可能是 OR 而不是 AND 的原因是因为 Q 对象在这种情况下不是必需的,因为 AND 是默认值,您可以将它们直接传递给 filter。但是您对它应该如何工作的理解是正确的,因此请仔细检查 SurveryAnswer[x] 是否确实满足这些条件。可能是您的表单数据格式不正确,无法处理您的查询,或者 null 值没有得到正确考虑。 form.cleaned_data[x] 是问题所在。我需要将该数据类型转换为 ID 列表。然后在我的过滤器中使用 ID 字段(例如:placeholder__purchase__id__in=cpuchase_id_list)。感谢您指出这可能是问题所在。我被困了一段时间。 cpurchase 是一个查询集。所以我做了一个循环把它转换成一个列表: cpurchases = form.cleaned_data['purchase'] purchase_list = [] for x in cpurchases: purchase_list.append(x.id) print(purchase_list) 我为每个表单键。有没有更好的方法从查询集中获取 Id?也许在一行中而不是一个 for 循环? 是的!你可以使用cpurchases.values_list('id', flat=True)

以上是关于Django过滤器问题多对多的主要内容,如果未能解决你的问题,请参考以下文章

Django 多对多字段过滤器列表

Django如何过滤多对多字段中的对象,而不是原始查询集

Django 多对多交集过滤

Django Rest Framework(多对多字段上的 GET 过滤器)

过滤后更新 Django 的多对多

在Django中按关系字段过滤多对多关系