Выбор конкретного менеджера в смежной области для модели
У меня возникла ситуация, когда мне нужно использовать пользовательский менеджер для связанного поля, который отличается от 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 с моделью заставит эту модель использовать полный менеджер для поля внешнего ключа, независимо от того, какой менеджер связан по умолчанию.