Dmitrii Mikhailov
Dmitrii Mikhailov

Reputation: 5221

Create new related object when updating one

I can't figure out how to create new related object when I update my User instance. Here'are my models:

class User(AbstractBaseUser, PermissionsMixin):
    name = models.CharField(max_length=50, verbose_name=_('Name'))
    email = models.EmailField(max_length=50, unique=True, verbose_name=_('Email'))

class Weight(models.Model):
    value = models.FloatField()
    units = models.IntegerField(choices=UNITS)
    created = models.DateTimeField(auto_now_add=True)
    user = models.ForeignKey(User)

Here are my serializers:

class UserSerializer(serializers.ModelSerializer):
    weight = WeightSerializer(source='weight')

    class Meta:
        model = User
        fields = ('name', 'email', 'weight')

class WeightSerializer(serializers.ModelSerializer):
    class Meta:
        model = Weight
        fields = ('value', 'units',)

Here's the actual view:

class UserDetails(APIView):
    def post(self, request, format=None):
        user = request.user
        if user.is_authenticated():
            serializer = UserSerializer(user, data=request.DATA, partial=True)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data)

I need to create new Weight object every time I update User info. How can I do that? When I'm trying to do it now, I get AttributeError: can't set attribute.

Upvotes: 0

Views: 129

Answers (1)

Kevin Brown-Silva
Kevin Brown-Silva

Reputation: 41671

In Django REST Framework 3.0, serializers must explicitly handle nested updates, which is essentially what you are trying to do here. You would just need to override perform_update and have it create the new Weight object instead of manually updating it.

In Django REST Framework 2.4, much of this happens automagically, and you are trying to change that magic behavior.

Django will do an update on a model object if the object has an id on it, and an update if id is set to None. This means that you can create a basic copy of the object by setting the id to None, which is the easiest way to do what you are looking for right now.

class UserDetails(APIView):
    def post(self, request, format=None):
        user = request.user
        if user.is_authenticated():
            serializer = UserSerializer(user, data=request.DATA, partial=True)
            if serializer.is_valid():
                user.weight.pk = None
                user.weight.save()
                serializer.save()
                return Response(serializer.data)

This should cause a new Weight object to be created each time the user is updated.

Upvotes: 1

Related Questions