2016-06-20 4 views
4

나는 package과 다소 비슷한 활동 모델을 구축 중이다. 배우, 동사 및 대상이 있습니다. 어느 모델 (입찰, 작업 및 뉴스)의 새로운 객체가 생성 이제 때마다ContentType 객체를위한 django-rest-framework serializer

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) 

는 새로운 활동의 목적은 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) 

eg. user (actor) published (verb) title (target)

내가 나에게 필요한 JSON 데이터를 얻을 것이다 API를 제작하고이 데이터를 얻으려면. 나는 이것에 대해 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') 

위의 시리얼, 나는 actor은 사용자가 될 것이라는 점을 알고 있었다. 그래서 ActorSerializer 클래스의 User 모델을 사용했습니다. 그러나 target의 경우 세 가지 모델 (뉴스/작업/입찰) 중 하나 일 수 있습니다. 나는 ActivitySerializer 클래스 필드에 target을 사용할 수 있도록

은 어떻게 ContentType이 개체의 직렬화 (예. TargetSerialier 클래스)를 만들 수 있습니까?

답변

1

좋아요 그래서 여기에 내 자신의 질문에 대답. 나는 zymud의 대답에 대한 도움을 받았습니다. 그래서 분명히 documentation에는 일반 관계를 직렬화하는 방법이 있습니다.()`인수 '뉴스 - 상세' '에 대한 역 :

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') 
+0

하지만이 필드는 현재 읽기 전용입니까? ActivityObject를 저장하려고하면 어떻게됩니까? read_only를주지 않으면 queryset을 주어야하지만, 그것이 도착하기 전에 어떤 모델이 될지 모릅니다. 내가 오해하고 있니? –

2

일반 키에 대한 사용자 지정 필드를 구현할 수 있습니다. 예 : 사용의

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 

예 :

class ActivitySerializer(serializers.HyperlinkedModelSerializer): 
    target = GenericRelatedField(related_models=(News, Job, Tender)) 
    ... 
+0

나는이 오류가 무엇입니까 :

그래서 내가했던 일은, 사용자 정의 필드를 생성하고 시리얼 자체에 해당 필드를 연결했다 '및 키워드 인수'{ 'pk': 3} '을 찾을 수 없습니다. 0 개의 패턴이 시도되었습니다 : []' – Aamu

+0

'News' 모델을위한 끝점이 없거나 basename이'news-detail'이 아니기 때문에 발생합니다. DRF 모델은 엔드 포인트를 URL로 표시해야합니다. – zymud

관련 문제