class Foo(models.Model):
bar = models.CharField(max_length=300)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
class FooSerializer(serializers.ModelSerializer):
class Meta:
model = Foo
class FooViewSet(viewsets.ModelViewSet):
model = Foo
serializer_class = FooSerializer
I can now post data to the viewset that looks like this:
{
bar: 'content',
content_type: 1
object_id: 5
}
The only thing that's bugging me is that the frontend would have to be aware of the contenttype id's
Instead I want to be able to post the content_types name like 'User' as content_type and have the backend determine the id.
The easiest and cleanest method as of DRF 3.x for read/write operations:
from django.contrib.contenttypes.models import ContentType
from rest_framework import serializers
from .models import Foo
class FooSerializer(serializers.ModelSerializer):
class Meta:
model = Foo
content_type = serializers.SlugRelatedField(
queryset=ContentType.objects.all(),
slug_field='model',
)
You can then perform CRUD operations using the model name:
data = {
'bar': "content",
'content_type': "model_name",
'object_id': 1,
}
You could customize WritableField
to map contenttype id to 'app_label.model'
string:
class ContentTypeField(serializers.WritableField):
def field_from_native(self, data, files, field_name, into):
into[field_name] = self.from_native(data[field_name])
def from_native(self, data):
app_label, model = data.split('.')
return ContentType.objects.get(app_label=app_label, model=model)
# If content_type is write_only, there is no need to have field_to_native here.
def field_to_native(self, obj, field_name):
if self.write_only:
return None
if obj is None:
return self.empty
ct = getattr(obj, field_name)
return '.'.join(ct.natural_key())
class FooSerializer(serializers.ModelSerializer):
content_type = ContentTypeField()
# ...
You may want to do a second mapping to limit choices of contenttype and to avoid unveiling of your app/model names:
CONTENT_TYPES = {
'exposed-contenttype': 'app_label.model'
}
class ContentTypeField(...):
def from_native(self, data):
if data not in CONTENT_TYPES:
raise serializers.ValidationError(...)
app_label, model = CONTENT_TYPES[data].split('.')
# ...
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