[英]django-rest-framework serializer for ContentType object
I am building an activity model, somewhat similar to this package .我正在构建一个活动模型,有点类似于这个包。 It has an actor, verb and the target.
它有一个演员、动词和目标。
class Activity(models.Model):
actor_type = models.ForeignKey(ContentType, related_name='actor_type_activities')
actor_id = models.PositiveIntegerField()
actor = GenericForeignKey('actor_type', 'actor_id')
verb = models.CharField(max_length=10)
target_type = models.ForeignKey(ContentType, related_name='target_type_activities')
target_id = models.PositiveIntegerField()
target = GenericForeignKey('target_type', 'target_id')
pub_date = models.DateTimeField(default=timezone.now)
Now whenever a new object of whichever models (Tender, Job and News) is created, a new Activity object is created, with the target
being the objects of any of these three models.现在,无论何时创建任何模型(Tender、Job 和 News)的新对象,都会创建一个新的 Activity 对象,
target
是这三个模型中任何一个的对象。
eg.
例如。 user (actor) published (verb) title (target)
用户(演员)发布(动词)标题(目标)
class Tender(models.Model):
title = models.CharField(max_length=256)
description = models.TextField()
class Job(models.Model):
title = models.CharField(max_length=256)
qualification = models.CharField(max_length=256)
class News(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
title = models.CharField(max_length=150)
To get this data I am making an API which will get me the required json data.为了获得这些数据,我正在制作一个 API,它将为我提供所需的 json 数据。 I am using django-rest-framework for this and very new with it.
我正在为此使用django-rest-framework并且非常新。
class ActorSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email')
class ActivitySerializer(serializers.HyperlinkedModelSerializer):
actor = ActorSerializer()
class Meta:
model = Activity
fields = ('url', 'actor', 'verb', 'pub_date')
In the above serializers, I knew that actor
will be the User.在上面的序列化程序中,我知道
actor
将是 User。 And so I used the User model for the ActorSerializer
class.所以我将 User 模型用于
ActorSerializer
类。 But as for the target
, it can be any of these three models (News/Job/Tender).但是对于
target
,它可以是这三种模型中的任何一种(News/Job/Tender)。
How can I make a serializer (eg. TargetSerialier
class) for the ContentType object so that I can use the target
in the ActivitySerializer
class field?如何为 ContentType 对象创建序列化程序(例如
TargetSerialier
类),以便我可以在ActivitySerializer
类字段中使用target
?
Okay so answering my own question here.好的,所以在这里回答我自己的问题。 I had some help with zymud's answer.
我对 zymud 的回答有一些帮助。 So, apparently in the documentation , there is a way to serialize the Generic relation.
因此,显然在文档中,有一种方法可以序列化通用关系。
So, all I had to do was create a custom field and associate that field in the serializer itself:所以,我所要做的就是创建一个自定义字段并将该字段关联到序列化程序本身:
class ActivityObjectRelatedField(serializers.RelatedField):
def to_representation(self, value):
if isinstance(value, User):
return 'User: ' + value.username
elif isinstance(value, News):
return 'News: ' + value.title
elif isinstance(value, Job):
return 'Job: ' + value.title
elif isinstance(value, Tender):
return 'Tender: ' + value.title
raise Exception('Unexpected type of tagged object')
class ActivitySerializer(serializers.HyperlinkedModelSerializer):
actor = ActivityObjectRelatedField(read_only=True)
target = ActivityObjectRelatedField(read_only=True)
class Meta:
model = Activity
fields = ('url', 'actor', 'verb', 'target', 'pub_date')
You can implement custom field for generic key.您可以为通用键实现自定义字段。 Example:
示例:
from django.core.urlresolvers import resolve
from rest_framework.fields import Field
class GenericRelatedField(Field):
"""
A custom field that expect object URL as input and transforms it
to django model instance.
"""
read_only = False
_default_view_name = '%(model_name)s-detail'
lookup_field = 'pk'
def __init__(self, related_models=(), **kwargs):
super(GenericRelatedField, self).__init__(**kwargs)
# related models - list of models that should be acceptable by
# field. Note that all this models should have corresponding
# endpoint.
self.related_models = related_models
def _get_url_basename(self, obj):
""" Get object URL basename """
format_kwargs = {
'app_label': obj._meta.app_label,
'model_name': obj._meta.object_name.lower()
}
return self._default_view_name % format_kwargs
def _get_request(self):
try:
return self.context['request']
except KeyError:
raise AttributeError('GenericRelatedField have to be initialized with `request` in context')
def to_representation(self, obj):
""" Serializes any object to its URL representation """
kwargs = {self.lookup_field: getattr(obj, self.lookup_field)}
request = self._get_request()
return request.build_absolute_uri(reverse(self._get_url_basename(obj), kwargs=kwargs))
def clear_url(self, url):
""" Removes domain and protocol from url """
if url.startswith('http'):
return '/' + url.split('/', 3)[-1]
return url
def get_model_from_resolve_match(self, match):
queryset = match.func.cls.queryset
if queryset is not None:
return queryset.model
else:
return match.func.cls.model
def instance_from_url(self, url):
url = self.clear_url(url)
match = resolve(url)
model = self.get_model_from_resolve_match(match)
return model.objects.get(**match.kwargs)
def to_internal_value(self, data):
""" Restores model instance from its URL """
if not data:
return None
request = self._get_request()
user = request.user
try:
obj = self.instance_from_url(data)
model = obj.__class__
except (Resolver404, AttributeError, MultipleObjectsReturned, ObjectDoesNotExist):
raise serializers.ValidationError("Can`t restore object from url: %s" % data)
if model not in self.related_models:
raise serializers.ValidationError('%s object does not support such relationship' % str(obj))
return obj
Example of usage:用法示例:
class ActivitySerializer(serializers.HyperlinkedModelSerializer):
target = GenericRelatedField(related_models=(News, Job, Tender))
...
https://www.django-rest-framework.org/api-guide/relations/#rest-framework-generic-relations https://www.django-rest-framework.org/api-guide/relations/#rest-framework-generic-relations
It is pretty neat actually, my serializer class ended up few readable lines:实际上它非常整洁,我的序列化器类最终只有几行可读的行:
class ActivityTypeSerializer(serializers.ModelSerializer):
target = GenericRelatedField({
User: UserSerializer(),
Device: DeviceSerializer(),
})
class Meta:
model = Activity
fields = ('target', 'target_id', 'verb', 'target_ct',)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.