Sam Richardson
Sam Richardson

Reputation: 1

Django Filter Issue Many to Many

I am having a problem where my chained filter using many to many lookups are not performing as I expected. Here are the pertinent details:

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))

So the issue is that cph1 will not return the appropriate SurveyAnswer objects. If I remove all but one of the Q objects the expected SurveyAnswer will return in the queryset but once I add a second Q object the expected Survey Answer does not get met by the filter. Any help would be greatly appreciated. If there are any missing details I can add them in.

Upvotes: 0

Views: 60

Answers (1)

azundo
azundo

Reputation: 6052

filter arguments are combined with AND by default. If you are looking for OR you can combine the Q objects using the | operator:

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) |
)

Upvotes: 1

Related Questions