Выбор конкретного менеджера в смежной области для модели

У меня возникла ситуация, когда мне нужно использовать пользовательский менеджер для связанного поля, который отличается от base_manager для связанных полей. Допустим, у меня есть модель Item, которая находится в отношении один ко многим с другой моделью Purchase. Теперь, Item имеет 2 менеджера:

objects = ItemManager() #which by default excludes the out_of_stock=True items
complete = CompleteItemManager() #which gives all the items.

Для всех других моделей, связанных с Item, по умолчанию используется ItemManager. Но в Purchase я бы хотел использовать CompleteItemManager для связанного элемента.

Допустим, когда-то была совершена покупка товара, который сейчас имеет номер out_of_stock=True, и у нас есть только id этой покупки, скажем old_purchase_id, теперь, если попытаться выполнить следующий запрос:

purchase = Purchase.objects.filter(id=old_purchase_id) # gives error

Выдаст ошибку типа "Item matching query does not exist", поскольку менеджер, используемый для связанных товаров, является ItemManager, который исключает такие товары, отсутствующие на складе.

Что можно сделать в этом случае? В идеале я хотел бы переопределить менеджер, который будет использоваться для данного связанного поля в каждой модели, так что даже если все остальные модели используют ItemManager для разрешения полей элементов, модель Purchase все равно использует CompleteItemManager для своих отношений с Item.

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

purchase.item_set.all() # Uses ItemManager
purchase.item_set(manager='complete').all() # Uses CompleteItemManager

Вот как я это сделал.

Я сделал CustomForwardManyToOneDescriptor, который просто выбирал менеджер с именем complete для связанной модели. И использовал этот дескриптор в CustomForeignKey в качестве forward_related_accessor_class.

class CustomForwardManyToOneDescriptor(ForwardManyToOneDescriptor):
    def get_queryset(self, **hints):
        related_model = self.field.remote_field.model

        return related_model.complete.db_manager(hints=hints).all()


class CustomForeignKey(models.ForeignKey):
    forward_related_accessor_class = CustomForwardManyToOneDescriptor

Теперь использование CustomForeignKey вместо ForeignKey для создания отношения OneToMany с моделью заставит эту модель использовать полный менеджер для поля внешнего ключа, независимо от того, какой менеджер связан по умолчанию.

Вернуться на верх