python – 复杂的Django查询

我的大杂烩应用程序的查询超出了我对Django的ORM如何工作的了解.

这是我当前(不正确)的尝试:

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

如果您想阅读它们,我的模型如下.

初始filter()工作正常.我的问题是将exclude()链接到最后.

exclude()似乎排除了Mentors:

>符合指定条件的关联Shift
(shift__session = session),
>和一个(可能是不同的)相关联的Shift符合第二组标准shift__jobs__time = job.time.

我只想过滤出与其相关联的Shift的导师符合标准.

有任何想法吗?

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)

boolean algebra中,¬(A∧B)(不是[A和B])实际上是(¬A∨¬B)(不是[A] OR而不是[B]).
因此,您在查询中的意思是:

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

编写具有多个参数的excludefilter时,请记住这一点.

因此,如果在您的查询中,您想要排除检查BOTH标准的元素(如果您愿意,那么标准的交集),最简单和最好的方法是链排除过滤器:

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

查询集操作是惰性的,大致意味着您的排除过滤器将同时进行评估.因此,两个过滤器不会是“工作的两倍”.

过滤器将转换为:

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

这正是你想要的!

编写查询有时很难,但请记住:

>排除多个args转换为:not(A)OR not(B)OR not(C)……
>如果您需要排除多个因子(AND)的项目,只需多次调用排除过滤器即可.

现在,这是您的新查询:

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])是空集.所以问题在于查询的语义.

从你的帖子我读到:

excluding […] an associated Shift which meets the conditions specified (shift__session = session) AND a (possibly different) associated Shift that meets the second set of criteria

您使用的过滤器已经保证shift__session = session,因此您不应将其放在排除过滤器中.

根据我的猜测(但告诉我,如果我错了),你想要的是:

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