Фильтр Django только для агрегатов/аннотаций

Я пытаюсь создать довольно сложный запрос Django, и у меня нет большого прогресса. Я надеялся, что какой-нибудь волшебник сможет мне помочь?

У меня есть следующие модели:

class Person(models.Model):
    MALE = "M"
    FEMALE = "F"
    OTHER = "O"
    UNKNOWN = "U"
    GENDER_CHOICES = (
            (MALE, "Male"),
            (FEMALE, "Female"),
            (UNKNOWN, "Other"),
    )


    firstName       = models.CharField(max_length=200, null=True, db_column="firstname")
    lastName        = models.CharField(max_length=200, null=True, db_column="lastname")

    gender          = models.CharField(max_length=1, choices=GENDER_CHOICES, default=UNKNOWN, null=True)
    dateOfBirth     = models.DateField(null=True, db_column="dateofbirth")
    dateInService   = models.DateField(null=True, db_column="dateinservice")
    photo           = models.ImageField(upload_to='person_photos', null=True) 

class SuccessionTerm(models.Model):
    originalName    = models.CharField(max_length=200, null=True, db_column="originalname")
    description     = models.CharField(max_length=200, blank=True, null=True)
    score           = models.IntegerField()

class Succession(model.Model):
    position    = models.ForeignKey(Position, to_field='positionId', db_column="position_id")
    employee    = models.ForeignKey(Employee, to_field='employeeId', db_column="employee_id")
    term        = models.ForeignKey(SuccessionTerm)

class Position(models.Model):
    positionId      = models.CharField(max_length=200, unique=True, db_column="positionid")
    title           = models.CharField(max_length=200, null=True)
    # There cannot be a DB constraint, as that would make it impossible to add the first position.
    dottedLine      = models.ForeignKey("Position", to_field='positionId', related_name="Dotted Line",
                                        null=True, db_constraint=False, db_column="dottedline_id")
    solidLine       = models.ForeignKey("Position", to_field='positionId', related_name="SolidLine", 
                                        null=True, db_constraint=False, db_column="solidline_id")
    grade           = models.ForeignKey(Grade)
    businessUnit    = models.ForeignKey(BusinessUnit, null=True, db_column="businessunit_id")
    functionalArea  = models.ForeignKey(FunctionalArea, db_column="functionalarea_id")
    location        = models.ForeignKey(Location, db_column="location_id")

class Employee(models.Model):
    person                = models.OneToOneField(Person, db_column="person_id")
    fte                   = models.IntegerField(default=100)
    dataSource            = models.ForeignKey(DataSource, db_column="datasource_id")
    talentStatus          = models.ForeignKey(TalentStatus, db_column="talentstatus_id")
    retentionRisk         = models.ForeignKey(RetentionRisk, db_column="retentionrisk_id")
    retentionRiskReason   = models.ForeignKey(RetentionRiskReason, db_column="retentionriskreason_id")
    performanceStatus     = models.ForeignKey(PerformanceStatus, db_column="performancestatus_id")
    potential             = models.ForeignKey(Potential, db_column="potential_id")
    mobility              = models.ForeignKey(Mobility, db_column="mobility_id")
    currency              = models.ForeignKey(Currency, null=True, db_column="currency_id")
    grade                 = models.ForeignKey(Grade, db_column="grade_id")
    position              = models.OneToOneField(Position, to_field='positionId', null=True, 
                                                 blank=True, db_column="position_id")
    employeeId            = models.CharField(max_length=200, unique=True, db_column="employeeid")
    dateInPosition        = models.DateField(null=True, db_column="dateinposition")

Теперь я хочу, чтобы каждый сотрудник получил название должности, имя человека и для каждого срока преемственности (которых три), сколько раз должность этого сотрудника находится в таблице преемственности, и количество раз каждый из этих сотрудников встречается в таблице преемников. Прежде всего, я хочу сделать все это в одном запросе (или, точнее, в одном операторе Django ORM), поскольку я делаю это с разбивкой на страницы, но я хочу иметь возможность упорядочивать результат на любом из эти колонки!

Пока что у меня это:

emps = Employee.objects.all()
       .annotate(ls_st=Count('succession__term'))
       .filter(succession__term__description="ShortTerm")
       .order_by(ls_st)
       .prefetch_related('person', 'position')[lower_limit:upper_limit]

Это только один из терминов преемственности, и я хотел бы распространить его на все термины, добавив дополнительные вызовы аннотации. Моя проблема в том, что вызов фильтра работает со всем запросом. Я хотел бы фильтровать только вызов Count.

Я пытался сделать что-то вроде Count(succession__term__description'="ShortTerm"), но это не работает. Есть ли другой способ сделать это?

Заранее большое спасибо, С уважением,

Линус


person Linus    schedule 26.11.2014    source источник


Ответы (1)


Итак, что вам нужно, это подсчет каждого типа термина последовательности__? Это довольно сложно, и я не думаю, что вы можете сделать это со встроенным django orm прямо сейчас. (если вы не сделали запрос .extra())

Я полагаю, что в django 1.8 вы сможете сделать это с помощью новых выражений запросов (https://docs.djangoproject.com/en/dev/releases/1.8/#query-expressions). Но, конечно, версия 1.8 еще не выпущена, так что это вам не поможет.

А пока вы можете использовать очень удобный пакет django-aggregate-if. (https://github.com/henriquebastos/django-aggregate-if/, https://pypi.python.org/pypi/django-aggregate-if )

С django-aggregate-if ваш запрос может выглядеть так:

emps = Employee.objects.annotate(
        ls_st=Count('succession__term', only=Q(succession__term__description="ShortTerm")),
        ls_lt=Count('succession__term', only=Q(succession__term__description="LongTerm")), # whatever your other term descriptions are.
        ls_ot=Count('succession__term', only=Q(succession__term__description="OtherTerm"))
    )
    .order_by('ls_st')
    .prefetch_related('person', 'position')[lower_limit:upper_limit]

Отказ от ответственности: я никогда не использовал django-aggregate-if, поэтому я не совсем уверен, что это сработает, но, судя по README, похоже, что так и должно быть.

person jproffitt    schedule 26.11.2014