From 7beffd299d2ab2e6e163bcb7701a714ac8fe2702 Mon Sep 17 00:00:00 2001 From: Nikolai R Kristiansen Date: Sun, 18 Jan 2015 17:30:42 +0100 Subject: [PATCH] Don not allow duplicate aliases, lowercase some fields --- README.md | 8 -------- dpdapi/migrations/0002_auto_20150118_1638.py | 18 ++++++++++++++++++ dpdapi/models.py | 17 +++++++++++++++++ dpdapi/serializers.py | 6 ++++++ dpdapi/views.py | 17 +++++++++-------- 5 files changed, 50 insertions(+), 16 deletions(-) create mode 100644 dpdapi/migrations/0002_auto_20150118_1638.py diff --git a/README.md b/README.md index 0dc7b27..898c8e8 100644 --- a/README.md +++ b/README.md @@ -18,14 +18,6 @@ Then you can do fab deploy - -# TODO -- Alias should only allow unique (source,destination) tuples -- lowercase domain, alias and user on save - -# TODO later -- Create domain dir for dovecot? - # Inspiration: - http://modoboa.readthedocs.org/ - https://github.com/lgunsch/django-vmail diff --git a/dpdapi/migrations/0002_auto_20150118_1638.py b/dpdapi/migrations/0002_auto_20150118_1638.py new file mode 100644 index 0000000..ddeaf07 --- /dev/null +++ b/dpdapi/migrations/0002_auto_20150118_1638.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('dpdapi', '0001_initial'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='alias', + unique_together=set([('source', 'destination', 'domain')]), + ), + ] diff --git a/dpdapi/models.py b/dpdapi/models.py index 1410aa3..c58f369 100644 --- a/dpdapi/models.py +++ b/dpdapi/models.py @@ -19,8 +19,15 @@ class Alias(BaseModel): def __unicode__(self): return "{}={}".format(self.source, self.destination) + def save(self, *args, **kwargs): + self.source = self.source.lower() + self.destination = self.destination.lower() + + super(Alias, self).save(*args, **kwargs) + class Meta: verbose_name_plural = 'aliases' + unique_together = ('source', 'destination', 'domain') class Domain(BaseModel): @@ -29,6 +36,11 @@ class Domain(BaseModel): def __unicode__(self): return self.name + def save(self, *args, **kwargs): + self.name = self.name.lower() + + super(Domain, self).save(*args, **kwargs) + class User(BaseModel): email = models.EmailField(max_length=254, unique=True) @@ -38,3 +50,8 @@ class User(BaseModel): def __unicode__(self): return self.email + def save(self, *args, **kwargs): + self.email = self.email.lower() + + super(User, self).save(*args, **kwargs) + diff --git a/dpdapi/serializers.py b/dpdapi/serializers.py index 2215ddf..a10de99 100644 --- a/dpdapi/serializers.py +++ b/dpdapi/serializers.py @@ -8,6 +8,12 @@ class AliasSerializer(serializers.ModelSerializer): model = Alias +class AliasDeleteSerializer(serializers.ModelSerializer): + class Meta: + model = Alias + validators = [] # No Unique togheter check on deletion + + class DomainSerializer(serializers.ModelSerializer): class Meta: model = Domain diff --git a/dpdapi/views.py b/dpdapi/views.py index 7d3f9dc..55fb926 100644 --- a/dpdapi/views.py +++ b/dpdapi/views.py @@ -9,7 +9,7 @@ from rest_framework import status, filters, viewsets from dpdapi.filters import AliasRegexFilterBackend from dpdapi.models import Alias, Domain, User -from dpdapi.serializers import AliasSerializer, DomainSerializer, UserSerializer +from dpdapi.serializers import AliasSerializer, AliasDeleteSerializer, DomainSerializer, UserSerializer logger = logging.getLogger(__name__) @@ -26,16 +26,17 @@ class AliasViewSet(viewsets.ModelViewSet): @list_route(methods=['post']) def create_bulk(self, request): serializer = AliasSerializer(data=request.data, many=True) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status=status.HTTP_201_CREATED) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + serializer.is_valid(raise_exception=True) + + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) @list_route(methods=['delete']) def delete_bulk(self, request): - serializer = AliasSerializer(data=request.data, many=True) - if not serializer.is_valid(): - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + serializer = AliasDeleteSerializer(data=request.data, many=True) + + serializer.is_valid(raise_exception=True) # Ref: http://michelepasin.org/blog/2010/07/20/the-power-of-djangos-q-objects/ q_list = map(lambda x: Q(**x), serializer.initial_data)