So, I completely get the use of reverse relationships in the serializer for Django Rest Framework. And I've successfully implemented a solution that gives me what I want to see in the data output.
However, the final implementation makes me a little unhappy.
Essentially, I wanted to have an API result that gave me complete nested information of my related model - so Modules with all its fields and then its reverse relationship, SubModules, with all its fields.
So, in my example, in order we have Modules which in turn have many SubModules.
The SubModules models.py has a relationship like:
module = models.ForeignKey(Module, related_name='sub_modules')
Obviously, there is no direct sub_modules property defined in the Modules models.py.
So, in the Modules serializer.py, I wanted to make sure that the full sub_modules objects were returned in the API call to /api/modules - not URLs or IDs.
So, my initial implementation (not what I wanted) was something like:
class ModuleSerializer(serializers.ModelSerializer):
class Meta:
model = Module
fields = '__all__'
depth = 1
However, this returns the following (notice, no sub_modules):
{
"id": 1,
"created_at": "2017-02-13T11:19:28.665000Z",
"updated_at": "2017-02-13T11:19:28.665000Z",
"order": 1,
"inactive": null,
"name": "Module 1",
"description": "Module 1",
"price": "100.0000000000"
},
The implementation that eventually worked (but whose pattern I'm not happy with) was something like:
class ModuleSerializer(serializers.ModelSerializer):
class Meta:
model = Module
fields = ('id', 'created_at', 'updated_at', 'order', 'inactive',
'price', 'name', 'description', 'sub_modules')
depth = 1
This, correctly, gave me:
{
"id": 1,
"created_at": "2017-02-13T11:19:28.665000Z",
"updated_at": "2017-02-13T11:19:28.665000Z",
"order": 1,
"inactive": null,
"price": "100.0000000000",
"name": "Module 1",
"description": "Module 1",
"sub_modules": [
{
"id": 1,
"created_at": "2017-02-13T14:16:00.478429Z",
"updated_at": "2017-02-13T14:16:00.478471Z",
"order": 1,
"inactive": null,
"name": "SubModule 1",
"description": "SubModule 1",
"price": "100.0000000",
"module": 1
}
]
},
The thing that makes Django Rest Framework my goto REST implementation of choice (over say, Flask RESTful for example) is that I can usually rely on my pattern involving DRY code and the implementation of consistent single sources of truth.
I want to be able to keep my data layer where it should be - at my model level and change only that and have it feedback, by default, to the serializer. Obviously, then I'd manage exceptions on the serializer level when I want the data to transform or be absent or whatever (a good example is keeping the password field off of the users endpoint data).
If I have to maintain the fields value in the Meta class, I'm inherently managing two sources of truth for my Modules entity.
I'd like my sub_modules object to be returned by default by the fields = '__all__' expression. But, it's explicitly stated, this isn't done by default in the DRF docs.
So, I tried a couple of implementations of a manual sub_modules field on the Modules serializer.py (an acceptable implementation in my opinion since it is a reverse relationship).
Remember, the following are on the Modules serializer.py:
sub_modules = serializers.RelatedField()
Result
AssertionError: Relational field must provide a queryset argument, override get_queryset, or set read_only=True.
sub_modules = serializers.RelatedField(read_only=True)
Result
NotImplementedError at /modules/ RelatedField.to_representation() must be implemented for field sub_modules
Say what??
Basically, I'm looking for a pattern that allows me to link my serializer to my model AND include the reverse relationships in a manner where I don't have to define my field specification explicitly on the serializer.py.
I'm the biggest advocate of explicit over implicit but, the model is quite explicit enough, in my opinion.
Create a serializer for SubModules with fields set to all. Then, refer to this serializer from your ModuleSerializer like so:
class ModuleSerializer(serializers.ModelSerializer):
sub_modules = SubModuleSerializer(many=True)
class Meta:
fields = '__all __'
model = Modules
depth = 1
...
This should give you the nested representation that you are looking for while preserving a DRY format.
Haven't actually tried this out but should work
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With