diff --git a/freekake_api/content/models.py b/freekake_api/content/models.py index 71a8362..a31ab32 100644 --- a/freekake_api/content/models.py +++ b/freekake_api/content/models.py @@ -1,3 +1,60 @@ from django.db import models +from django_softdelete.models import SoftDeleteModel -# Create your models here. +class ContentTheme(SoftDeleteModel): + theme = models.CharField(max_length=255, null=False) + description = models.CharField(max_length=1000) + + featured_image = models.CharField(max_length=1000) + + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.theme + +class ContentTopic(SoftDeleteModel): + topic = models.CharField(max_length=255, null=False) + description = models.CharField(max_length=1000) + + featured_image = models.CharField(max_length=1000) + + theme = models.ForeignKey(ContentTheme, on_delete=models.CASCADE, null=False) + + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.topic + +class ContentFormat(SoftDeleteModel): + format = models.CharField(max_length=255, null=False) + + featured_image = models.CharField(max_length=1000) + + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.format + +class Content(SoftDeleteModel): + title = models.CharField(max_length=255, null=False) + slug = models.SlugField(max_length=255) + + featured_image = models.CharField(max_length=1000) + + theme = models.ForeignKey(ContentTheme, on_delete=models.CASCADE, null=False) + topic = models.ForeignKey(ContentTopic, on_delete=models.CASCADE, null=False) + format = models.ForeignKey(ContentFormat, on_delete=models.CASCADE, null=False) + + content = models.TextField() + + point = models.IntegerField() + coin = models.IntegerField() + + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.title \ No newline at end of file diff --git a/freekake_api/content/serializers.py b/freekake_api/content/serializers.py new file mode 100644 index 0000000..24a9790 --- /dev/null +++ b/freekake_api/content/serializers.py @@ -0,0 +1,40 @@ +from rest_framework import serializers +from .models import Content, ContentTheme, ContentTopic, ContentFormat + + +class ContentThemeSerializer(serializers.ModelSerializer): + class Meta: + model = ContentTheme + fields = ['id', 'theme', 'description', 'featured_image'] + +class ContentTopicSerializer(serializers.ModelSerializer): + + class Meta: + model = ContentTopic + fields = ['id', 'topic', 'description', 'featured_image', 'theme'] + +class ContentTopicDetailSerializer(serializers.ModelSerializer): + theme = ContentThemeSerializer(read_only=True) + + class Meta: + model = ContentTopic + fields = ['id', 'topic', 'description', 'featured_image', 'theme'] + +class ContentFormatSerializer(serializers.ModelSerializer): + class Meta: + model = ContentFormat + fields = ['id', 'format', 'featured_image', 'created_at', 'updated_at'] + +class ContentSerializer(serializers.ModelSerializer): + class Meta: + model = Content + fields = ['id', 'title', 'slug', 'featured_image', 'theme', 'topic', 'format', 'content', 'point', 'coin'] + +class ContentDetailSerializer(serializers.ModelSerializer): + theme = ContentThemeSerializer(read_only=True) + topic = ContentTopicDetailSerializer(read_only=True) + format = ContentFormatSerializer(read_only=True) + + class Meta: + model = Content + fields = ['id', 'title', 'slug', 'featured_image', 'theme', 'topic', 'format', 'content', 'point', 'coin'] diff --git a/freekake_api/content/urls.py b/freekake_api/content/urls.py new file mode 100644 index 0000000..72503d1 --- /dev/null +++ b/freekake_api/content/urls.py @@ -0,0 +1,16 @@ +from django.urls import path +from .views import ContentList, ContentDetail, ContentThemeList, ContentFormatList, ContentTopicList + +urlpatterns = [ + path('contents/', ContentList.as_view()), + path('contents//', ContentDetail.as_view()), + + path('themes/', ContentThemeList.as_view()), + path('themes//', ContentThemeList.as_view()), + + path('formats/', ContentFormatList.as_view()), + path('formats//', ContentFormatList.as_view()), + + path('topics/', ContentTopicList.as_view()), + path('topics//', ContentTopicList.as_view()), +] diff --git a/freekake_api/content/views.py b/freekake_api/content/views.py index 91ea44a..39c0b11 100644 --- a/freekake_api/content/views.py +++ b/freekake_api/content/views.py @@ -1,3 +1,51 @@ from django.shortcuts import render +from rest_framework import generics, filters +from django_filters.rest_framework import DjangoFilterBackend -# Create your views here. +from content import models, serializers + +class ContentList(generics.ListCreateAPIView): + queryset = models.Content.objects.all() + serializer_class = serializers.ContentSerializer + filter_backends = [filters.SearchFilter, filters.OrderingFilter, DjangoFilterBackend] + search_fields = ['title'] + filterset_fields = ['format', 'theme', 'topic'] + ordering_fields = '__all__' + +class ContentDetail(generics.RetrieveUpdateDestroyAPIView): + queryset = models.Content.objects.all() + serializer_class = serializers.ContentSerializer + +class ContentThemeList(generics.ListCreateAPIView): + queryset = models.ContentTheme.objects.all() + serializer_class = serializers.ContentThemeSerializer + filter_backends = [filters.SearchFilter, filters.OrderingFilter] + search_fields = ['theme'] + ordering_fields = '__all__' + +class ContentThemeDetail(generics.RetrieveUpdateDestroyAPIView): + queryset = models.ContentTheme.objects.all() + serializer_class = serializers.ContentThemeSerializer + +class ContentTopicList(generics.ListCreateAPIView): + queryset = models.ContentTopic.objects.all() + serializer_class = serializers.ContentTopicSerializer + filter_backends = [filters.SearchFilter, filters.OrderingFilter, DjangoFilterBackend] + search_fields = ['topic'] + filterset_fields = ['theme'] + ordering_fields = '__all__' + +class ContentTopicDetail(generics.RetrieveUpdateDestroyAPIView): + queryset = models.ContentTopic.objects.all() + serializer_class = serializers.ContentTopicSerializer + +class ContentFormatList(generics.ListCreateAPIView): + queryset = models.ContentFormat.objects.all() + serializer_class = serializers.ContentFormatSerializer + filter_backends = [filters.SearchFilter, filters.OrderingFilter] + search_fields = ['format'] + ordering_fields = '__all__' + +class ContentFormatDetail(generics.RetrieveUpdateDestroyAPIView): + queryset = models.ContentFormat.objects.all() + serializer_class = serializers.ContentFormatSerializer diff --git a/freekake_api/core/models.py b/freekake_api/core/models.py index bffc6cf..44c1f78 100644 --- a/freekake_api/core/models.py +++ b/freekake_api/core/models.py @@ -1,9 +1,13 @@ from django.db import models +from django_softdelete.models import SoftDeleteModel -class Province(models.Model): +class Province(SoftDeleteModel): code = models.CharField(max_length=2, null=False) name = models.CharField(max_length=255, null=False) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + def __str__(self): return self.name diff --git a/freekake_api/core/views.py b/freekake_api/core/views.py index 8c4f72a..384cd45 100644 --- a/freekake_api/core/views.py +++ b/freekake_api/core/views.py @@ -1,5 +1,6 @@ from django.shortcuts import render -from rest_framework import generics +from rest_framework import generics, filters +from django_filters.rest_framework import DjangoFilterBackend from core import models, serializers @@ -8,6 +9,7 @@ from core import models, serializers class ProvinceList(generics.ListCreateAPIView): queryset = models.Province.objects.all() serializer_class = serializers.ProvinceSerializer + filter_backends = [filters.SearchFilter, filters.OrderingFilter, DjangoFilterBackend] search_fields = ['name', 'code'] filterset_fields = ['name', 'code'] ordering_fields = '__all__' @@ -21,8 +23,9 @@ class ProvinceDetail(generics.RetrieveUpdateDestroyAPIView): class RegencyCityList(generics.ListCreateAPIView): queryset = models.RegencyCity.objects.all() serializer_class = serializers.RegencyCitySerializer + filter_backends = [filters.SearchFilter, filters.OrderingFilter, DjangoFilterBackend] search_fields = ['name', 'code'] - filterset_fields = ['name', 'code'] + filterset_fields = ['name', 'code', 'province'] ordering_fields = '__all__' def get_serializer_class(self): @@ -50,8 +53,9 @@ class RegencyCityDetail(generics.RetrieveUpdateDestroyAPIView): class DistrictList(generics.ListCreateAPIView): queryset = models.District.objects.all() serializer_class = serializers.DistrictSerializer + filter_backends = [filters.SearchFilter, filters.OrderingFilter, DjangoFilterBackend] search_fields = ['name', 'code'] - filterset_fields = ['name', 'code'] + filterset_fields = ['name', 'code', 'regency_city'] ordering_fields = '__all__' def get_serializer_class(self): diff --git a/freekake_api/freekake_api/pagination.py b/freekake_api/freekake_api/pagination.py new file mode 100644 index 0000000..15905b5 --- /dev/null +++ b/freekake_api/freekake_api/pagination.py @@ -0,0 +1,23 @@ +from rest_framework.pagination import PageNumberPagination +from rest_framework.response import Response + + +DEFAULT_PAGE = 1 +DEFAULT_PAGE_SIZE = 10 + +class CustomPagination(PageNumberPagination): + page = DEFAULT_PAGE + page_size = DEFAULT_PAGE_SIZE + page_size_query_param = 'page_size' + + # def get_paginated_response(self, data): + # return Response({ + # 'links': { + # 'next': self.get_next_link(), + # 'previous': self.get_previous_link() + # }, + # 'total': self.page.paginator.count, + # 'page': int(self.request.GET.get('page', DEFAULT_PAGE)), # can not set default = self.page + # 'page_size': int(self.request.GET.get('page_size', self.page_size)), + # 'results': data + # }) \ No newline at end of file diff --git a/freekake_api/freekake_api/settings.py b/freekake_api/freekake_api/settings.py index 1d6e8df..06a4c8e 100644 --- a/freekake_api/freekake_api/settings.py +++ b/freekake_api/freekake_api/settings.py @@ -38,9 +38,11 @@ INSTALLED_APPS = [ 'django.contrib.messages', 'django.contrib.staticfiles', + 'oauth2_provider', 'rest_framework', 'psycopg2', 'corsheaders', + 'django_filters', 'core', 'content', @@ -137,11 +139,32 @@ STATIC_URL = 'static/' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' CORS_ALLOWED_ORIGINS = [ + "http://localhost:3000", "http://localhost:5173", "http://127.0.0.1:5173", ] REST_FRAMEWORK = { - 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', - 'PAGE_SIZE': 10 -} \ No newline at end of file + # 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', + # 'PAGE_SIZE': 10, + 'DEFAULT_PAGINATION_CLASS': + 'freekake_api.pagination.CustomPagination', + 'DEFAULT_FILTER_BACKENDS': [ + 'django_filters.rest_framework.DjangoFilterBackend' + ], + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework.authentication.BasicAuthentication', + 'rest_framework.authentication.SessionAuthentication', + 'oauth2_provider.contrib.rest_framework.OAuth2Authentication', + ), + 'DEFAULT_PERMISSION_CLASSES': ( + 'rest_framework.permissions.IsAuthenticated', + ) +} + +OAUTH2_PROVIDER = { + # this is the list of available scopes + 'SCOPES': {'read': 'Read scope', 'write': 'Write scope', 'groups': 'Access to your groups'} +} + +LOGIN_URL = '/admin/login/' \ No newline at end of file diff --git a/freekake_api/freekake_api/urls.py b/freekake_api/freekake_api/urls.py index f82a506..75ff711 100644 --- a/freekake_api/freekake_api/urls.py +++ b/freekake_api/freekake_api/urls.py @@ -16,9 +16,13 @@ Including another URLconf """ from django.contrib import admin from django.urls import path, include +from oauth2_provider import urls as oauth2_urls urlpatterns = [ # path('admin/', admin.site.urls), - + path('admin/', admin.site.urls), + path('oauth/', include(oauth2_urls)), + path('core/', include('core.urls')), + path('content/', include('content.urls')), ] diff --git a/freekake_api/manage.py b/freekake_api/manage.py old mode 100755 new mode 100644