class MeSerializer(serializers.HyperlinkedModelSerializer): groups = serializers.SlugRelatedField(slug_field='name', many=True, queryset=Group.objects.all(), required=False) _links = HALLinksField( nested_endpoints=['regen_secret', 'change_password'], view_name='users:munchuser-detail') class Meta: model = MunchUser fields = [ 'identifier', 'first_name', 'last_name', 'secret', 'groups', 'organization', '_links' ] extra_kwargs = { 'organization': { 'view_name': 'users:organization-detail' } } def validate(self, attrs): super().validate() for field in ('first_name', 'last_name'): if field in attrs and attrs[field] == '': raise ValidationError(_('{} cannot be empty'.format(field)))
class SendingDomainSerializer(serializers.HyperlinkedModelSerializer): dkim_status = serializers.ReadOnlyField(allow_null=True) app_domain_status = serializers.ReadOnlyField(allow_null=True) _links = HALLinksField(nested_endpoints=['revalidate'], view_name='domains:sendingdomain-detail') alt_organizations = AltOrganizationsField( many=True, view_name='users:organization-detail', required=False) organization = serializers.HiddenField( default=serializers.CreateOnlyDefault(CurrentOrganizationDefault())) class Meta: model = SendingDomain fields = [ 'url', 'name', 'dkim_status', 'app_domain_status', 'organization', 'alt_organizations', 'app_domain', 'app_domain_status_date', 'dkim_status_date', '_links' ] read_only_fields = [ 'organization', 'dkim_status_date', 'app_domain_status_date' ] extra_kwargs = {'url': {'view_name': 'domains:sendingdomain-detail'}} def validate(self, attrs): for organization in attrs.get('alt_organizations', []): if organization.parent_id != attrs.get('organization').id: raise ValidationError('Invalid organization') return attrs
class OrganizationSerializer(serializers.HyperlinkedModelSerializer): can_attach_files = serializers.ReadOnlyField() can_external_optout = serializers.ReadOnlyField() name = serializers.CharField(required=True) contact_email = serializers.EmailField(required=True) settings = OrganizationSettingsSerializer(required=False) parent = ScopedHyperLinkedRelatedField( required=False, allow_null=True, view_name='users:organization-detail', queryset=Organization.objects.all()) _links = HALLinksField( nested_endpoints=['opt_outs', 'children', 'invite_user'], view_name='users:organization-detail') class Meta: model = Organization fields = [ 'url', 'name', 'contact_email', 'settings', 'parent', 'can_attach_files', 'can_external_optout', '_links' ] read_only_fields = ['can_attach_files', 'can_external_optout'] extra_kwargs = { 'url': { 'view_name': 'users:organization-detail' }, 'parent': { 'view_name': 'users:organization-detail' } } def validate(self, data): if not self.instance and not data.get('parent'): raise ValidationError({'parent': 'This field may not be blank.'}) if self.context.get('request').user.organization != data.get('parent'): raise ValidationError( {'parent': 'Parent organization must be your organization.'}) return super().validate(data) def create(self, validated_data): settings_data = validated_data.pop('settings', {}) organization = Organization(**validated_data) organization.clean() organization.save() if settings_data: for field, value in settings_data.items(): setattr(organization.settings, field, value) organization.settings.save() return organization def update(self, instance, validated_data): settings_data = validated_data.pop('settings', {}) if settings_data: for field, value in settings_data.items(): setattr(instance.settings, field, value) instance.settings.save() return super().update(instance, validated_data)
class MailBatchSerializer(serializers.HyperlinkedModelSerializer): _links = HALLinksField( nested_endpoints=['mails', 'stats', 'bounces', 'opt_outs'], view_name='transactional:batch-detail') class Meta: model = MailBatch fields = ['url', 'creation_date', 'name', 'category', '_links'] extra_kwargs = { 'url': { 'view_name': 'transactional:batch-detail' }, 'category': { 'view_name': 'core:category-detail' } }
class MunchUserSerializer(serializers.HyperlinkedModelSerializer): api_key = serializers.ReadOnlyField(source='secret') groups = serializers.SlugRelatedField(slug_field='name', many=True, queryset=Group.objects.all(), required=False) _links = HALLinksField( nested_endpoints=['regen_secret', 'change_password'], view_name='users:munchuser-detail') class Meta: model = MunchUser fields = [ 'url', 'identifier', 'first_name', 'last_name', 'groups', 'api_key', '_links' ] extra_kwargs = {'url': {'view_name': 'users:munchuser-detail'}}
class MailSerializer(serializers.HyperlinkedModelSerializer): last_status = NestedMailStatusSerializer(read_only=True) tracking = NestedTrackingSummarySerializer(read_only=True, source='tracking_info') message = CachedScopedHyperLinkedRelatedField( view_name='campaigns:message-detail', queryset=Message.objects.all()) # TODO: Apiv2 rename to = serializers.EmailField(source='recipient') _links = HALLinksField(nested_endpoints=['optout', 'status_log'], view_name='campaigns:recipient-detail') # TODO: Apiv2 rename date = serializers.DateTimeField(source='creation_date', read_only=True) delivery_status = serializers.CharField(source='curstatus', read_only=True) class Meta: model = Mail fields = [ 'url', 'to', 'date', 'last_status', 'message', 'tracking', 'delivery_status', 'source_type', 'source_ref', 'properties', '_links' ] read_only_fields = ['date', 'delivery_status'] list_serializer_class = MailListSerializer extra_kwargs = {'url': {'view_name': 'campaigns:recipient-detail'}} def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) extra_fields = ['last_status', 'tracking'] request = kwargs['context'].get('request') if request and request.method == 'GET': for field in request.GET.get('extra_fields', '').split(','): if field in extra_fields: extra_fields.remove(field) for field in extra_fields: self.fields.pop(field) def validate_message(self, value): if value.status in [Message.SENDING, Message.SENT]: raise ValidationError( _("Cannot add this recipient to a " "sending or already sent message.")) return value
class MailSerializer(serializers.HyperlinkedModelSerializer): last_status = MailStatusSerializer(read_only=True) tracking = NestedTrackingSummarySerializer(read_only=True, source='tracking_info') _links = HALLinksField(nested_endpoints=['statuses'], view_name='transactional:mail-detail') class Meta: model = Mail fields = [ 'url', 'identifier', 'sender', 'creation_date', 'last_status', 'tracking', 'batch', 'track_open', 'track_clicks', '_links' ] extra_kwargs = { 'url': { 'view_name': 'transactional:mail-detail' }, 'batch': { 'view_name': 'transactional:batch-detail' } }
class SmtpApplicationSerializer(serializers.HyperlinkedModelSerializer): _links = HALLinksField(nested_endpoints=['regen_credentials'], view_name='users:applications-smtp-detail') class Meta: model = SmtpApplication fields = ['url', 'username', 'secret', 'identifier', '_links'] read_only_fields = ['username', 'secret'] extra_kwargs = {'url': {'view_name': 'users:applications-smtp-detail'}} def validate(self, attrs): identifier = attrs.get('identifier') try: application = SmtpApplication.objects.get( author=self.context.get('request').user, identifier=identifier) except SmtpApplication.DoesNotExist: return attrs if self.instance and application.id == self.instance.id: return attrs else: raise ValidationError( _('You already own an smtp application with this identifier'))
class MessageAttachmentSerializer(serializers.HyperlinkedModelSerializer): file = serializers.FileField(validators=[validate_no_virus], write_only=True) _links = HALLinksField(nested_endpoints=['download', 'content'], view_name='campaigns:messageattachment-detail') def validate(self, attrs): # this is defined as a pre_save because cant be checked in clean() as # there is not yet instance.message at this step. same_named = attrs['message'].attachments.filter( file__endswith='/{}'.format(attrs['file'].name)) if same_named.exists(): raise ValidationError( ('A file with the same name already exists ' 'for this message : {}').format(same_named.first())) # Now check whether we would fit within the total allowed # attachment size for the message total_size = 0 for attachment in attrs['message'].attachments.all(): total_size += attachment.size total_size += attrs['file'].size if total_size > settings.CAMPAIGNS['ATTACHMENT_TOTAL_MAX_SIZE']: raise ValidationError( _('Can not accept this file. Total attachment size for this ' 'message ({} bytes) would exceed the maximum allowed size of ' '{} bytes'.format( total_size, settings.CAMPAIGNS['ATTACHMENT_TOTAL_MAX_SIZE']))) return attrs def validate_file(self, value): # Check this attachment's size to make sure it's not too big if value.size > settings.CAMPAIGNS['ATTACHMENT_MAX_SIZE']: raise ValidationError( _('This file is too big. Maximum allowed size is ' '{} bytes'.format( settings.CAMPAIGNS['ATTACHMENT_MAX_SIZE']))) if len(value.name) > 100: raise ValidationError( _('This file name is too long. It must be under 100 characters' )) return value def create(self, validated_data): validated_data.update({'original_name': validated_data['file'].name}) return super().create(validated_data) class Meta: model = MessageAttachment fields = ('url', 'message', 'file', 'filename', 'size', 'size_in_mail', '_links') readonly_fields = ('filename', 'size', 'size_in_mail') extra_kwargs = { 'url': { 'view_name': 'campaigns:messageattachment-detail' }, 'message': { 'view_name': 'campaigns:message-detail' } }
class MessageSerializer(ForwardValidationErrorsMixin, serializers.HyperlinkedModelSerializer): completion_date = serializers.ReadOnlyField() creation_date = serializers.ReadOnlyField() msg_issue = serializers.ReadOnlyField() is_spam = serializers.ReadOnlyField() spam_score = serializers.ReadOnlyField() recipient_count = serializers.ReadOnlyField(source='mails.count') category = ScopedHyperLinkedRelatedField(view_name='core:category-detail', queryset=Category.objects.all(), required=False, allow_null=True) _links = HALLinksField(nested_endpoints=[ 'preview_send', 'preview', 'preview.html', 'preview.txt', 'recipients', 'bounces', 'opt_outs', 'stats', 'attachments' ], endpoints={ 'archive_url': { 'view_name': 'message_web_view', 'lookup_field': 'identifier', 'lookup_key': 'identifier' } }, view_name='campaigns:message-detail') def validate(self, attrs): """ http://www.django-rest-framework.org/topics/3.0-announcement/#differences-between-modelserializer-validation-and-modelform """ request = self.context.get('request', None) # Re-use model validation logic instance = Message(author=request.user, **attrs) try: instance.clean() if 'html' in attrs: instance.validate_html() except DjangoValidationError as err: message = {} for field, errors in err.message_dict.items(): if not isinstance(errors, list): errors = [errors] message[field] = errors raise ValidationError(message) return attrs class Meta: model = Message read_only_fields = ['send_date'] fields = ( 'url', 'id', 'name', 'sender_email', 'sender_name', 'subject', 'html', 'status', 'category', 'recipient_count', 'properties', 'creation_date', 'send_date', 'completion_date', 'track_open', 'track_clicks', 'external_optout', 'detach_images', 'spam_score', 'spam_details', 'is_spam', 'msg_issue', '_links', ) extra_kwargs = {'url': {'view_name': 'campaigns:message-detail'}}