src/p4l/api/serializers.py
author cavaliet
Mon, 16 Sep 2013 18:00:04 +0200
changeset 94 fd2ba38ce4ad
parent 93 c675183a9325
child 98 7b6ca4ca3f67
permissions -rw-r--r--
debug

# -*- coding: utf-8 -*-
'''
Created on Sep 2, 2013

@author: ymh
'''
from django.core.exceptions import ValidationError
from p4l.models import Record
from rest_framework import serializers
import logging
from rest_framework.serializers import NestedValidationError
import copy
from django.db.models.fields.related import ForeignKey

logger = logging.getLogger(__name__)

class ThesaurusSerializer(serializers.SlugRelatedField):
    
    def __init__(self, *args, **kwargs):
        kwargs.update({                       
            'read_only': False,
        })
        serializers.SlugRelatedField.__init__(self, *args, **kwargs)
        
    def from_native(self, data):
        if self.queryset is None:
            raise Exception('Writable related fields must include a `queryset` argument')
        try:
            res, _ = self.queryset.get_or_create(**{self.slug_field: data})
            return res
        except (TypeError, ValueError):
            msg = self.error_messages['invalid']
            raise ValidationError(msg)


class P4lModelSerializer(serializers.ModelSerializer):
    
    
    def get_nested_field(self, model_field, related_model, to_many):
        field_exclude = ()
        if model_field is None:
            fk = [f.name for f in related_model._meta.fields if (isinstance(f, ForeignKey) and f.rel.to == Record)][0]
            field_exclude = ('id', fk)
        class NestedModelSerializer(P4lModelSerializer):
            class Meta:
                model = related_model
                depth = self.opts.depth - 1
                exclude = field_exclude

        return NestedModelSerializer(many=to_many)

        
    def field_from_native(self, data, files, field_name, into):
        """
        Override default so that the serializer can be used as a writable
        nested field across relationships.
        """        
        if self.read_only:
            return
        
        try:
            value = data[field_name]
        except KeyError:
            if self.default is not None and not self.partial:
                # Note: partial updates shouldn't set defaults
                value = copy.deepcopy(self.default)
            else:
                if self.required:
                    raise ValidationError(self.error_messages['required'])
                return

        # Set the serializer object if it exists
        obj = getattr(self.parent.object, field_name) if self.parent.object else None

        if self.source == '*':
            if value:
                into.update(value)
        else:
            if value in (None, ''):
                into[(self.source or field_name)] = None
            elif self.many and hasattr(value, '__iter__'):
                obj.all().delete()
                nested_items = []
                for val in value:
                    kwargs = {
                        'instance': None,
                        'data': val,
                        'context': self.context,
                        'partial': self.partial,
                        'many': False
                    }
                    serializer = self.__class__(**kwargs)
                    nested_items.append(serializer.from_native(val, files))
                into[self.source or field_name] = nested_items
            else:
                kwargs = {
                    'instance': obj,
                    'data': value,
                    'context': self.context,
                    'partial': self.partial,
                    'many': self.many
                }
                serializer = self.__class__(**kwargs)

                if serializer.is_valid():
                    into[self.source or field_name] = serializer.object
                else:
                    # Propagate errors up to our parent
                    raise NestedValidationError(serializer.errors)





#class RecordSerializer(serializers.ModelSerializer):
class RecordSerializer(P4lModelSerializer):
    '''
    Serializer for record
    '''
    language = ThesaurusSerializer(many=False, required=False, slug_field='uri') 
    otherLanguages = ThesaurusSerializer(many=True, slug_field='uri')
    subjects = ThesaurusSerializer(many=True, slug_field='uri')
    themes = ThesaurusSerializer(many=True, slug_field='uri')
    countries = ThesaurusSerializer(many=True, slug_field='uri')
    projectNames = ThesaurusSerializer(many=True, slug_field='uri')
    subjectCorporateBodies = ThesaurusSerializer(many=True, slug_field='uri') 
    corporateAuthors = ThesaurusSerializer(many=True, slug_field='uri')

    class Meta:
        model = Record
        depth = 1
        read_only_fields = ('identifier', 'uri')
        fields = ("identifier", "uri", "subjects", "notes", "otherLanguages",
                  "language", "editionStatement", "recordType", "isDocumentPart",
                  "isMultilingual", "themes", "countries", "projectNames", 
                  "subjectCorporateBodies", "corporateAuthors", "imprints",
                  "titles", "addedTitles", "issns", "isbns", "documentCodes",
                  "abstracts", "titlesMainDocument", "collations", "volumeIssues",
                  "periodicals", "meetings", "subjectMeetings", "series",
                  "authors", "subjectPersons", "urls")