Monday, February 27, 2017

Re: Flattening model relationships (in APIs)

Quick question. How will you handle the fields updates ?
Like, what if you get an city_name="Bombay" and city_code="DEL" ?

If you can set them as read-only, then using the source argument is the way to go.
Alternatively, you can define a non model serializer and flatten the data before hand.
You can also decide to drop the serializers and pass a Python structure directly though you would have some work for the validation.

The more strict RESTfull way would be to use the several entry points and use hyperlinks on them (though it would add some extra requests).

You have a couple of options opened to you.
Remember that Django REST framework is no silver bullet.
You should think about how you want to represent your resources first and then write the associated code.
Also remember that easy isn't the same as simple. The framework targets the latter.

Regards,
Xavier.

Le mardi 21 février 2017 20:13:25 UTC+1, Ankush Thakur a écrit :
I'm using DRF for an API. My problem is that I defined my postal address like this (breaking it up into City, State, Country):

class Country(models.Model):    
    class Meta:
        db_table = 'countries'

    name = models.TextField()
    code = models.TextField(unique=True)

class State(models.Model):    
    class Meta:
        db_table = 'states'

    name = models.TextField()
    code = models.TextField(unique=True)
    country = models.ForeignKey('Country', on_delete=models.CASCADE)


class City(models.Model):    
    class Meta:
        db_table = 'cities'

    name = models.TextField()
    code = models.TextField(unique=True)
    state = models.ForeignKey('State', on_delete=models.CASCADE)

class Address(models.Model):
    class Meta:
        db_table = 'addresses'
    
    building_no = models.TextField()
    street = models.TextField(null=True)
    locality = models.TextField(null=True)
    landmark = models.TextField(null=True)
    pincode = models.TextField(null=True)
    latitude = models.DecimalField(max_digits=9, decimal_places=6, null=True)
    longitude = models.DecimalField(max_digits=9, decimal_places=6, null=True)
    city = models.ForeignKey('City', on_delete=models.CASCADE)

Now, in my system, a venue has an address, and so the serializes are defined like this:

class VenueSerializer(serializers.ModelSerializer):
    address = AddressSerializer()
    offerings = OfferingSerializer(many=True)

    class Meta:
        model = Venue
        fields = ['id', 'name', 'price_per_day', 'status', 'offerings', 'address', 'photos']

which leads us to:

class AddressSerializer(serializers.ModelSerializer):
    city = CitySerializer()

    class Meta:
        model = Address
        fields = ['id', 'building_no', 'street', 'locality', 'landmark', 'pincode', 'latitude', 'longitude', 'city']

which leads us to:

class CitySerializer(serializers.ModelSerializer):
    state = StateSerializer()

    class Meta:
        model = City
        fields = ['id', 'name', 'code', 'state']

which leads us to:

class StateSerializer(serializers.ModelSerializer):
    country = CountrySerializer()

    class Meta:
        model = State
        fields = ['id', 'name', 'code', 'country']

which finally leads to:

class CountrySerializer(serializers.ModelSerializer):
    class Meta:
        model = Country
        fields = ['id', 'name', 'code']

and when this gets serialized, the address is given in the following format:

 "address": {
      "id": 2,
      "building_no": "11",
      "street": "Another Street",
      "locality": "",
      "landmark": "Fortis Hospital",
      "pincode": "201003",
      "latitude": "28.632778",
      "longitude": "77.219722",
      "city": {
        "id": 1,
        "name": "Delhi",
        "code": "DEL",
        "state": {
          "id": 1,
          "name": "Delhi",
          "code": "DEL",
          "country": {
            "id": 1,
            "name": "India",
            "code": "IN"
          }
        }
      }
    }

So there's a hell lot of nesting from city to state to country. If the relationship chain was even deeper, there would be even more nesting, which I feel isn't great for API consumers. What is the best practice here to put state and country at the same level as the city? I think this will also complicate the logic while POSTing data, so I'm interested in knowing about that as well.

I'm sorry if there is too much code, but I couldn't think of a better way to convey the situation than actually post everything.


Regards,
Ankush Thakur

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/fc17f66f-26fd-4efe-af54-32bb4781d302%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment