From 548417612fb98ed8d23253ac7dd29ce96f47a64a Mon Sep 17 00:00:00 2001 From: Jerome Caisip Date: Fri, 13 Sep 2019 14:43:12 +0800 Subject: [PATCH 1/3] Additonal option to setup DRF. --- CONTRIBUTORS.rst | 2 ++ cookiecutter.json | 1 + hooks/post_gen_project.py | 8 +++++++ .../config/api_router.py | 14 +++++++++++ .../config/settings/base.py | 16 +++++++++++-- {{cookiecutter.project_slug}}/config/urls.py | 11 +++++++++ .../users/api/serializers.py | 12 ++++++++++ .../users/api/views.py | 24 +++++++++++++++++++ 8 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 {{cookiecutter.project_slug}}/config/api_router.py create mode 100644 {{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/api/serializers.py create mode 100644 {{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/api/views.py diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index 4c3ad8a0..f1981cbd 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -120,6 +120,7 @@ Listed in alphabetical order. Irfan Ahmad `@erfaan`_ @erfaan Jan Van Bruggen `@jvanbrug`_ Jelmer Draaijer `@foarsitter`_ + Jerome Caisip `@jeromecaisip`_ Jens Nilsson `@phiberjenz`_ Jerome Leclanche `@jleclanche`_ @Adys Jimmy Gitonga `@afrowave`_ @afrowave @@ -276,6 +277,7 @@ Listed in alphabetical order. .. _@jangeador: https://github.com/jangeador .. _@jazztpt: https://github.com/jazztpt .. _@jcass77: https://github.com/jcass77 +.. _@jeromecaisip: https://github.com/jeromecaisip .. _@jleclanche: https://github.com/jleclanche .. _@juliocc: https://github.com/juliocc .. _@jvanbrug: https://github.com/jvanbrug diff --git a/cookiecutter.json b/cookiecutter.json index d6d217ca..5e70bedb 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -33,6 +33,7 @@ "GCP", "None" ], + "use_drf": "n", "custom_bootstrap_compilation": "n", "use_compressor": "n", "use_celery": "n", diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index ff84f180..42d5dcee 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -279,6 +279,11 @@ def remove_node_dockerfile(): shutil.rmtree(os.path.join("compose", "local", "node")) +def remove_drf_starter_files(): + os.remove(os.path.join("config", "api_router.py")) + shutil.rmtree(os.path.join("{{cookiecutter.project_slug}}", "users", "api")) + + def main(): debug = "{{ cookiecutter.debug }}".lower() == "y" @@ -342,6 +347,9 @@ def main(): if "{{ cookiecutter.use_travisci }}".lower() == "n": remove_dottravisyml_file() + if "{{ cookiecutter.use_drf }}".lower() == "n": + remove_drf_starter_files() + print(SUCCESS + "Project initialized, keep up the good work!" + TERMINATOR) diff --git a/{{cookiecutter.project_slug}}/config/api_router.py b/{{cookiecutter.project_slug}}/config/api_router.py new file mode 100644 index 00000000..fc8107b4 --- /dev/null +++ b/{{cookiecutter.project_slug}}/config/api_router.py @@ -0,0 +1,14 @@ +from rest_framework.routers import DefaultRouter, SimpleRouter +from django.conf import settings +from {{ cookiecutter.project_slug }}.users.api.views import UserViewSet + +if settings.DEBUG: + router = DefaultRouter() +else: + router = SimpleRouter() + +router.register('users', UserViewSet) + + +app_name = 'api' +urlpatterns = router.urls diff --git a/{{cookiecutter.project_slug}}/config/settings/base.py b/{{cookiecutter.project_slug}}/config/settings/base.py index 41f0f46c..a6081950 100644 --- a/{{cookiecutter.project_slug}}/config/settings/base.py +++ b/{{cookiecutter.project_slug}}/config/settings/base.py @@ -292,14 +292,26 @@ ACCOUNT_EMAIL_VERIFICATION = "mandatory" ACCOUNT_ADAPTER = "{{cookiecutter.project_slug}}.users.adapters.AccountAdapter" # https://django-allauth.readthedocs.io/en/latest/configuration.html SOCIALACCOUNT_ADAPTER = "{{cookiecutter.project_slug}}.users.adapters.SocialAccountAdapter" - {% if cookiecutter.use_compressor == 'y' -%} # django-compressor # ------------------------------------------------------------------------------ # https://django-compressor.readthedocs.io/en/latest/quickstart/#installation INSTALLED_APPS += ["compressor"] STATICFILES_FINDERS += ["compressor.finders.CompressorFinder"] - +{%- endif %} +{% if cookiecutter.use_drf == "y" -%} +# django-reset-framework +# ------------------------------------------------------------------------------- +# django-rest-framework - https://www.django-rest-framework.org/api-guide/settings/ +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework.authentication.SessionAuthentication', + 'rest_framework.authentication.TokenAuthentication', + ), + 'DEFAULT_PERMISSION_CLASSES': ( + 'rest_framework.permissions.IsAuthenticated', + ) +} {%- endif %} # Your stuff... # ------------------------------------------------------------------------------ diff --git a/{{cookiecutter.project_slug}}/config/urls.py b/{{cookiecutter.project_slug}}/config/urls.py index 909d5e86..5382bdb7 100644 --- a/{{cookiecutter.project_slug}}/config/urls.py +++ b/{{cookiecutter.project_slug}}/config/urls.py @@ -4,6 +4,9 @@ from django.conf.urls.static import static from django.contrib import admin from django.views.generic import TemplateView from django.views import defaults as default_views +{% if cookiecutter.use_drf == 'y' -%} +from rest_framework.authtoken.views import obtain_auth_token +{%- endif %} urlpatterns = [ path("", TemplateView.as_view(template_name="pages/home.html"), name="home"), @@ -17,6 +20,14 @@ urlpatterns = [ path("accounts/", include("allauth.urls")), # Your stuff: custom urls includes go here ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) +{% if cookiecutter.use_drf == 'y' -%} +# API URLS +urlpatterns += [ + # API base url + path("api/", include('config.api_router')), + # DRF auth token + path("auth-token/", obtain_auth_token)] +{%- endif %} if settings.DEBUG: # This allows the error pages to be debugged during development, just visit diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/api/serializers.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/api/serializers.py new file mode 100644 index 00000000..8fc2262e --- /dev/null +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/api/serializers.py @@ -0,0 +1,12 @@ +from rest_framework import serializers +from {{ cookiecutter.project_slug }}.users.models import User + +class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ['username', 'email', 'name', 'url'] + + extra_kwargs = { + 'url': {'view_name': 'api:user-detail', 'lookup_field': 'username'} + } + diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/api/views.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/api/views.py new file mode 100644 index 00000000..440cc9b2 --- /dev/null +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/api/views.py @@ -0,0 +1,24 @@ +from django.contrib.auth import get_user_model +from rest_framework import status +from rest_framework.decorators import action +from rest_framework.mixins import RetrieveModelMixin, ListModelMixin, UpdateModelMixin +from rest_framework.response import Response +from rest_framework.viewsets import GenericViewSet + + +from .serializers import UserSerializer +User = get_user_model() + + +class UserViewSet(RetrieveModelMixin, ListModelMixin, UpdateModelMixin, GenericViewSet): + serializer_class = UserSerializer + queryset = User.objects.all() + lookup_field = 'username' + + def get_queryset(self, *args, **kwargs): + return self.queryset.filter(id=self.request.user.id) + + @action(detail=False, methods=['GET']) + def me(self, request): + serializer = UserSerializer(request.user, context={'request': request}) + return Response(status=status.HTTP_200_OK, data=serializer.data) From 2833600aec432f9af9f4437e38a7fb2674f17335 Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Thu, 23 Jan 2020 14:43:17 +0000 Subject: [PATCH 2/3] Test generation with and without DRF --- tests/test_cookiecutter_generation.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_cookiecutter_generation.py b/tests/test_cookiecutter_generation.py index 8c2f71fe..cd23b312 100755 --- a/tests/test_cookiecutter_generation.py +++ b/tests/test_cookiecutter_generation.py @@ -33,6 +33,7 @@ def context(): @pytest.mark.parametrize("use_mailhog", ["y", "n"], ids=lambda yn: f"mailhog:{yn}") @pytest.mark.parametrize("use_sentry", ["y", "n"], ids=lambda yn: f"sentry:{yn}") @pytest.mark.parametrize("use_compressor", ["y", "n"], ids=lambda yn: f"cmpr:{yn}") +@pytest.mark.parametrize("use_drf", ["y", "n"], ids=lambda yn: f"drf:{yn}") @pytest.mark.parametrize( "use_whitenoise,cloud_provider", [ @@ -53,6 +54,7 @@ def context_combination( use_sentry, use_compressor, use_whitenoise, + use_drf, cloud_provider, ): """Fixture that parametrize the function where it's used.""" @@ -64,6 +66,7 @@ def context_combination( "use_mailhog": use_mailhog, "use_sentry": use_sentry, "use_whitenoise": use_whitenoise, + "use_drf": use_drf, "cloud_provider": cloud_provider, } From fa9a8cfe7ba3d40540d30f8f5b834541191b3f0d Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Thu, 23 Jan 2020 15:04:30 +0000 Subject: [PATCH 3/3] Fix code formatting --- {{cookiecutter.project_slug}}/config/api_router.py | 4 ++-- {{cookiecutter.project_slug}}/config/settings/base.py | 10 ++++------ {{cookiecutter.project_slug}}/config/urls.py | 5 +++-- .../users/api/serializers.py | 6 +++--- .../{{cookiecutter.project_slug}}/users/api/views.py | 8 ++++---- 5 files changed, 16 insertions(+), 17 deletions(-) diff --git a/{{cookiecutter.project_slug}}/config/api_router.py b/{{cookiecutter.project_slug}}/config/api_router.py index fc8107b4..46a797a7 100644 --- a/{{cookiecutter.project_slug}}/config/api_router.py +++ b/{{cookiecutter.project_slug}}/config/api_router.py @@ -7,8 +7,8 @@ if settings.DEBUG: else: router = SimpleRouter() -router.register('users', UserViewSet) +router.register("users", UserViewSet) -app_name = 'api' +app_name = "api" urlpatterns = router.urls diff --git a/{{cookiecutter.project_slug}}/config/settings/base.py b/{{cookiecutter.project_slug}}/config/settings/base.py index 06a3fc89..ca43eb88 100644 --- a/{{cookiecutter.project_slug}}/config/settings/base.py +++ b/{{cookiecutter.project_slug}}/config/settings/base.py @@ -308,13 +308,11 @@ STATICFILES_FINDERS += ["compressor.finders.CompressorFinder"] # ------------------------------------------------------------------------------- # django-rest-framework - https://www.django-rest-framework.org/api-guide/settings/ REST_FRAMEWORK = { - 'DEFAULT_AUTHENTICATION_CLASSES': ( - 'rest_framework.authentication.SessionAuthentication', - 'rest_framework.authentication.TokenAuthentication', + "DEFAULT_AUTHENTICATION_CLASSES": ( + "rest_framework.authentication.SessionAuthentication", + "rest_framework.authentication.TokenAuthentication", ), - 'DEFAULT_PERMISSION_CLASSES': ( - 'rest_framework.permissions.IsAuthenticated', - ) + "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",), } {%- endif %} # Your stuff... diff --git a/{{cookiecutter.project_slug}}/config/urls.py b/{{cookiecutter.project_slug}}/config/urls.py index 5382bdb7..382bf895 100644 --- a/{{cookiecutter.project_slug}}/config/urls.py +++ b/{{cookiecutter.project_slug}}/config/urls.py @@ -24,9 +24,10 @@ urlpatterns = [ # API URLS urlpatterns += [ # API base url - path("api/", include('config.api_router')), + path("api/", include("config.api_router")), # DRF auth token - path("auth-token/", obtain_auth_token)] + path("auth-token/", obtain_auth_token), +] {%- endif %} if settings.DEBUG: diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/api/serializers.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/api/serializers.py index 8fc2262e..bb52738b 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/api/serializers.py +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/api/serializers.py @@ -1,12 +1,12 @@ from rest_framework import serializers from {{ cookiecutter.project_slug }}.users.models import User + class UserSerializer(serializers.ModelSerializer): class Meta: model = User - fields = ['username', 'email', 'name', 'url'] + fields = ["username", "email", "name", "url"] extra_kwargs = { - 'url': {'view_name': 'api:user-detail', 'lookup_field': 'username'} + "url": {"view_name": "api:user-detail", "lookup_field": "username"} } - diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/api/views.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/api/views.py index 440cc9b2..7b5af999 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/api/views.py +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/api/views.py @@ -5,20 +5,20 @@ from rest_framework.mixins import RetrieveModelMixin, ListModelMixin, UpdateMode from rest_framework.response import Response from rest_framework.viewsets import GenericViewSet - from .serializers import UserSerializer + User = get_user_model() class UserViewSet(RetrieveModelMixin, ListModelMixin, UpdateModelMixin, GenericViewSet): serializer_class = UserSerializer queryset = User.objects.all() - lookup_field = 'username' + lookup_field = "username" def get_queryset(self, *args, **kwargs): return self.queryset.filter(id=self.request.user.id) - @action(detail=False, methods=['GET']) + @action(detail=False, methods=["GET"]) def me(self, request): - serializer = UserSerializer(request.user, context={'request': request}) + serializer = UserSerializer(request.user, context={"request": request}) return Response(status=status.HTTP_200_OK, data=serializer.data)