Reputation: 952
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.
Upvotes: 3
Views: 2122
Reputation: 1559
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
Upvotes: 4