class HearingSerializer(serializers.ModelSerializer): labels = LabelSerializer(many=True, read_only=True) images = HearingImageSerializer.get_field_serializer(many=True, read_only=True) sections = serializers.SerializerMethodField() comments = HearingCommentSerializer.get_field_serializer(many=True, read_only=True) commenting = EnumField(enum_type=Commenting) geojson = JSONField() def get_sections(self, hearing): queryset = hearing.sections.all() if not hearing.closed: queryset = queryset.exclude(type__identifier=InitialSectionType.CLOSURE_INFO) serializer = SectionFieldSerializer(many=True, read_only=True) serializer.bind('sections', self) # this is needed to get context in the serializer return serializer.to_representation(queryset) class Meta: model = Hearing fields = [ 'abstract', 'title', 'id', 'borough', 'n_comments', 'commenting', 'published', 'labels', 'open_at', 'close_at', 'created_at', 'servicemap_url', 'images', 'sections', 'images', 'closed', 'comments', 'geojson' ]
class SectionSerializer(serializers.ModelSerializer, TranslatableSerializer): """ Serializer for section instance. """ images = PublicFilteredImageField(serializer_class=SectionImageSerializer) type = serializers.SlugRelatedField(slug_field='identifier', read_only=True) type_name_singular = serializers.SlugRelatedField(source='type', slug_field='name_singular', read_only=True) type_name_plural = serializers.SlugRelatedField(source='type', slug_field='name_plural', read_only=True) commenting = EnumField(enum_type=Commenting) voting = EnumField(enum_type=Commenting) class Meta: model = Section fields = [ 'id', 'type', 'commenting', 'voting', 'published', 'title', 'abstract', 'content', 'created_at', 'images', 'n_comments', 'type_name_singular', 'type_name_plural', 'plugin_identifier', 'plugin_data', 'plugin_iframe_url', 'plugin_fullscreen', ]
class SectionSerializer(serializers.ModelSerializer): """ Serializer for section instance. """ images = SectionImageSerializer.get_field_serializer(many=True, read_only=True) type = serializers.SlugRelatedField(slug_field='identifier', read_only=True) type_name_singular = serializers.SlugRelatedField( source='type', slug_field='name_singular', read_only=True) type_name_plural = serializers.SlugRelatedField(source='type', slug_field='name_plural', read_only=True) commenting = EnumField(enum_type=Commenting) class Meta: model = Section fields = [ 'id', 'type', 'commenting', 'published', 'title', 'abstract', 'content', 'created_at', 'created_by', 'images', 'n_comments', 'plugin_identifier', 'plugin_data', 'type_name_singular', 'type_name_plural' ]
def test_enum(): ef = EnumField(enum_type=PeculiarEnum) assert ef.to_representation(PeculiarEnum.COOL) == "cool" assert ef.to_representation(PeculiarEnum.HOT) == "hot" assert ef.to_representation(PeculiarEnum.BLEGBLAGPLEPLOP) == "foo" assert ef.to_representation(None) == None assert ef.to_internal_value("cool") == PeculiarEnum.COOL assert ef.to_internal_value("CooL") == PeculiarEnum.COOL assert ef.to_internal_value("HOT") == PeculiarEnum.HOT assert ef.to_internal_value(1) == PeculiarEnum.COOL assert ef.to_internal_value("100") == PeculiarEnum.HOT assert ef.to_internal_value("foo") == PeculiarEnum.BLEGBLAGPLEPLOP assert ef.to_internal_value("fOO") == PeculiarEnum.BLEGBLAGPLEPLOP assert ef.to_internal_value("BLEGBLAGPLEPLOP") == PeculiarEnum.BLEGBLAGPLEPLOP with pytest.raises(ValidationError): ef.to_internal_value("jmsdfkgi")
class SectionCreateUpdateSerializer(serializers.ModelSerializer, TranslatableSerializer): """ Serializer for section create/update. """ id = serializers.CharField(required=False) type = serializers.SlugRelatedField(slug_field='identifier', queryset=SectionType.objects.all()) commenting = EnumField(enum_type=Commenting) # this field is used only for incoming data validation, outgoing data is added manually # in to_representation() images = serializers.ListField(child=serializers.DictField(), write_only=True) class Meta: model = Section fields = [ 'id', 'type', 'commenting', 'published', 'title', 'abstract', 'content', 'plugin_identifier', 'plugin_data', 'images', ] @transaction.atomic() def create(self, validated_data): images_data = validated_data.pop('images', []) section = super().create(validated_data) self._handle_images(section, images_data) return section @transaction.atomic() def update(self, instance, validated_data): images_data = validated_data.pop('images', []) section = super().update(instance, validated_data) self._handle_images(section, images_data) return section def validate_images(self, data): for index, image_data in enumerate(data): pk = image_data.get('id') image_data['ordering'] = index serializer_params = {'data': image_data} if pk: try: image = self.instance.images.get(pk=pk) except SectionImage.DoesNotExist: raise ValidationError('The Section does not have an image with ID %s' % pk) serializer_params['instance'] = image serializer = SectionImageCreateUpdateSerializer(**serializer_params) serializer.is_valid(raise_exception=True) # save serializer in data so it can be used when handling the images image_data['serializer'] = serializer return data def _handle_images(self, section, data): new_image_ids = set() for image_data in data: serializer = image_data.pop('serializer') image = serializer.save(section=section) new_image_ids.add(image.id) for image in section.images.exclude(id__in=new_image_ids): image.soft_delete() return section def to_representation(self, instance): data = super().to_representation(instance) data['images'] = SectionImageSerializer( instance.images.all(), many=True, context=self.context, ).data return data
class SectionCreateUpdateSerializer(serializers.ModelSerializer, TranslatableSerializer): """ Serializer for section create/update. """ id = serializers.CharField(required=False) type = serializers.SlugRelatedField(slug_field='identifier', queryset=SectionType.objects.all()) commenting = EnumField(enum_type=Commenting) # this field is used only for incoming data validation, outgoing data is added manually # in to_representation() images = serializers.ListField(child=serializers.DictField(), write_only=True) questions = serializers.ListField(child=serializers.DictField(), write_only=True, required=False) files = serializers.ListField(child=serializers.DictField(), write_only=True, required=False) class Meta: model = Section fields = [ 'id', 'type', 'commenting', 'title', 'abstract', 'content', 'plugin_identifier', 'plugin_data', 'images', 'questions', 'files', 'ordering', ] @transaction.atomic() def create(self, validated_data): images_data = validated_data.pop('images', []) polls_data = validated_data.pop('questions', []) files_data = validated_data.pop('files', []) section = super().create(validated_data) self._handle_images(section, images_data) self._handle_questions(section, polls_data) self._handle_files(section, files_data) return section @transaction.atomic() def update(self, instance, validated_data): images_data = validated_data.pop('images', []) polls_data = validated_data.pop('questions', []) files_data = validated_data.pop('files', []) section = super().update(instance, validated_data) self._handle_images(section, images_data) self._handle_questions(section, polls_data) self._handle_files(section, files_data) return section def validate_images(self, data): for index, image_data in enumerate(data): pk = image_data.get('id') image_data['ordering'] = index serializer_params = {'data': image_data} if pk: try: image = self.instance.images.get(pk=pk) except SectionImage.DoesNotExist: raise ValidationError( 'The Section does not have an image with ID %s' % pk) serializer_params['instance'] = image serializer = SectionImageCreateUpdateSerializer( **serializer_params) serializer.is_valid(raise_exception=True) # save serializer in data so it can be used when handling the images image_data['serializer'] = serializer return data def validate_files(self, data): for index, file_data in enumerate(data): pk = file_data.get('id') file_data['ordering'] = index serializer_params = { 'data': file_data, 'context': { 'request': self.context['request'] } } if pk: try: # only allow orphan files or files within this section already file = SectionFile.objects.filter( Q(section=None) | (Q(section=self.instance))).get(pk=pk) except SectionImage.DoesNotExist: raise ValidationError( 'No file with ID %s available in this section' % pk) serializer_params['instance'] = file serializer = RootFileBase64Serializer(**serializer_params) serializer.is_valid(raise_exception=True) # save serializer in data so it can be used when handling the files file_data['serializer'] = serializer return data def _handle_images(self, section, data): new_image_ids = set() for image_data in data: serializer = image_data.pop('serializer') image = serializer.save(section=section) new_image_ids.add(image.id) for image in section.images.exclude(id__in=new_image_ids): image.soft_delete() return section def _handle_files(self, section, data): new_file_ids = set() for file_data in data: serializer = file_data.pop('serializer') file = serializer.save(section=section) new_file_ids.add(file.id) for file in section.files.exclude(id__in=new_file_ids): file.soft_delete() return section def _validate_question_update(self, poll_data, poll): poll_has_answers = poll.n_answers > 0 if not poll_has_answers: return try: old_poll_data = SectionPollSerializer(poll).data assert compare_serialized(old_poll_data['text'], poll_data['text']) assert len(old_poll_data['options']) == len(poll_data['options']) for old_option, option in zip(old_poll_data['options'], poll_data['options']): assert compare_serialized(old_option['text'], option['text']) except AssertionError: raise ValidationError( 'Poll with ID %s has answers - editing it is not allowed' % repr(poll.pk)) def validate_questions(self, data): for index, poll_data in enumerate(data): pk = poll_data.get('id') poll_data['ordering'] = index + 1 serializer_params = {'data': poll_data} if pk: try: poll = self.instance.polls.get(pk=pk) except SectionPoll.DoesNotExist: raise ValidationError( 'The Section does not have a poll with ID %s' % repr(pk)) self._validate_question_update(poll_data, poll) serializer_params['instance'] = poll serializer = SectionPollSerializer(**serializer_params) serializer.is_valid(raise_exception=True) # save serializer in data so it can be used when handling the polls poll_data['serializer'] = serializer return data def _handle_questions(self, section, data): new_poll_ids = set() for poll_data in data: serializer = poll_data.pop('serializer') poll = serializer.save(section=section, ordering=poll_data['ordering']) new_poll_ids.add(poll.id) for poll in section.polls.exclude(id__in=new_poll_ids): poll.soft_delete() def to_representation(self, instance): data = super().to_representation(instance) data['images'] = SectionImageSerializer( instance.images.all(), many=True, context=self.context, ).data data['questions'] = SectionPollSerializer(instance.polls.all(), many=True).data return data