Search code examples
pythondjangodjango-modelsdjango-annotate

django model method to return queryset with annotate


i am trying to define a field which being calculated based on user_type, I did the following method but its not working, if anyone can advise me to the correct approach.

so what i am trying to achieve here is to construct a method such as <user>.book_limit to return the maximum number of allowed books based on user_type

#models.py 

class UserProfile(models.Model):
    class UserType(models.TextChoices):
        FREE = 'FR', 'FREE'
        BASIC = 'BS', 'BASIC'
        PREMIUM = 'PR', 'PREMIUM'

    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
    bio = models.CharField(max_length=255)
    created_on = models.DateTimeField(auto_now_add=True)
    last_updated = models.DateTimeField(auto_now=True)
    user_type = models.CharField(max_length=2, choices=UserType.choices, default=UserType.FREE)

    def __str__(self):
        return self.user.username

    @property
    def borrow_limit(self):
        return UserProfile.objects.annotate(book_limit=Case(
            When(user_type=UserProfile.UserType.FREE, then=Value('1')),
            When(user_type=UserProfile.UserType.BASIC, then=Value('2')),
            When(user_type=UserProfile.UserType.PREMIUM, then=Value('5')),
            default=Value('1'), output_field=IntegerField))

Solution

  • You can use a manager to each time annotate the value:

    from django.db.models import Case, IntegerField, Value, When
    
    class UserProfileManager(models.Manager):
    
        def get_queryset(self, *args, **kwargs):
            return super().get_queryset(*args, **kwargs).annotate(
                book_limit=Case(
                    When(user_type=UserProfile.UserType.FREE, then=Value(1)),
                    When(user_type=UserProfile.UserType.BASIC, then=Value(2)),
                    When(user_type=UserProfile.UserType.PREMIUM, then=Value(5)),
                    default=Value(1),
                    output_field=IntegerField()
                )
            )
    
    class UserProfile(models.Model):
        class UserType(models.TextChoices):
            FREE = 'FR', 'FREE'
            BASIC = 'BS', 'BASIC'
            PREMIUM = 'PR', 'PREMIUM'
    
        user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
        bio = models.CharField(max_length=255)
        created_on = models.DateTimeField(auto_now_add=True)
        last_updated = models.DateTimeField(auto_now=True)
        user_type = models.CharField(max_length=2, choices=UserType.choices, default=UserType.FREE)
        objects = UserProfileManager()