J'utilise django 2.1, python 3.6 et SQL Server 2012 comme backend. J'ai les modèles suivants:

class ModelA(models.Model):
    name = models.CharField(...)
    value = models.PositiveIntegerField(...)

class ModelB(models.Model):
    name = models.CharField(...)
    values = models.ManyToManyField(ModelA, through='ModelC')

class ModelC(models.Model):
    model_a = models.ForeignKey(ModelA, ...)
    model_b = models.ForeignKey(ModelB, ...)
    info_a = models.CharField(...)
    info_b = models.CharField(...)

Comment puis-je réaliser la requête SQL suivante:

SELECT t1.model_a_id AS a_id, t3.value AS a_value
FROM ModelB AS t0
INNER JOIN ModelC t1 ON t1.model_b_id = t0.id
INNER JOIN ModelC t2 ON t2.model_b_id = t0.id
INNER JOIN ModelA t3 ON t3.id = t2.model_a_id
INNER JOIN ModelC t4 ON t4.model_b_id = t0.id
WHERE t1.model_a_id in (1,2) AND t2.model_a_id in (8,9,10,11) AND t4.model_a_id in (21,22)

Ce que j'ai jusqu'à présent:

ModelB.objects.filter(values__in=[1,2]).filter(values__in=[8,9,10,11]).filter(values__in=[21,22])

Qui produit le QuerySet filtré correct. Mais comment puis-je obtenir les bons champs?

J'ai essayé d'utiliser la fonction annotate mais j'ai échoué. L'utilisation de Subquery de django comme décrit dans la documentation génère une erreur de base de données, car SQL Server ne prend pas en charge les sous-requêtes dans la partie SELECT.

Des recommandations? Merci!

1
binaryEcon 8 mars 2019 à 02:04

2 réponses

Meilleure réponse

Je l'ai résolu sans revenir à sql brut. J'ai utilisé les objets FilteredRealtion de Django en combinaison d'objets supplémentaires annotate comme ceci :

from django.db.models import Q, F, FilteredRelation

qs = ModelB.objects.filter(values__in=[21,22])
qs = qs.filter(values__in=[1,2])
qs = qs.filter(values__in=[8,9,10,11])
qs = qs.annotate(_a_id=FilteredRelation('modelc', condition=Q(values__in=[8,9,10,11])),
                 _a_value=FilteredRelation('modelc', condition=Q(values__in=[1,2])))
qs = qs.annotate(a_id=F('_a_id__model_a'), a_value=F('_a_value__model_a__value'))
qs = qs.values('a_id', 'a_value')
0
binaryEcon 8 mars 2019 à 11:35

Votre requête n'est pas optimale, mais c'est un problème différent. Vous pouvez essayer la requête brute

Vous pouvez donc exécuter:

query = """
SELECT t1.model_a_id AS a_id, t3.value AS a_value
FROM ModelB AS t0
INNER JOIN ModelC t1 ON t1.model_b_id = t0.id
INNER JOIN ModelC t2 ON t2.model_b_id = t0.id
INNER JOIN ModelA t3 ON t3.id = t2.model_a_id
INNER JOIN ModelC t4 ON t4.model_b_id = t0.id
WHERE t1.model_a_id in ({0}) AND t2.model_a_id in ({1}) AND t4.model_a_id in 
({2})"""
t1_filters = ','.join(['1','2'])
t2_filters = ','.join(['8', '9', '10', '11'])
t4_filters = ','.join(['21', '22'])

results = ModelA.objects.raw(query.format(t1_filters, t2_filters, t4_filters))
for instance in results.all():
    print(instance)
0
Yarh 8 mars 2019 à 07:54