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.

lycosis
  • 131
  • 2
  • 12

1 Answers1

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",
}
lycosis
  • 131
  • 2
  • 12