Reputation: 3642
I have a model like so:
class MyModel(models.Model):
thing = models.ForeignKey('Thing')
Serializers and ViewSet like so:
class ThingSerializer(serializers.ModelSerializer):
class Meta:
model = Thing
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
class MyModelViewSet(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer
For MyModel list endpoint, DRF return objects like:
[
{ id: 1, thing: 1 },
{ id: 2, thing: 1 },
{ id: 3, thing: 2 },
{ id: 4, thing: 4 }
]
Is there a way to tell DRF to automatically include "_id" on the end of ForeignKey fields that are just IDs and not the actual related object? e.g.
[
{ id: 1, thing_id: 1 },
{ id: 2, thing_id: 1 },
{ id: 3, thing_id: 2 },
{ id: 4, thing_id: 4 }
]
Upvotes: 12
Views: 3616
Reputation: 177
This has worked for me as of today in July 2022.
def ParentModel():
...
def ChildModel():
parent = models.ForeignKey(ParentModel)
serializers.py
def ChildSerializer():
parent_id = serializers.PrimaryKeyRelatedField(
source="parent",
queryset=ParentModel.objects.all(),
)
class Meta:
exclude = ["parent"]
fields = "__all__"
The source="parent"
in the serializer field, is what allows this rename to happen; it needs to match the ForeignKey
field name in ChildModel
.
Note: The exclude = ["parent"]
is required if you are using fields = "__all__"
so that DRF doesn't require or return the default field name.
Note 2: If using UUID
for your ID field then you'll need to add pk_field=UUIDField(format="hex_verbose"),
to the serializer field you're renaming to {field}_id
Upvotes: 3
Reputation: 19813
You can use db_column
model field option in your model
:
class MyModel(models.Model):
thing = models.ForeignKey('Thing', db_column='thing_id')
Or if you don't want to change your model, you can do so by changing source
serializer field in your serializer
:
class ThingSerializer(serializers.ModelSerializer):
thing_id = serializers.IntegerField(source='thing')
class Meta:
model = Thing
fields = ('thing_id','other_field', 'another_field')
Upvotes: 1
Reputation: 3642
Found same request and solution here:
https://github.com/tomchristie/django-rest-framework/issues/3121
https://gist.github.com/ostcar/eb78515a41ab41d1755b
The AppendIdSerializerMixin.get_fields
override suffices for output of JSON objects (with _id appended) but when writing back to the API, it's a little more complicated and the logic in IdPrimaryKeyRelatedField
and IdManyRelatedField
handles that.
class IdManyRelatedField(relations.ManyRelatedField):
field_name_suffix = '_ids'
def bind(self, field_name, parent):
self.source = field_name[:-len(self.field_name_suffix)]
super(IdManyRelatedField, self).bind(field_name, parent)
class IdPrimaryKeyRelatedField(relations.PrimaryKeyRelatedField):
"""
Field that the field name to FIELD_NAME_id.
Only works together the our ModelSerializer.
"""
many_related_field_class = IdManyRelatedField
field_name_suffix = '_id'
def bind(self, field_name, parent):
"""
Called when the field is bound to the serializer.
Changes the source so that the original field name is used (removes
the _id suffix).
"""
if field_name:
self.source = field_name[:-len(self.field_name_suffix)]
super(IdPrimaryKeyRelatedField, self).bind(field_name, parent)
class AppendIdSerializerMixin(object):
'''
Append '_id' to FK field names
https://gist.github.com/ostcar/eb78515a41ab41d1755b
'''
serializer_related_field = IdPrimaryKeyRelatedField
def get_fields(self):
fields = super(AppendIdSerializerMixin, self).get_fields()
new_fields = type(fields)()
for field_name, field in fields.items():
if getattr(field, 'field_name_suffix', None):
field_name += field.field_name_suffix
new_fields[field_name] = field
return new_fields
class MyModelSerializer(AppendIdSerializerMixin, serializers.ModelSerializer):
class Meta:
model = MyModel
class MyModelViewSet(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer
Upvotes: 11
Reputation: 2310
It appears to be rather complicated since it's not something DRF allows to configure. But like always, you can override things.
Everything seems to happens in the model_meta.py file. In this file, you can replace
forward_relations[field.name] = RelationInfo(
by
forward_relations[field.name + '_id'] = RelationInfo(
Careful, you need to do it twice in that function.
Once you've done that, you have still work to do as the ModelSeralizer
depends on the real model_meta. It appears you need to replace those three lines:
Then, you need to implement a MyModelSerializer
which overrides ModelSerializer
and the three methods: create
, get_fields
, get_unique_together_validators
. I tested it on GET
requests and it works.
As you can see, it's a significant amount of code rewriting which implies difficulties for maintaining upgrades. Then, I would strongly recommend to think twice before doing so. In the mean time, you can still open an issue on the DRF project for making it more configurable (and maintainable).
Upvotes: 0
Reputation: 1678
ok matt simply add that parameter to your model class:
thing = models.ForeignKey(Thing, related_name='thing_id')
Upvotes: 0