Переопределение Django RelatedManager и Queryset для фильтрации по полю в сквозной модели - PullRequest
2 голосов
/ 09 марта 2020

У меня есть две модели, которые связаны через поле «многие ко многим» через третью сквозную модель. Сквозная модель имеет несколько дополнительных полей. Я хочу переопределить RelatedManager для создания некоторых пользовательских QuerySets, которые используют дополнительные поля в сквозной модели. Предостережение заключается в том, что моя сквозная модель создается с использованием подкласса / миксина, который определяет дополнительные поля. Я хочу получить все объекты Gene, связанные с SampleBatch, которые имеют активный статус 1 в экземплярах SampleBatchGene, связывающих поля «многие ко многим»:

In [1]: SampleBatch.objects.get(sample__lab_number="18_11998").genes.active()                                                                                                        
Out[1]: <QuerySet [<Gene: BAP1>]>

Вот что У меня так далеко:

class SampleBatch(models.Model):
    sample = models.ForeignKey(Sample, on_delete=models.CASCADE)
    batch = models.ForeignKey(Batch, on_delete=models.CASCADE)
    genes = models.ManyToManyField(
        "Gene", through='SampleBatchGene', related_name='samplebatches',
        blank=True, through_fields=('samplebatch', 'gene')
    )

    class Meta:
        managed = True
        db_table = 'sample_batch'
        unique_together = (('sample', 'batch'),)
        verbose_name_plural = "Sample batches"

    def __str__(self):
        return '{}-{}'.format(self.sample, self.batch)


class Gene(models.Model):
    gene_id = models.BigAutoField(primary_key=True)
    gene_name = models.CharField(unique=True, max_length=255, db_index=True)
    hgnc_number = models.IntegerField(unique=True, db_index=True)

    class Meta:
        managed = True
        db_table = 'gene'

    def __str__(self):
        return self.gene_name


class ActiveQuerySet(models.QuerySet):
    def active(self):
        return self.filter(active=1)

    def inactive(self):
        return self.filter(active=0)

class LinkMixin(models.Model):
    ACTIVE_CHOICES = (
        (0, 'Inactive'),
        (1, 'Active')
    )

    activated = models.DateTimeField(default=timezone.now)
    active = models.IntegerField(
        choices=ACTIVE_CHOICES, default=1, db_index=True
    )
    created = models.DateTimeField(auto_now_add=True)
    deactivated = models.DateTimeField(blank=True, null=True, default=None)
    objects = models.Manager()
    statuses = ActiveQuerySet.as_manager()

    class Meta:
        abstract = True


class SampleBatchGene(LinkMixin):
    samplebatch = models.ForeignKey(SampleBatch, on_delete=models.CASCADE)
    gene = models.ForeignKey(Gene, on_delete=models.PROTECT)

    class Meta:
        managed = True
        db_table = 'sample_batch_gene'
        unique_together = (('samplebatch', 'gene'), )

    def __str__(self):
        return '{}-{}'.format(self.samplebatch, self.gene)

Попытка использовать пользовательский обратный менеджер ( в соответствии с документами ) выдает следующую ошибку:

In [5]: SampleBatch.objects.get(sample__lab_number="18_11998").genes.all()                                                                                                                                    
Out[5]: <QuerySet [<Gene: BAP1>]>

In [6]: SampleBatch.objects.get(sample__lab_number="18_11998").genes.filter(samplebatchgene__active=1)                                                                                                        
Out[6]: <QuerySet [<Gene: BAP1>]>

In [7]: SampleBatch.objects.get(sample__lab_number="18_11998").genes(manager="statuses").active()                                                                                                             
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-6-d8d95f5854b4> in <module>
----> 1 SampleBatch.objects.get(sample__lab_number="18_11998").genes(manager="statuses").active()

~/.pyenv/versions/3.6.6/envs/varDB/lib/python3.6/site-packages/django/db/models/fields/related_descriptors.py in __call__(self, manager)
    848 
    849         def __call__(self, *, manager):
--> 850             manager = getattr(self.model, manager)
    851             manager_class = create_forward_many_to_many_manager(manager.__class__, rel, reverse)
    852             return manager_class(instance=self.instance)

AttributeError: type object 'Gene' has no attribute 'statuses'

РЕДАКТИРОВАТЬ: Запрошено Виллем Ван Онсем: он пытается использовать собственный менеджер из Gene, но я определил его для SampleBatchGene. Поэтому мне нужно разобраться, как указать собственный менеджер из сквозной модели. Я надеялся сделать это с помощью LinkMixin, поэтому мне не пришлось бы определять пользовательский менеджер для обеих моделей на обоих концах Many-To-Many. Я думаю, я мог бы прикрепить пользовательский менеджер на SampleBatch и Gene и заставить его работать, но у меня есть много такого типа отношений, которые используют LinkMixin в качестве сквозной модели, поэтому надеялся избежать этого каким-то образом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...