Comment interroger correctement un ManyToManyField pour tous les objets dans une liste (ou un autre ManyToManyField)?
Je suis plutôt perplexe quant à la meilleure façon de construire un Django requête qui vérifie si tous les éléments d'une ManyToMany
champ (ou une liste) sont présents dans un autre ManyToMany
champ.
Comme un exemple, j'ai plusieurs Person
s, qui peut avoir plus d'une Spécialité. Il y a aussi des Job
s que les gens peuvent commencer, mais ils nécessitent une ou plusieurs Specialty
s pour être admissible à être démarré.
class Person(models.Model):
name = models.CharField()
specialties = models.ManyToManyField('Specialty')
class Specialty(models.Model):
name = models.CharField()
class Job(models.Model):
required_specialties = models.ManyToManyField('Specialty')
Une personne peut commencer un travail seulement si ils ont tous les spécialités que le travail exige. Donc, encore une fois par souci d'exemple, nous avons trois spécialités:
- Codage
- Chant
- Danse
Et j'ai un Job
qui nécessite le Chant et la Danse spécialités. Une personne avec des Chants et de la Danse spécialités pouvez le démarrer, mais une autre avec le Codage et le Chant des spécialités qui ne peut pas -- que le Poste exige une Personne qui peut à la fois chanter et danser.
Alors, maintenant, j'ai besoin d'un moyen de trouver tous les emplois qu'une personne peut prendre. C'était ma façon de l'aborder, mais je suis sûre qu'il y a de plus élégant approche:
def jobs_that_person_can_start(person):
# we start with all jobs
jobs = Job.objects.all()
# find all specialties that this person does not have
specialties_not_in_person = Specialty.objects.exclude(name__in=[s.name for s in person.specialties])
# and exclude jobs that require them
for s in specialties_not_in_person:
jobs = jobs.exclude(specialty=s)
# the ones left should fill the criteria
return jobs.distinct()
C'est parce que l'utilisation de Job.objects.filter(specialty__in=person.specialties.all())
sera de retour un emploi qui correspond à tout de la personne spécialités, pas tous d'entre eux. À l'aide de cette requête, le travail qui exige de Chant et de Danse semble pour le chant codeur, ce qui n'est pas le résultat souhaité.
J'espère que cet exemple n'est pas trop compliquée à comprendre. La raison pour laquelle je m'inquiète c'est que les Spécialités dans le système sera probablement beaucoup plus, et en boucle sur eux ne semble pas être le meilleur moyen d'y parvenir. Je me demandais si quelqu'un pourrait donner un zéro pour cette démangeaison!
OriginalL'auteur rlafuente | 2009-12-03
Vous devez vous connecter pour publier un commentaire.
Une Autre Idée
Ok je crois que j'aurais ajouté ceci à l'autre réponse, mais quand j'ai commencé sur elle, il semblait que ça allait être une autre direction haha
Pas nécessaire d'itérer:
note: je ne sais pas exactement à quelle vitesse c'est. Vous pouvez être mieux avec mes autres suggestions.
Aussi: Ce code n'est pas testé
Je suis marquage de cette réponse acceptée car c'est exactement ce que ma question était de savoir; même si votre réponse suggère que de nombreuses autres améliorations des performances.
Merci! Heureux de vous aider =)
génial! Merci!!!!
OriginalL'auteur Jiaaro
Je pense que vous devriez regarder à l'aide de values_list à obtenir de la personne spécialités
Remplacer:
avec:
Qui va vous donner une simple liste (ie. ['spec1', 'spec2', ...]) que vous pouvez utiliser à nouveau. Et la requête sql utilisée dans le bg sera aussi plus rapide car il ne sélectionnez " nom " au lieu de faire un
select *
pour remplir l'ORM objetsVous pourriez aussi obtenir une amélioration de la vitesse par le filtrage des emplois que la personne certainement ne peut PAS effectuer:
pour le remplacer:
avec (2 requêtes - les œuvres de django 1.0+)
ou avec (1 requête? - travaux pour django1.1+)
Vous pouvez également obtenir une amélioration par l'utilisation de select_related() sur vos travaux/personne requêtes (car ils ont une clé étrangère que vous utilisez)
J'ai pensé à ça, faut-il faire la même requête que ce que j'ai ici? Je n'étais pas sûr
Wow -- merci beaucoup! Triste, il n'y a pas de "magie" façon de faire, mais vos suggestions sont une bouée de sauvetage ici.
selon le django docs, certains db backends (comme mysql) ne sont pas très rapide avec la sous-requête de la méthode docs.djangoproject.com/en/dev/ref/models/querysets/#in
Je ne pense pas que ce filtrage des enregistrements qui ne disposent que "toutes les" qualités
OriginalL'auteur Jiaaro