customfields.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. from django.contrib.contenttypes.models import ContentType
  2. from django.db import transaction
  3. from rest_framework import serializers
  4. from rest_framework.exceptions import ValidationError
  5. from extras.models import CF_TYPE_SELECT, CustomField, CustomFieldChoice, CustomFieldValue
  6. #
  7. # Custom fields
  8. #
  9. class CustomFieldsSerializer(serializers.BaseSerializer):
  10. def to_representation(self, obj):
  11. return obj
  12. def to_internal_value(self, data):
  13. content_type = ContentType.objects.get_for_model(self.parent.Meta.model)
  14. custom_fields = {field.name: field for field in CustomField.objects.filter(obj_type=content_type)}
  15. for field_name, value in data.items():
  16. # Validate custom field name
  17. if field_name not in custom_fields:
  18. raise ValidationError(u"Invalid custom field for {} objects: {}".format(content_type, field_name))
  19. # Validate selected choice
  20. cf = custom_fields[field_name]
  21. if cf.type == CF_TYPE_SELECT:
  22. valid_choices = [c.pk for c in cf.choices.all()]
  23. if value not in valid_choices:
  24. raise ValidationError(u"Invalid choice ({}) for field {}".format(value, field_name))
  25. # Check for missing required fields
  26. missing_fields = []
  27. for field_name, field in custom_fields.items():
  28. if field.required and field_name not in data:
  29. missing_fields.append(field_name)
  30. if missing_fields:
  31. raise ValidationError(u"Missing required fields: {}".format(u", ".join(missing_fields)))
  32. return data
  33. class CustomFieldModelSerializer(serializers.ModelSerializer):
  34. """
  35. Extends ModelSerializer to render any CustomFields and their values associated with an object.
  36. """
  37. custom_fields = CustomFieldsSerializer(required=False)
  38. def __init__(self, *args, **kwargs):
  39. def _populate_custom_fields(instance, fields):
  40. custom_fields = {f.name: None for f in fields}
  41. for cfv in instance.custom_field_values.all():
  42. if cfv.field.type == CF_TYPE_SELECT:
  43. custom_fields[cfv.field.name] = CustomFieldChoiceSerializer(cfv.value).data
  44. else:
  45. custom_fields[cfv.field.name] = cfv.value
  46. instance.custom_fields = custom_fields
  47. super(CustomFieldModelSerializer, self).__init__(*args, **kwargs)
  48. if self.instance is not None:
  49. # Retrieve the set of CustomFields which apply to this type of object
  50. content_type = ContentType.objects.get_for_model(self.Meta.model)
  51. fields = CustomField.objects.filter(obj_type=content_type)
  52. # Populate CustomFieldValues for each instance from database
  53. try:
  54. for obj in self.instance:
  55. _populate_custom_fields(obj, fields)
  56. except TypeError:
  57. _populate_custom_fields(self.instance, fields)
  58. def _save_custom_fields(self, instance, custom_fields):
  59. content_type = ContentType.objects.get_for_model(self.Meta.model)
  60. for field_name, value in custom_fields.items():
  61. custom_field = CustomField.objects.get(name=field_name)
  62. CustomFieldValue.objects.update_or_create(
  63. field=custom_field,
  64. obj_type=content_type,
  65. obj_id=instance.pk,
  66. defaults={'serialized_value': value},
  67. )
  68. def create(self, validated_data):
  69. custom_fields = validated_data.pop('custom_fields', None)
  70. with transaction.atomic():
  71. instance = super(CustomFieldModelSerializer, self).create(validated_data)
  72. # Save custom fields
  73. if custom_fields is not None:
  74. self._save_custom_fields(instance, custom_fields)
  75. instance.custom_fields = custom_fields
  76. return instance
  77. def update(self, instance, validated_data):
  78. custom_fields = validated_data.pop('custom_fields', None)
  79. with transaction.atomic():
  80. instance = super(CustomFieldModelSerializer, self).update(instance, validated_data)
  81. # Save custom fields
  82. if custom_fields is not None:
  83. self._save_custom_fields(instance, custom_fields)
  84. instance.custom_fields = custom_fields
  85. return instance
  86. class CustomFieldChoiceSerializer(serializers.ModelSerializer):
  87. """
  88. Imitate utilities.api.ChoiceFieldSerializer
  89. """
  90. value = serializers.IntegerField(source='pk')
  91. label = serializers.CharField(source='value')
  92. class Meta:
  93. model = CustomFieldChoice
  94. fields = ['value', 'label']