كيفية الاستعلام بشكل صحيح ManyTomanyfield لجميع الكائنات في القائمة (أو آخر manytomanyfield)؟

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

سؤال

أنا أحظت بدلا من أفضل طريقة لبناء استعلام DJANGO الذي يتحقق إذا الكل عناصر ManyToMany الحقل (أو القائمة) موجودة في آخر ManyToMany حقل.

كمثال، لدي العديد من PersonS، الذي يمكن أن يكون أكثر من واحد من تخصص. هناك أيضا Jobيمكن أن يبدأ الأشخاص، لكنهم يحتاجون إلى واحد أو أكثر Specialtyليكون مؤهلا للبدء.

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

يمكن للشخص بدء وظيفة فقط إذا كان لديهم الكل التخصصات التي تتطلب الوظيفة. لذلك، مرة أخرى من أجل المثال، لدينا ثلاثة تخصصات:

  • ترميز
  • الغناء
  • رقص

ولدي Job وهذا يتطلب التخصصات الغناء والرقص. يمكن لأي شخص ذو تجميلات الغناء والرقص أن يبدأه، لكن آخر مع تخصصات الترميز والغناء لا يمكن - حيث تتطلب الوظيفة شخصا يستطيع كل من الغناء والرقص.

لذلك، الآن أحتاج إلى طريقة للعثور على جميع الوظائف التي يمكن للشخص القيام بها. كانت هذه طريقي لمعالجة ذلك، لكنني متأكد من وجود نهج أكثر أناقة:

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

هذا بسبب استخدام Job.objects.filter(specialty__in=person.specialties.all()) سوف تعيد الوظائف التي تطابق أي من تخصصات الشخص، وليس كلهم. باستخدام هذا الاستعلام، ستظهر المهمة التي تتطلب الغناء والرقص من أجل الرموز الغناء، وهذا ليس الإخراج المطلوب.

آمل أن لا يكون هذا المثال غير متوافق للغاية. السبب في أنني أشعر بالقلق إزاء ذلك هو أن التخصصات في النظام ستكون أكثر من ذلك بكثير، ويبدو أن الحلقات عليهم لا يبدو أن أفضل طريقة لتحقيق ذلك. أنا أتساءل ما إذا كان يمكن لأي شخص أن يقرض خدش هذا الحث!

هل كانت مفيدة؟

المحلول

فكرة اخرى

حسنا، أعتقد أنني يجب أن أضاف هذا إلى الإجابة الأخرى، ولكن عندما بدأت عليه، يبدو أنه سيكون اتجاه مختلف هاها

لا حاجة للتكرار:

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)

ملاحظة: أنا لا أعرف بالضبط مدى سرعة هذا. قد تكون أفضل حالا مع اقتراحاتي الأخرى.
أيضا: هذا الرمز غير مهتم

نصائح أخرى

أعتقد أنك يجب أن ننظر في استخدام القيم_List. للحصول على تخصصات الشخص

يستبدل:

[s.name for s in person.specialties]

مع:

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

من شأنها أن تعطيك قائمة عادي (أي. ['spec1'، 'spec2'، ...]) والتي يمكنك استخدامها مرة أخرى. وسوف يكون استعلام SQL المستخدم في BG أسرع لأنه سيحدد "الاسم" فقط بدلا من القيام select * لتعبئة كائنات orm

قد تحصل أيضا على تحسن سرعة عن طريق تصفية الوظائف التي لا يمكن أن يؤديها الشخص بالتأكيد:

لذلك استبدل:

jobs = Job.objects.all()

مع (2 استفسار - Works for django 1.0+)

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

أو مع (1 استفسار؟ - يعمل ل django1.1 +)

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

قد تحصل أيضا على تحسن باستخدام select_related () في استفسارات وظائفك / شخص (نظرا لأن لديهم مفتاح أجنبي تستخدمه)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top