Mohsen Amiri
Mohsen Amiri

Reputation: 175

retrieving the last instance of a model in another model's modelSerializer in django rest framework

I am creating rest APIs for a website in which users can purchase one of the provided subscriptions.

In this website there is a user-info API which returns the information about the logged in user which can be used to show their info on the website. The problem is that, the mentioned API's serializer is a modelSerializer on the "User" model and the information that I want to return is the instance of "Subscription" model which the latest instance of "SubPurchase" model refers to.

These are my serializers, models and views.And I need to somehow return the user's current subscription's ID and name along with the user's information. If you have any further questions, ask me in the comments and I'll answer them.

# models.py
class User(AbstractBaseUser, PermissionsMixin):
    userID = models.AutoField(primary_key=True)
    username = models.CharField(max_length=100, unique=True, validators=[RegexValidator(regex="^(?=[a-z0-9._]{5,20}$)(?!.*[_.]{2})[^_.].*[^_.]$")])
    email= models.EmailField(max_length=100, unique=True,  validators=[EmailValidator()])
    name = models.CharField(max_length=100)
    isSuspended = models.BooleanField(default=False)
    isAdmin = models.BooleanField(default=False)
    emailActivation = models.BooleanField(default=False)
    balance = models.IntegerField(default=0)

    objects = UserManager()

    USERNAME_FIELD = 'username'


class Subscription(models.Model):
    subID = models.AutoField(primary_key=True)
    nameOf = models.CharField(max_length=50)
    price = models.PositiveIntegerField()
    salePercentage = models.PositiveIntegerField(default=0)
    saleExpiration = models.DateTimeField(default=datetime.datetime.now, blank=True)

    def __str__(self):
        return f"{self.nameOf}"


class SubPurchase(models.Model):
    price = models.PositiveIntegerField()
    dateOf = models.DateTimeField(auto_now_add=True)
    
    user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
    subscription = models.ForeignKey(Subscription, null=True, on_delete=models.SET_NULL)
    
    def __str__(self):
        return self.subscription
# serializers.py
class UserInfoSerializer(serializers.ModelSerializer):
    class Meta:
        model = get_user_model()
        fields = ('userID', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')
        read_only_fields = ('userID', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')


# views.py
class UserInfoViewSet(viewsets.ModelViewSet):
    queryset = get_user_model().objects.all()
    serializer_class = UserInfoSerializer

    def get_queryset(self):
        uID = getattr(self.request.user,'userID')
        return get_user_model().objects.filter(userID=uID)

    def get_object(self):
        uID = getattr(self.request.user,'userID')
        return self.queryset.filter(userID=uID)

Again, I need to change the UserInfoSerializer in a way that would give me the user's current subscription's name, ID and expiration date which would be 30 days after the purchase date

Upvotes: 1

Views: 427

Answers (2)

rediet yosef
rediet yosef

Reputation: 222

you can use NestedSerializers to achieve what you are looking for

basically, nested serialization is a method in which you can return, create, put..., into a model from another model, it goes like this..

models.py

class User(AbstractBaseUser, PermissionsMixin):
....
#user model data

class SubPurchase(models.Model):
   ...
  user = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE)

serializers.py

class SubscriptionSerializer(serializers.ModelSerializer):
     class Meta:
         model = Subscription
         fields =["anyfield you wanna include"]

class SubPurchaseSerializer(serializers.ModelSerializer):
     class Meta:
         model = SubPurchase
         fields =["anyfield you wanna include"]


class UserInfoSerializer(serializers.ModelSerializer): 
       subpurchace = SubPurchaseSerializer()
       subscription= SubscriptionSerializer() #later included in the fields of this serializer
    class Meta:
        model = get_user_model()
        fields = ('userID','subpurchace', 'subscription', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')
        read_only_fields = ('userID', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')

Upvotes: 1

Jack
Jack

Reputation: 288

If you are only interested in the returned data, you can override the function to_representation of your serializer and create a serializer for your related model. If I understood correctly, the current subscription of your user is the last one (if sorted by "dateOf"). So something like that could do the trick

class SubscriptionSerializer(serializers.ModelSerializer):
     class Meta:
           model = Subscription
           fields = ('nameOf', 'id', 'saleExpiration ')

class UserInfoSerializer(serializers.ModelSerializer):
    class Meta:
        model = get_user_model()
        fields = ('userID', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')
        read_only_fields = ('userID', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')

    def to_representation(self, instance):
         data = super().to_representation(instance)
         current_subs = instance.subpurchase_set.order_by('dateOf').last().subscription
         data['current_subscription'] = SubscriptionSerializer(instance=current_subs).data
         return data


Upvotes: 2

Related Questions