Skip to content

Commit

Permalink
Add Django Rest Framework contrib package
Browse files Browse the repository at this point in the history
  • Loading branch information
ferndot committed Mar 13, 2019
1 parent 59a1636 commit bfacaa7
Show file tree
Hide file tree
Showing 11 changed files with 344 additions and 1 deletion.
14 changes: 14 additions & 0 deletions Pipfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]
tox = "*"
coverage = "*"
djangorestframework = "*"

[packages]

[requires]
python_version = "3.7"
116 changes: 116 additions & 0 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Empty file.
Empty file.
14 changes: 14 additions & 0 deletions simple_email_confirmation/contrib/rest_framework/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from rest_framework import permissions


class EmailViewSetPermission(permissions.IsAuthenticated):
"""
The request is authenticated as a user, or is a request to the
confirm endpoint.
"""

def has_permission(self, request, view):
if view.action == 'confirm':
return True

return super().has_permission(request, view)
20 changes: 20 additions & 0 deletions simple_email_confirmation/contrib/rest_framework/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from rest_framework import serializers

from simple_email_confirmation import get_email_address_model


class EmailAddressSerializer(serializers.ModelSerializer):

class Meta:
model = get_email_address_model()
fields = (
'pk',
'email',
'is_confirmed',
'is_primary',
)


class ConfirmEmailAddressSerializer(serializers.Serializer):
id = serializers.UUIDField()
code = serializers.CharField()
12 changes: 12 additions & 0 deletions simple_email_confirmation/contrib/rest_framework/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from rest_framework.routers import SimpleRouter

from . import views


router = SimpleRouter()

router.register(
r'auth/emails',
views.EmailViewSet,
basename='user-emails',
)
25 changes: 25 additions & 0 deletions simple_email_confirmation/contrib/rest_framework/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
class CurrentUserMixin:
def get_queryset(self):
queryset = super().get_queryset()

# Get configuration
current_user = self.request.user
user_field = self.user_field

return queryset.filter(**{
user_field: current_user
})


class ActionSerializerMixin:
action_serializers = {}

def get_action_serializers(self):
return self.action_serializers

def get_serializer_class(self):
if self.action in self.get_action_serializers():
return self.action_serializers.get(self.action, None)

else:
return super().get_serializer_class()
134 changes: 134 additions & 0 deletions simple_email_confirmation/contrib/rest_framework/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
from django.db import IntegrityError
from django.shortcuts import get_object_or_404
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response

from simple_email_confirmation import get_email_address_model
from simple_email_confirmation.exceptions import (
EmailConfirmationExpired, EmailIsPrimary, EmailNotConfirmed,
)

from . import permissions, serializers, utils


class EmailViewSet(
utils.ActionSerializerMixin,
utils.CurrentUserMixin,
viewsets.ModelViewSet,
):
email_address_model = get_email_address_model()
queryset = email_address_model.objects.all()
user_field = 'user'
permission_classes = [permissions.EmailViewSetPermission]

serializer_class = serializers.EmailAddressSerializer
action_serializers = {
'send_confirmation': None,
'confirm': serializers.ConfirmEmailAddressSerializer,
'set_primary': None,
}

def create(self, request, *args, **kwargs):
try:
# Add the new unconfirmed email
request.user.add_unconfirmed_email(request.data['email'])

# Retrieve the new email
new_email = request.user.email_address_set.filter(
email=request.data['email']
).first()

# Return serialized email
serializer = self.get_serializer(new_email)
return Response(serializer.data)

except IntegrityError:
return Response(
{'errors': ['Email already exists']},
status=status.HTTP_400_BAD_REQUEST,
)

def destroy(self, request, *args, **kwargs):
email = self.get_object()

try:
# Delete the email
request.user.remove_email(email.email)

return Response({'status': 'OK'})

except EmailIsPrimary:
return Response(
{'errors': ['Cannot delete the primary email']},
status=status.HTTP_400_BAD_REQUEST,
)

@action(methods=['post'], detail=True)
def send_confirmation(self, request, pk=None):
"""
Send a confirmation email.
"""

# Retrive email
email = self.get_object()

# Confirm email
email.user.reset_email_confirmation(email.email)
email.user.send_confirmation_email(email.email)

# Success!
return Response({'status': 'OK'})

@action(methods=['post'], detail=False)
def confirm(self, request):
"""
Confirm an email.
"""

try:
# Parse data
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)

# Retrieve email
email = get_object_or_404(
self.email_address_model,
pk=serializer.data['id'],
)

# Retrieve user
email.user.confirm_email(serializer.data['code'])

# Success!
return Response({'status': 'OK'})

except EmailConfirmationExpired:
return Response(
{'errors': ['Confirmation key is not valid']},
status=status.HTTP_400_BAD_REQUEST,
)

@action(methods=['post'], detail=True)
def set_primary(self, request, pk=None):
"""
Set an email as primary.
"""

# Retrive email
email = self.get_object()

try:
# Set email as primary
email.user.set_primary_email(email.email)

# Success!
return Response({'status': 'OK'})

except EmailNotConfirmed:
return Response(
{'errors': [
'Email must be confirmed before it can be set as primary'
]},
status=status.HTTP_400_BAD_REQUEST,
)
9 changes: 8 additions & 1 deletion simple_email_confirmation/tests/myproject/urls.py
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
urlpatterns = []
from django.urls import path, include

from simple_email_confirmation.contrib.rest_framework.urls import router


urlpatterns = [
path('api/', include(router.urls)),
]
1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ commands =
deps =
coverage
django<1.12
djangorestframework

0 comments on commit bfacaa7

Please sign in to comment.