123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- from __future__ import unicode_literals
- from datetime import datetime
- from rest_framework import serializers
- from rest_framework.exceptions import ValidationError
- from django.contrib.contenttypes.models import ContentType
- from django.db import transaction
- from extras.models import (
- CF_TYPE_BOOLEAN, CF_TYPE_DATE, CF_TYPE_SELECT, CustomField, CustomFieldChoice, CustomFieldValue,
- )
- #
- # Custom fields
- #
- class CustomFieldsSerializer(serializers.BaseSerializer):
- def to_representation(self, obj):
- return obj
- def to_internal_value(self, data):
- content_type = ContentType.objects.get_for_model(self.parent.Meta.model)
- custom_fields = {field.name: field for field in CustomField.objects.filter(obj_type=content_type)}
- for field_name, value in data.items():
- cf = custom_fields[field_name]
- # Validate custom field name
- if field_name not in custom_fields:
- raise ValidationError("Invalid custom field for {} objects: {}".format(content_type, field_name))
- # Validate boolean
- if cf.type == CF_TYPE_BOOLEAN and value not in [True, False, 1, 0]:
- raise ValidationError("Invalid value for boolean field {}: {}".format(field_name, value))
- # Validate date
- if cf.type == CF_TYPE_DATE:
- try:
- datetime.strptime(value, '%Y-%m-%d')
- except ValueError:
- raise ValidationError("Invalid date for field {}: {}. (Required format is YYYY-MM-DD.)".format(
- field_name, value
- ))
- # Validate selected choice
- if cf.type == CF_TYPE_SELECT:
- try:
- value = int(value)
- except ValueError:
- raise ValidationError("{}: Choice selections must be passed as integers.".format(field_name))
- valid_choices = [c.pk for c in cf.choices.all()]
- if value not in valid_choices:
- raise ValidationError("Invalid choice for field {}: {}".format(field_name, value))
- # Check for missing required fields
- missing_fields = []
- for field_name, field in custom_fields.items():
- if field.required and field_name not in data:
- missing_fields.append(field_name)
- if missing_fields:
- raise ValidationError("Missing required fields: {}".format(u", ".join(missing_fields)))
- return data
- class CustomFieldModelSerializer(serializers.ModelSerializer):
- """
- Extends ModelSerializer to render any CustomFields and their values associated with an object.
- """
- custom_fields = CustomFieldsSerializer(required=False)
- def __init__(self, *args, **kwargs):
- def _populate_custom_fields(instance, fields):
- custom_fields = {f.name: None for f in fields}
- for cfv in instance.custom_field_values.all():
- if cfv.field.type == CF_TYPE_SELECT:
- custom_fields[cfv.field.name] = CustomFieldChoiceSerializer(cfv.value).data
- else:
- custom_fields[cfv.field.name] = cfv.value
- instance.custom_fields = custom_fields
- super(CustomFieldModelSerializer, self).__init__(*args, **kwargs)
- if self.instance is not None:
- # Retrieve the set of CustomFields which apply to this type of object
- content_type = ContentType.objects.get_for_model(self.Meta.model)
- fields = CustomField.objects.filter(obj_type=content_type)
- # Populate CustomFieldValues for each instance from database
- try:
- for obj in self.instance:
- _populate_custom_fields(obj, fields)
- except TypeError:
- _populate_custom_fields(self.instance, fields)
- def _save_custom_fields(self, instance, custom_fields):
- content_type = ContentType.objects.get_for_model(self.Meta.model)
- for field_name, value in custom_fields.items():
- custom_field = CustomField.objects.get(name=field_name)
- CustomFieldValue.objects.update_or_create(
- field=custom_field,
- obj_type=content_type,
- obj_id=instance.pk,
- defaults={'serialized_value': custom_field.serialize_value(value)},
- )
- def create(self, validated_data):
- custom_fields = validated_data.pop('custom_fields', None)
- with transaction.atomic():
- instance = super(CustomFieldModelSerializer, self).create(validated_data)
- # Save custom fields
- if custom_fields is not None:
- self._save_custom_fields(instance, custom_fields)
- instance.custom_fields = custom_fields
- return instance
- def update(self, instance, validated_data):
- custom_fields = validated_data.pop('custom_fields', None)
- with transaction.atomic():
- instance = super(CustomFieldModelSerializer, self).update(instance, validated_data)
- # Save custom fields
- if custom_fields is not None:
- self._save_custom_fields(instance, custom_fields)
- instance.custom_fields = custom_fields
- return instance
- class CustomFieldChoiceSerializer(serializers.ModelSerializer):
- """
- Imitate utilities.api.ChoiceFieldSerializer
- """
- value = serializers.IntegerField(source='pk')
- label = serializers.CharField(source='value')
- class Meta:
- model = CustomFieldChoice
- fields = ['value', 'label']
|