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)