在Django REST Framework中扩展User ModelSerializer以返回用户最新订阅信息
Alright, let's tackle this problem step by step. You need to extend your UserInfoSerializer to include details from the user's latest SubPurchase and its associated Subscription—specifically the subscription ID, name, and the expiry date (30 days after the purchase date). Here's how you can do it cleanly:
Step 1: Update the UserInfoSerializer
Modify your serializers.py to add custom fields using SerializerMethodField, which lets you define custom logic for fetching and calculating the required data:
from rest_framework import serializers from django.utils import timezone from datetime import timedelta from .models import get_user_model, SubPurchase class UserInfoSerializer(serializers.ModelSerializer): # Custom fields for subscription details current_subscription_id = serializers.SerializerMethodField() current_subscription_name = serializers.SerializerMethodField() subscription_expiry_date = serializers.SerializerMethodField() class Meta: model = get_user_model() fields = ( 'userID', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended', 'current_subscription_id', 'current_subscription_name', 'subscription_expiry_date' ) read_only_fields = fields # All fields are read-only def get_current_subscription_id(self, obj): # Get the latest SubPurchase for the user latest_purchase = SubPurchase.objects.filter(user=obj).order_by('-dateOf').first() if latest_purchase and latest_purchase.subscription: return latest_purchase.subscription.subID return None def get_current_subscription_name(self, obj): latest_purchase = SubPurchase.objects.filter(user=obj).order_by('-dateOf').first() if latest_purchase and latest_purchase.subscription: return latest_purchase.subscription.nameOf return None def get_subscription_expiry_date(self, obj): latest_purchase = SubPurchase.objects.filter(user=obj).order_by('-dateOf').first() if latest_purchase: # Calculate expiry: purchase date + 30 days expiry_date = latest_purchase.dateOf + timedelta(days=30) # Return in ISO format (consistent with Django's datetime serialization) return timezone.localtime(expiry_date).isoformat() return None
Key Notes:
- Fetching the latest subscription: We use
order_by('-dateOf').first()to get the most recentSubPurchasefor the user. This ensures we're always pulling the active/latest subscription. - Handling empty states: We check if
latest_purchaseand its associatedsubscriptionexist before accessing their attributes. This prevents errors if the user has never made a subscription purchase. - Timezone awareness: Using
timezone.localtime()ensures the expiry date is adjusted to your app's timezone (if you haveUSE_TZ = Trueinsettings.py), which is best practice for datetime handling. - Read-only fields: We set
read_only_fields = fieldssince all data here is for display purposes and shouldn't be modified via this API.
Step 2: Verify the View
Your existing UserInfoViewSet doesn't need any changes—since we've added the custom fields directly to the serializer, the view will automatically include them in the response when fetching the user's data.
Example Response
When a user has an active subscription, your API response will look like this:
{ "userID": 1, "username": "johndoe", "email": "john@example.com", "name": "John Doe", "balance": 500, "emailActivation": true, "isSuspended": false, "current_subscription_id": 2, "current_subscription_name": "Premium Plan", "subscription_expiry_date": "2024-05-20T14:30:00+02:00" }
If the user has no subscriptions, those three fields will return null.
内容的提问来源于stack exchange,提问作者Mohsen Amiri

