Sierran
Sierran

Reputation: 117

Django rest framework, change ForeignKey

i'm fighting with DRF too long so now i must ask question.. How change ForeignKey to another? I have user profile and relation to status model.

models.py

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    ldap_uid = models.CharField(max_length=100, blank=True, null=True, default=None)
    redmine_id = models.IntegerField(blank=True, null=True, default=None)
    status = models.ForeignKey(Status, models.SET_NULL, blank=False, null=True, default=DEFAULT_STATUS_ID)
    location = models.ForeignKey(Location, models.SET_NULL, blank=False, null=True, default=DEFAULT_LOCATION_ID)
    online = models.BooleanField(default=False)


class SelectValuesModel(models.Model):
    name = models.CharField(max_length=100)
    display_name = models.CharField(max_length=100)

    def __str__(self):
        return self.name

    class Meta:
        abstract = True


class Status(SelectValuesModel):
    pass


class Location(SelectValuesModel):
    pass

What is good way to change Profile status to another? I'm trying with something like this without success

views.py

class UserStatusView(viewsets.ViewSet):
    def partial_update(self, request, pk=None):
        user = User.objects.get(pk=pk)
        user_profile = user.profile
        new_stauts = Status.objects.get(request.data.status)
        serialized_data = ProfileSerializer(user_profile)
        if(serialized_data.is_valid()):
            serialized_data.save(status=new_stauts)
        return Response(serialized_data.errors)

And trying send new id via PATCH. I'm trying tto find solution but no success here too. And how do it good? Make another route for updating Profile status? Or make something like profile/1/update_status/2? Now my routing looks like:

router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'redmine', views.RedmineCurrentTaskView, base_name='redmine')
router.register(r'parameters', views.ParametersView, base_name='parameters')
router.register(r'update_status', views.UserStatusView, base_name='update_status')
router.register(r'debug', views.DebugStatus, base_name='debug')

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

And serializers.py

class SelectValuesSerializer(serializers.ModelSerializer):
    class Meta:
        fields = ('pk', 'name', 'display_name')


class LocationSerializer(SelectValuesSerializer):
    class Meta(SelectValuesSerializer.Meta):
        model = Location


class StatusSerializer(SelectValuesSerializer):
    class Meta(SelectValuesSerializer.Meta):
        model = Status


class ProfileSerializer(serializers.ModelSerializer):
    status = StatusSerializer()
    location = LocationSerializer()

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


class UserSerializer(serializers.ModelSerializer):
    profile = ProfileSerializer(read_only=True)

    class Meta:
        model = User
        fields = ('pk', 'first_name', 'profile')
        read_only_fields = ('first_name',)

Upvotes: 1

Views: 445

Answers (1)

neverwalkaloner
neverwalkaloner

Reputation: 47354

Just pass request.data to the serializer with partial=True argument:

class UserStatusView(viewsets.ViewSet):
    def partial_update(self, request, pk=None):
        user = User.objects.get(pk=pk)
        user_profile = user.profile
        serialized_data = ProfileSerializer(user_profile, data=request.data, partial=True)
        if serialized_data.is_valid():
            serialized_data.save()
            return Response(serialized_data.data)
        return Response(serialized_data.errors)

You need to provide status_id with request body like this:

{"status": 1}

UPD

To pass status as id change your serializer to this:

class ProfileSerializer(serializers.ModelSerializer):
    location = LocationSerializer()

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

    def to_representation(self, instance):
        self.fields['status'] = StatusSerializer()
        return super(ProfileSerializer, self).to_representation(instance)

This allows to post status_id, but get status details with your API.

Upvotes: 3

Related Questions