Wie frage ich ein ManyToManyField ordnungsgemäß nach allen Objekten in einer Liste (oder einem anderen ManyToManyField) ab?

StackOverflow https://stackoverflow.com/questions/1841931

Frage

Ich bin ziemlich ratlos darüber, wie ich am besten eine Django-Abfrage erstellen kann, die prüft, ob alle die Elemente von a ManyToMany Feld (oder eine Liste) in einem anderen vorhanden sind ManyToMany Feld.

Als Beispiel habe ich mehrere Persons, die mehr als eine Spezialität haben können.es gibt auch JobEs ist so, dass Leute anfangen können, aber sie brauchen einen oder mehrere Specialtys, um startberechtigt zu sein.

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

Eine Person kann einen Job beginnen nur wenn sie haben alle die Fachkenntnisse, die der Job erfordert.Zur Veranschaulichung: Wir haben drei Spezialgebiete:

  • Codierung
  • Singen
  • Tanzen

Und ich habe eine Job Dafür sind die Spezialgebiete Gesang und Tanz erforderlich.Eine Person mit den Spezialisierungen Gesang und Tanz kann damit beginnen, eine Person mit den Spezialisierungen Programmieren und Gesang jedoch nicht – da für den Job eine Person erforderlich ist, die sowohl singen als auch tanzen kann.

Jetzt brauche ich also eine Möglichkeit, alle Jobs zu finden, die eine Person annehmen kann.Das war meine Art, es anzugehen, aber ich bin mir sicher, dass es einen eleganteren Ansatz gibt:

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

Dies liegt an der Verwendung Job.objects.filter(specialty__in=person.specialties.all()) gibt passende Jobs zurück beliebig der Fachgebiete der Person, nicht alle.Bei Verwendung dieser Abfrage würde der Job, der Singen und Tanzen erfordert, für den singenden Codierer angezeigt, was nicht die gewünschte Ausgabe ist.

Ich hoffe, dass dieses Beispiel nicht zu kompliziert ist.Der Grund, warum ich mir darüber Sorgen mache, ist, dass es wahrscheinlich noch viel mehr Spezialitäten im System geben wird, und es scheint nicht der beste Weg zu sein, dies zu erreichen, indem man sie durchschleift.Ich frage mich, ob irgendjemand diesen Juckreiz lindern könnte!

War es hilfreich?

Lösung

Eine andere Idee

Ok, ich schätze, ich hätte das zu der anderen Antwort hinzufügen sollen, aber als ich damit anfing, schien es, als würde es eine andere Richtung einschlagen, haha

Keine Notwendigkeit zu iterieren:

person_specialties = person.specialties.values_list('pk', flat=True)

non_specialties = Specialties.objects.exclude(pk__in=person_specialties)

jobs = Job.objects.exclude(required_specialties__in=non_specialties)

Notiz: Ich weiß nicht genau, wie schnell das ist.Vielleicht sind Sie mit meinen anderen Vorschlägen besser dran.
Auch:Dieser Code ist ungetestet

Andere Tipps

Ich glaube, Sie bei Verwendung aussehen sollte values_list die Person, die Spezialitäten zu bekommen

Ersetzen Sie:

[s.name for s in person.specialties]

mit:

person.specialties.values_list('name', flat=True)

Das gibt Ihnen eine einfache Liste (dh. [ 'Spec1', 'spec2', ...]), die Sie wieder verwenden können. Und die SQL-Abfrage in der bg verwendet wird auch schneller sein, weil es nur wählt ‚name‘ stattdessen eine select * tun die ORM-Objekte zu füllen

Sie können auch eine Geschwindigkeitsverbesserung durch Filterung Arbeitsplätze erhalten, dass die Person auf jeden Fall nicht durchführen kann:

so ersetzen:

jobs = Job.objects.all()

mit (2 Abfragen - arbeitet für django 1.0 +)

person_specialties = person.specialties.values_list('id', flat=True)
jobs = Job.objects.filter(required_specialties__id__in=person_specialties)

oder mit (1 Abfrage - arbeitet für django1.1 +)

jobs = Job.objects.filter(required_specialties__in=person.specialties.all())

Sie können auch eine Verbesserung durch die Verwendung select_related () erhalten auf Ihre Jobs / Person Anfragen (da sie einen Fremdschlüssel haben, die Sie verwenden)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top