簡體   English   中英

復雜的Django查詢

[英]Complicated Django Query

我的大雜燴應用程序的查詢超出了我對Django的ORM如何工作的了解。

這是我當前(不正確)的嘗試:

queryset = Mentor.objects.filter(
    shift__session = session,
    jobs_desired = job
).exclude(
    shift__session = session,
    shift__jobs__time = job.time
)

如果您想閱讀它們,我的模型如下。

初始filter()工作正常。 我的問題是將exclude()鏈接到最后。

exclude()好像用以下方法排除了Mentor

  • 一個滿足指定條件的相關Shiftshift__session = session ),
  • 和一個(可能是不同的)相關聯的Shift符合第二組標准shift__jobs__time = job.time

我只想過濾出與其相關聯的Shift Mentor ,它符合標准。

有任何想法嗎?

class DojoSession(models.Model):
    term = models.ForeignKey(DojoTerm, help_text = "Dojo Term")
    date = models.DateField(
        blank = False,
        help_text = "Date during which the session will take place."
    )

    start = models.TimeField(
        blank = False,
        help_text = "Start Time"
    )

    end = models.TimeField(
        blank = False,
        help_text = "End Time"
    )

    rooms = models.ManyToManyField(
        Room,
        blank = True,
        help_text = "The rooms in which this session will be running."
    )

class Shift(models.Model):
    mentor = models.ForeignKey(
        'mentors.Mentor',
        blank = False,
        help_text = 'The mentor unergoing this shift.'
    )

    session = models.ForeignKey(
        DojoSession,
        blank = False,
        help_text = 'The session during which this shift takes place.',
    )

    role = models.ForeignKey(
        'mentors.Role',
        blank = False,
        help_text = "The role that the mentor will be undertaking during this shift.",
    )

    room = models.ForeignKey(
        Room,
        blank = True,
        null = True,
        help_text = "The room, if any, that the mentor will be undertaking the shift in."
    )

    jobs = models.ManyToManyField(
        'jobs.Job',
        blank = True,
        null = True,
    )

    start = models.TimeField(
        blank = False,
        help_text = "Start Time"
    )

    end = models.TimeField(
        blank = False,
        help_text = "End Time"
    )

class Job(models.Model):
    BEFORE = 'B'
    DURING = 'D'
    AFTER = 'A'

    TIME_CHOICES = (
        (BEFORE, 'Before session'),
        (DURING, 'During session'),
        (AFTER, 'After session'),
    )

    name = models.CharField(
        max_length = 50,
        help_text = "The job's name."
    )

    description = models.TextField(
        max_length = 1024,
        help_text = "A description of the job."
    )

    location = models.CharField(
        max_length = 50,
        help_text = "The job's location."
    )

    time = models.CharField(
        max_length = 1,
        choices = TIME_CHOICES,
        help_text = "The time during a session at which this job can be carried out."
    )

class Mentor(models.Model):
    MALE_SMALL = "MS"
    MALE_MEDIUM = "MM"
    MALE_LARGE = "ML"
    MALE_EXTRA_LARGE = "MXL"

    FEMALE_EXTRA_SMALL = "FXS"
    FEMALE_SMALL = "FS"
    FEMALE_MEDIUM = "FM"
    FEMALE_LARGE = "FL"
    FEMALE_EXTRA_LARGE = "FXL"

    SHIRT_SIZE_CHOICES = (
        ('Male', (
            (MALE_SMALL, "Male S"),
            (MALE_MEDIUM, "Male M"),
            (MALE_LARGE, "Male L"),
            (MALE_EXTRA_LARGE, "Male XL")
        )),
        ('Female', (
            (FEMALE_EXTRA_SMALL, "Female XS"),
            (FEMALE_SMALL, "Female S"),
            (FEMALE_MEDIUM, "Female M"),
            (FEMALE_LARGE, "Female L"),
            (FEMALE_EXTRA_LARGE, "Female XL")
        ))
    )

    ASSOCIATE = 'A'
    STAFF = 'S'
    NEITHER = 'N'

    CURTIN_STATUS_CHOICES = (
        (ASSOCIATE, 'Associate'),
        (STAFF, 'Staff'),
        (NEITHER, 'Neither/not sure')
    )

    NOTHING = 'NO'
    SOMETHING = 'SO'
    EVERYTHING = 'EV'

    KNOWLEDGE_CHOICES = (
        (NOTHING, 'I know nothing but am keen to learn!'),
        (SOMETHING, 'I know some basics'),
        (EVERYTHING, 'I know a great deal')
    )

    uni = models.CharField(
        max_length = 50,
        null = True,
        blank = True,
        help_text = "University of study"
    )

    uni_study = models.CharField(
        max_length = 256,
        null = True,
        blank = True,
        help_text = "If you're attending university, what are you studying?"
    )

    work = models.CharField(
        max_length = 256,
        null = True,
        blank = True,
        help_text = "If you workwhat do you do?"
    )

    shirt_size = models.CharField(
        max_length = 3,
        blank = True,
        choices = SHIRT_SIZE_CHOICES,
        help_text = "T-shirt size (for uniform)"
    )

    needs_shirt = models.BooleanField(
        default = True,
        help_text = "Does the mentor need to have a shirt provisioned for them?"
    )

    wwcc = models.CharField(
        max_length = 10,
        verbose_name = "WWCC card number",
        blank = True,
        null = True,
        help_text = "WWCC card number (if WWCC card holder)"
    )

    wwcc_receipt = models.CharField(
        max_length = 15,
        verbose_name = "WWCC receipt number",
        blank = True,
        null = True,
        help_text = "WWCC receipt number (if WWCC is processing)"
    )

    curtin_status = models.CharField(
        max_length = 1,
        verbose_name = "Current Curtin HR status",
        choices = CURTIN_STATUS_CHOICES,
        default = NEITHER,
        blank = False,
        help_text = "When possible, we recommend that all CoderDojo mentors are either Curtin University Associates or Staff members."
    )

    curtin_id = models.CharField(
        max_length = 10,
        verbose_name = "Curtin Staff/Associate ID",
        blank = True,
        null = True,
        help_text = "Your Curtin Staff/Associate ID (if applicable)"
    )

    coding_experience = models.CharField(
        max_length = 2,
        blank = False,
        default = NOTHING,
        choices = KNOWLEDGE_CHOICES,
        help_text = "How much programming experience do you have?"
    )

    children_experience = models.CharField(
        max_length = 2,
        blank = False,
        default = NOTHING,
        choices = KNOWLEDGE_CHOICES,
        help_text = "How much experience do you have with children?"
    )

    roles_desired = models.ManyToManyField(Role)

    jobs_desired = models.ManyToManyField('jobs.Job')

    shift_availabilities = models.ManyToManyField(
        'planner.DojoSession',
        help_text = "When are you available?"
    )

    user = models.OneToOneField(settings.AUTH_USER_MODEL,
        unique = True
    )

首先,我們來解釋一下這里發生了什么。 當你寫:

set.exclude( A=arg1, B=arg2 )

這轉換為以下查詢:

SELECT [...] WHERE NOT (A=arg1 AND B=arg2)

布爾代數中 ,¬(A∧B) (不是[A和B])實際上是(¬A∨¬B) (不是[A] OR而不是[B]) 因此,您在查詢中的意思是:

SELECT [...] WHERE NOT(A=arg1) OR NOT(B=arg2)

編寫具有多個參數的exclude過濾器時請記住這一點。

因此,如果在您的查詢中,您想要排除檢查BOTH標准的元素(如果您願意,那么標准的交集 ),最簡單和最好的方法是鏈排除過濾器

set.exclude(A=arg1).exclude(B=arg2)

查詢集操作是惰性的 ,大致意味着您的exclude過濾器將同時進行評估。 因此,兩個過濾器不會是“工作的兩倍”。

過濾器將轉換為:

SELECT [...] WHERE NOT(A=arg1) AND NOT(B=arg2)

這正是你想要的!

編寫查詢有時很難,但請記住:

  • 排除多個args轉換為:not(A)OR not(B)OR not(C)......
  • 如果您需要排除多個因子(AND)的項目,只需多次調用exclude過濾器。

現在,這是您的新查詢:

queryset = Mentor.objects.filter(
    shift__session = session,
    jobs_desired = job
).exclude(
    shift__session = session
).exclude(
    shift__jobs__time = job.time
)

如果我們“扁平化”您的要求,您需要:

  • 屬於會話的記錄: filter(shift__session = session)
  • 但也...... 屬於那個會話.exclude(shift__session = session)

生成的SQL將是:

SELECT [...] WHERE shift__session = session AND [...] AND NOT(shift__session = session)

但是A∧¬A( A AND NOT [A] )是空集。 所以問題在於查詢的語義

從你的帖子我讀到:

排除符合指定條件的相關Shift(shift__session = session)和符合第二組標准的(可能不同的)相關Shift

您使用的filter已經保證shift__session = session ,因此您不應將其放在exclude過濾器中。

根據我的猜測(但告訴我,如果我錯了),你想要的是:

queryset = Mentor.objects.filter(
    shift__session = session,
    jobs_desired = job
).exclude(
    shift__jobs__time = job.time
)

我相信你應該能夠使用這樣的東西來獲得你想要的東西。

shifts = Shift.objects.filter(session=session)
excluded_shifts = shifts.filter(jobs__time=job.time)
queryset = Mentor.objects.filter(
    jobs_desired=job
    shift__in=shifts
).exclude(
    shift__in=excluded_shifts
)

不要擔心在運行查詢之前執行shiftsexcluded_shifts ; 它們是懶惰的,只會作為子查詢包含在您正在創建的最終查詢集中。

在偽sql中,我認為以上內容將對應於以下內容(這里僅僅是過去的經驗):

SELECT *
FROM mentor
LEFT JOIN jobs_desired ON (mentor.id=jobs_desired.mentor_id)
WHERE jobs_desired.id=1
AND shift_id IN (
    SELECT id
    FROM shifts
    WHERE session_id=2
)
AND shift_id NOT IN (
    SELECT id
    FROM shifts
    LEFT JOIN jobs ON (shifts.id=jobs.session_id)
    WHERE session_id=2
    AND jobs.time='B'
)

正如您可能已經注意到的那樣,這兩個子查詢中的DB正在執行一些雙重工作,但我認為沒有辦法避免這種情況。

如何使用Q函數

from django.db.models import Q
queryset = Mentor.objects.exclude(
        Q(shift__jobs__time=job.time) & Q(shift__session=session)
    ).filter(jobs_desired=job, shift__session=session)
print str(queryset.query)

這產生SQL如:

SELECT "your_project_mentor"."id", "your_project_mentor"."uni", "your_project_mentor"."uni_study", "your_project_mentor"."work", "your_project_mentor"."shirt_size", "your_project_mentor"."needs_shirt", "your_project_mentor"."wwcc", "your_project_mentor"."wwcc_receipt", "your_project_mentor"."curtin_status", "your_project_mentor"."curtin_id", "your_project_mentor"."coding_experience", "your_project_mentor"."children_experience", "your_project_mentor"."user_id" 
  FROM "your_project_mentor" 
  INNER JOIN "your_project_mentor_jobs_desired" 
    ON ( "your_project_mentor"."id" = "your_project_mentor_jobs_desired"."mentor_id" ) 
  INNER JOIN "your_project_shift" 
    ON ( "your_project_mentor"."id" = "your_project_shift"."mentor_id" ) 
  WHERE 
    (NOT 
      ("your_project_mentor"."id" IN 
        (SELECT U1."mentor_id" 
          FROM "your_project_shift" U1 
          INNER JOIN "your_project_shift_jobs" U2 
            ON ( U1."id" = U2."shift_id" ) 
          INNER JOIN "your_project_job" U3 
            ON ( U2."job_id" = U3."id" ) 
          WHERE U3."time" = <job_time> ) 
      AND "your_project_mentor"."id" IN 
        (SELECT U1."mentor_id" 
          FROM "your_project_shift" U1 
          WHERE U1."session_id" = <session_id> )
      ) 
    AND "your_project_mentor_jobs_desired"."job_id" = <job_id>  
    AND "your_project_shift"."session_id" = <session_id> )

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM