Django 2.2 ORM Exclude работает не так, как ожидалось

#django #django-queryset

#django #django-набор запросов

Вопрос:

Я пытаюсь получить все модели формы списка клиентов Cust , у которых нет записи в Stat (ForeignKey), Django 2.2

 
class Cust(models.Model):
    name = models.CharField(max_length=50)
    active = models.BooleanField(default=True)


class Stat(models.Model):
    cust = models.ForeignKey(Cust, on_delete=models.PROTECT, null=True)
    date = models.DateField(null=True, blank=True)
    
 

я пытаюсь это сделать, но не получается,

     month = datetime.now()
    month = month.strftime("%Y-%m")
    
    inner_qs = Stat.objects.filter(date__icontains=month)
    data = Cust.objects.exclude(id__in=inner_qs)
    
    print(inner_qs)
    print(data)

 

Приведенный выше запрос возвращает:

 
<QuerySet [<Stat: Ruth>]>
<QuerySet [<Cust: Jhonny>, <Cust: Rony>, <Cust: Sinta>, <Cust: Ruth>]>

 

Как вы можете видеть, мне нужен результат [<Stat: Ruth>] , исключенный из набора / списка запросов данных.

но я ожидал, что:

 <QuerySet [<Stat: Ruth>]>
<QuerySet [<Cust: Jhonny>, <Cust: Rony>, <Cust: Sinta>>
 

Ответ №1:

Согласно документу django doc 1 about __in , в нем говорится:

В заданном итерируемом; часто список, кортеж или набор запросов. Это не обычный вариант использования, но строки (являющиеся итеративными) принимаются.

Есть несколько способов решить эту проблему.

заменить

     inner_qs = Stat.objects.filter(date__icontains=month)
    data = Cust.objects.exclude(id__in=inner_qs)
 

1. с

     inner_qs = Stat.objects.filter(date__icontains=month)
    data = Cust.objects.exclude(id__in=[o.cust_id for o in inner_qs])
 

2. Другой способ — заменить на

     id_list =Stat.objects.filter(date__icontains=month)
            .values_list('cust_id', flat=True) 
            # flat=True will return list rather 
            # than tuple in the ValueListQueryset

    data = Cust.objects.exclude(id__in=id_list)
 

Здесь вы сначала создаете список идентификаторов исключения, а затем используете его для исключения.

3. пересмотренный способ # 2 с distinct()

     id_list =Stat.objects.filter(date__icontains=month)
            .values_list('cust_id', flat=True)
            .distinct().order_by() 
            # distinct() is used to get unique 'cust_id's
            # distinct() does not work without order_by()

    data = Cust.objects.exclude(id__in=id_list)
 

Комментарии:

1. Спасибо! это работает! мне нужно только изменить id cust_id в данном решении, чтобы оно заработало@

2. упс!!! Я пропустил эту часть. Я отредактировал свой ответ и добавил метод distinct().