Django: Duplicated logic between properties and queryset annotations

后端 未结 5 968
我寻月下人不归
我寻月下人不归 2020-12-31 10:48

When I want to define my business logic, I\'m struggling finding the right way to do this, because I often both need a property AND a custom queryset to get the same info. I

5条回答
  •  爱一瞬间的悲伤
    2020-12-31 11:30

    I don't think there is a silver bullet here. But I use this pattern in my projects for such cases.

    class PickupTimeSlotAnnotatedManager(models.Manager):
        def with_nb_bookings(self):
            return self.annotate(
                _nb_bookings=Count(
                    'order', filter=Q(order__status=Order.VALIDATED)
                )
            )
    
    class PickupTimeSlot(models.Model):
        ...
        annotated = PickupTimeSlotAnnotatedManager()
    
        @property
        def nb_bookings(self) -> int:
            """ How many times this time slot is booked? """ 
            if hasattr(self, '_nb_bookings'):
                return self._nb_bookings
            return self.order_set.validated().count()
    

    In code

    qs = PickupTimeSlot.annotated.with_nb_bookings()
    for item in qs:
        print(item.nb_bookings)
    

    This way I can always use property, if it is part of annotated queryset it will use annotated value if not it will calculate it. This approach guaranties that I will have full control of when to make queryset "heavier" by annotating it with required values. If I don't need this I just use regular PickupTimeSlot.objects. ...

    Also if there are many such properties you could write decorator that will wrap property and simplify code. It will work as cached_property decorator, but instead it will use annotated value if it is present.

提交回复
热议问题