Skip to content

Commit 00107c2

Browse files
committed
Add full CRUD example for BaseUser
1 parent d726f7b commit 00107c2

File tree

4 files changed

+91
-5
lines changed

4 files changed

+91
-5
lines changed

styleguide_example/users/apis.py

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,35 @@
1+
from django.shortcuts import Http404
12
from rest_framework import serializers
3+
from rest_framework.response import Response
24
from rest_framework.views import APIView
35

46
from styleguide_example.api.pagination import (
57
LimitOffsetPagination,
68
get_paginated_response,
79
)
810
from styleguide_example.users.models import BaseUser
9-
from styleguide_example.users.selectors import user_list
10-
11+
from styleguide_example.users.selectors import user_get, user_list
12+
from styleguide_example.users.services import user_create, user_update
1113

1214
# TODO: When JWT is resolved, add authenticated version
15+
16+
17+
class UserDetailApi(APIView):
18+
class OutputSerializer(serializers.Serializer):
19+
id = serializers.IntegerField()
20+
email = serializers.CharField()
21+
22+
def get(self, request, user_id):
23+
user = user_get(user_id)
24+
25+
if user is None:
26+
raise Http404
27+
28+
data = self.OutputSerializer(user).data
29+
30+
return Response(data)
31+
32+
1333
class UserListApi(APIView):
1434
class Pagination(LimitOffsetPagination):
1535
default_limit = 1
@@ -38,3 +58,51 @@ def get(self, request):
3858
request=request,
3959
view=self,
4060
)
61+
62+
63+
class UserCreateApi(APIView):
64+
class InputSerializer(serializers.Serializer):
65+
email = serializers.EmailField()
66+
password = serializers.CharField()
67+
68+
def post(self, request):
69+
serializer = self.InputSerializer(data=request.data)
70+
serializer.is_valid(raise_exception=True)
71+
72+
user = user_create(
73+
**serializer.validated_data
74+
)
75+
76+
# Note: This shows a possible reusability for serializers between APIs
77+
# Usually, this is how we approach things, when building APIs at first
78+
# But at the very moment when we need to make a change to the output,
79+
# that's specific to this API, we'll introduce a separate OutputSerializer just for this API
80+
data = UserDetailApi.OutputSerializer(user).data
81+
82+
return Response(data)
83+
84+
85+
class UserUpdateApi(APIView):
86+
class InputSerializer(serializers.Serializer):
87+
# Note: Currently, those are not actual user fields, but rather an example
88+
first_name = serializers.CharField(required=True)
89+
last_name = serializers.CharField(required=True)
90+
91+
def post(self, request, user_id):
92+
serializer = self.InputSerializer(data=request.data)
93+
serializer.is_valid(raise_exception=True)
94+
95+
user = user_get(user_id)
96+
97+
if user is None:
98+
raise Http404
99+
100+
user = user_update(user=user, data=serializer.validated_data)
101+
102+
# Note: This shows a possible reusability for serializers between APIs
103+
# Usually, this is how we approach things, when building APIs at first
104+
# But at the very moment when we need to make a change to the output,
105+
# that's specific to this API, we'll introduce a separate OutputSerializer just for this API
106+
data = UserDetailApi.OutputSerializer(user).data
107+
108+
return Response(data)

styleguide_example/users/selectors.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
from typing import Optional
2+
13
from django.db.models.query import QuerySet
24

5+
from styleguide_example.common.utils import get_object
36
from styleguide_example.users.filters import BaseUserFilter
47
from styleguide_example.users.models import BaseUser
58

@@ -20,3 +23,9 @@ def user_list(*, filters=None) -> QuerySet[BaseUser]:
2023
qs = BaseUser.objects.all()
2124

2225
return BaseUserFilter(filters, qs).qs
26+
27+
28+
def user_get(user_id) -> Optional[BaseUser]:
29+
user = get_object(BaseUser, id=user_id)
30+
31+
return user

styleguide_example/users/services.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from styleguide_example.users.models import BaseUser
77

88

9+
@transaction.atomic
910
def user_create(
1011
*, email: str, is_active: bool = True, is_admin: bool = False, password: Optional[str] = None
1112
) -> BaseUser:
@@ -16,7 +17,10 @@ def user_create(
1617

1718
@transaction.atomic
1819
def user_update(*, user: BaseUser, data) -> BaseUser:
19-
non_side_effect_fields = ["first_name", "last_name"]
20+
non_side_effect_fields = [
21+
# "first_name",
22+
# "last_name"
23+
]
2024

2125
user, has_updated = model_update(instance=user, fields=non_side_effect_fields, data=data)
2226

styleguide_example/users/urls.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
from django.urls import path
22

3-
from .apis import UserListApi
3+
from .apis import UserCreateApi, UserDetailApi, UserListApi, UserUpdateApi
44

5-
urlpatterns = [path("", UserListApi.as_view(), name="list")]
5+
urlpatterns = [
6+
path("", UserListApi.as_view(), name="list"),
7+
path("create/", UserCreateApi.as_view(), name="create"),
8+
path("<int:user_id>/", UserDetailApi.as_view(), name="detail"),
9+
path("<int:user_id>/update/", UserUpdateApi.as_view(), name="update"),
10+
]

0 commit comments

Comments
 (0)