1

Good day,

I'm trying to do a PUT request that contains a nested-object and I can't get the province to update correctly. There didn't seem to be anything obvious in the django-rest-framework docs to help with this and I've investigated the solutions of a few other similar problems but none have helped (set many=false, change to ModelSerializer, specialized serializers, etc.).

Everything else about the address will update correctly and return a 200 response (no errors in django logs either). Am I wrong to assume that django-rest-framework handles this all for me for free? Do I have to override the update and create methods within the serializer to validate and save the nested-object?

I'm thinking it's because I have the province serializer set to read_only within the address serializer. However, if I remove the read_only modifier on the province serializer, it gives an error about the province already existing:

{
    "province": {
        "province": [
            "valid province with this province already exists."
        ]
    }
}

Which is behaviour I do not expect and don't know how to resolve. I am not trying to add or update the province. I just want to change the province code in the address.province field and I can't use a string "MB" because it expects an object. I effectively want this behaviour:

UPDATE agent_business_address
  SET province = 'MB'
WHERE agent_id = 12345;

-- agent_business_address.province has a foreign key constraint on valid_province.province
-- valid_province is populated with all the 2-letter abbreviations for provinces(

I make this PUT request to /api/agent-business-address/

{
    "address": "123 Fake St",
    "agent_id": 12345,
    "city": "Calgary",
    "dlc": "2021-10-11 14:03:03",
    "operator_id": 4,
    "postal_code": "A1B 2C3",
    "province": {
        "description": "Manitoba",
        "province": "MB"
    },
    "valid_address": "N"
}

That is received by this ViewSet:

class AgentBusinessAddressViewSet(viewsets.ModelViewSet):
    queryset = AgentBusinessAddress.objects.all()
    serializer_class = AgentBusinessAddressSerializer

Relevant serializers:

class AgentBusinessAddressSerializer(serializers.HyperlinkedModelSerializer):
    province = ValidProvinceSerializer(read_only=True) # Removing the read_only causes the error above.
    class Meta:
        model = AgentBusinessAddress
        fields = ('agent_id', 'operator_id', 'dlc', 'address', 'city', 'province', 'postal_code', 'valid_address')

class ValidProvinceSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = ValidProvince
        read_only_fields = ('operator_id', 'dlc')
        fields = ('province', 'description')

Relevant models:

class AgentBusinessAddress(models.Model):
    agent = models.OneToOneField(Agent, models.DO_NOTHING, primary_key=True)
    operator_id = models.SmallIntegerField()
    dlc = models.DateTimeField()
    address = models.CharField(max_length=100)
    city = models.CharField(max_length=80)
    province = models.ForeignKey('ValidProvince', models.DO_NOTHING, db_column='province')
    postal_code = models.CharField(max_length=7)
    valid_address = models.CharField(max_length=1)

    class Meta:
        managed = False
        db_table = 'agent_business_address'

class ValidProvince(models.Model):
    province = models.CharField(primary_key=True, max_length=2)
    operator_id = models.SmallIntegerField()
    dlc = models.DateTimeField()
    description = models.CharField(max_length=30, blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'valid_province'

Any help would be appreciated.

1
  • Yes you need to write the custom create and update function for nested serializer Commented Oct 11, 2021 at 20:54

1 Answer 1

1

Solved it with the help of the specialized serializers post after a few re-reads.

I updated the serializers to this:

# This gets called for non-GET requests.
class AgentBusinessAddressSerializer(serializers.ModelSerializer):
    class Meta:
        model = AgentBusinessAddress
        fields = ('__all__')

# This get called for GET requests.
class AgentBusinessAddressReadSerializer(AgentBusinessAddressSerializer):
    province = ValidProvinceSerializer(read_only=True)
    class Meta:
        model = AgentBusinessAddress
        fields = ('__all__')

I updated the viewset to this:

class AgentBusinessAddressViewSet(viewsets.ModelViewSet):
    queryset = AgentBusinessAddress.objects.all()
    
    def get_serializer_class(self):
        if self.request.method in ['GET']:
            return AgentBusinessAddressReadSerializer
        return AgentBusinessAddressSerializer

Now I just send the primary key for the province in the PUT request:

{
    "address": "123 Fake St",
    "agent": 10000003,
    "city": "Calgary",
    "dlc": "2021-10-11 19:47:38",
    "operator_id": 4,
    "postal_code": "A1B 2C3",
    "province": "NS",
    "valid_address": "N"
}

I get a PUT 200 response back and verify in the DB that the province is now 'NS'.

{
    "agent": 10000003,
    "operator_id": 4,
    "dlc": "2021-10-11T19:47:38",
    "address": "123 Fake St",
    "city": "Calgary",
    "postal_code": "A1B 2C3",
    "valid_address": "N",
    "province": "NS",
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.