You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django REST Framework中修改Profile的Status外键关联?

How to Update the Status ForeignKey in Profile Model with Django REST Framework

Hey there, let's walk through fixing your issue with updating the Status ForeignKey in your Profile model, and also talk about the right way to structure your routes and views.

First, Let's Fix the Issues in Your Current Code

1. Critical Mistakes in Your UserStatusView

Looking at your partial_update method, several errors are blocking it from working:

  • Typo: new_stauts should be new_status (you misspelled "status")
  • Incorrect request data access: request.data.status isn't valid—request.data is a dictionary, so you need to use request.data.get('status_id') or request.data.get('status') to fetch the new status ID
  • Serializer misuse: You initialized ProfileSerializer with the existing user_profile but didn't pass any new update data. Even if you did, your nested StatusSerializer is read-only by default, so it can't handle writing the foreign key.
  • Missing error handling: You don't catch cases where the User or Status doesn't exist, which will throw unhelpful 500 errors instead of proper 404 responses.

2. Serializer Limitation

Your ProfileSerializer uses nested StatusSerializer for the status field. By default, nested serializers are read-only. To support updating the foreign key, you need to adjust this setup.

Let's go through a clean, working approach step by step.

Option 1: Update via Existing User/Profile Endpoint (Most RESTful)

If you already have a UserViewSet (or can extend it), this is the simplest approach.

Step 1: Modify ProfileSerializer to Support Writes

Update the serializer to accept a primary key for status during writes, while still returning nested data for reads:

class ProfileSerializer(serializers.ModelSerializer):
    # Nested serializer for read-only responses
    status = StatusSerializer(read_only=True)
    # Primary key field for writing the foreign key
    status_id = serializers.PrimaryKeyRelatedField(
        queryset=Status.objects.all(),
        write_only=True,
        source='status'
    )
    location = LocationSerializer(read_only=True)
    location_id = serializers.PrimaryKeyRelatedField(
        queryset=Location.objects.all(),
        write_only=True,
        source='location'
    )

    class Meta:
        model = Profile
        fields = ('status', 'status_id', 'location', 'location_id', 'online', 'redmine_id')

Step 2: Extend UserViewSet to Handle Profile Updates

Add logic to your UserViewSet to update profile data when it's included in the request:

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    def partial_update(self, request, *args, **kwargs):
        user = self.get_object()
        # Extract profile data from the request if present
        profile_data = request.data.pop('profile', None)
        if profile_data:
            Profile.objects.filter(user=user).update(**profile_data)
        return super().partial_update(request, *args, **kwargs)

Now you can send a PATCH request to /api/users/<user_id>/ with this body:

{
  "profile": {
    "status_id": 2
  }
}

Option 2: Dedicated View for Status Updates

If you want a separate endpoint just for updating user status, here's how to implement it correctly:

Step 1: Fix the UserStatusView

from rest_framework.response import Response
from rest_framework import status as drf_status
from rest_framework.exceptions import NotFound

class UserStatusView(viewsets.ViewSet):
    def partial_update(self, request, pk=None):
        # Handle missing user
        try:
            user = User.objects.get(pk=pk)
        except User.DoesNotExist:
            raise NotFound(detail="User not found")
        
        # Validate required status ID
        status_id = request.data.get('status_id')
        if not status_id:
            return Response(
                {"error": "status_id is required"}, 
                status=drf_status.HTTP_400_BAD_REQUEST
            )
        
        # Handle missing status
        try:
            new_status = Status.objects.get(pk=status_id)
        except Status.DoesNotExist:
            raise NotFound(detail="Status not found")
        
        # Update and save the profile
        user.profile.status = new_status
        user.profile.save()
        
        # Return updated profile data
        serializer = ProfileSerializer(user.profile)
        return Response(serializer.data, status=drf_status.HTTP_200_OK)

Step 2: Use Nested Routes for Better REST Structure

Instead of a standalone update_status route, nest it under the user endpoint for clarity. Use a nested router:

from rest_framework_nested import routers

router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)

# Create nested router for user-specific status updates
users_router = routers.NestedDefaultRouter(router, r'users', lookup='user')
users_router.register(r'status', views.UserStatusView, basename='user-status')

urlpatterns = [
    path('', views.index, name='index'),
    path('api/', include(router.urls)),
    path('api/', include(users_router.urls))
]

This gives you a clean URL: /api/users/<user_id>/status/ where you can send a PATCH request with {"status_id": 2} to update the user's status.

Key Takeaways

  • Avoid nested serializers for writes unless you need to create/update related objects in one request—using a primary key field is simpler for foreign key updates.
  • Follow REST conventions: Nesting status updates under the user endpoint makes your API intuitive (users expect user-related actions under user URLs).
  • Add error handling: Catching DoesNotExist exceptions and returning proper HTTP status codes (404, 400) makes your API robust and user-friendly.

内容的提问来源于stack exchange,提问作者Sierran

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:09:16