From 317b23e11d40967aaf11c7e81ab3c884f555a176 Mon Sep 17 00:00:00 2001 From: junboss <80582578+jundm@users.noreply.github.com> Date: Sat, 26 Feb 2022 09:15:21 +0900 Subject: [PATCH] Delete backend directory --- backend/.gitignore | 6 - backend/.pycrunch-config.yaml | 13 - backend/accounts/__init__.py | 0 backend/accounts/admin.py | 7 - backend/accounts/apps.py | 7 - backend/accounts/managers.py | 66 ----- backend/accounts/migrations/0001_initial.py | 105 ------- backend/accounts/migrations/__init__.py | 0 backend/accounts/models.py | 59 ---- backend/accounts/scripts/__init__.py | 0 backend/accounts/scripts/test_users.py | 7 - backend/accounts/serializers.py | 27 -- backend/accounts/tests.py | 61 ----- backend/accounts/urls.py | 18 -- backend/accounts/views.py | 128 --------- backend/backend/__init__.py | 0 backend/backend/asgi.py | 16 -- backend/backend/settings/__init__.py | 0 backend/backend/settings/common.py | 288 -------------------- backend/backend/settings/dev.py | 34 --- backend/backend/settings/prod.py | 1 - backend/backend/urls.py | 69 ----- backend/backend/wsgi.py | 16 -- backend/manage.py | 22 -- backend/posts/__init__.py | 0 backend/posts/admin.py | 58 ---- backend/posts/apps.py | 7 - backend/posts/migrations/0001_initial.py | 215 --------------- backend/posts/migrations/__init__.py | 0 backend/posts/models.py | 122 --------- backend/posts/pagination.py | 24 -- backend/posts/serializers.py | 175 ------------ backend/posts/tests.py | 3 - backend/posts/urls.py | 17 -- backend/posts/views.py | 245 ----------------- backend/requirements.txt | 1 - backend/requirements/common.txt | 17 -- backend/requirements/dev.txt | 7 - backend/requirements/prod.txt | 1 - 39 files changed, 1842 deletions(-) delete mode 100644 backend/.gitignore delete mode 100644 backend/.pycrunch-config.yaml delete mode 100644 backend/accounts/__init__.py delete mode 100644 backend/accounts/admin.py delete mode 100644 backend/accounts/apps.py delete mode 100644 backend/accounts/managers.py delete mode 100644 backend/accounts/migrations/0001_initial.py delete mode 100644 backend/accounts/migrations/__init__.py delete mode 100644 backend/accounts/models.py delete mode 100644 backend/accounts/scripts/__init__.py delete mode 100644 backend/accounts/scripts/test_users.py delete mode 100644 backend/accounts/serializers.py delete mode 100644 backend/accounts/tests.py delete mode 100644 backend/accounts/urls.py delete mode 100644 backend/accounts/views.py delete mode 100644 backend/backend/__init__.py delete mode 100644 backend/backend/asgi.py delete mode 100644 backend/backend/settings/__init__.py delete mode 100644 backend/backend/settings/common.py delete mode 100644 backend/backend/settings/dev.py delete mode 100644 backend/backend/settings/prod.py delete mode 100644 backend/backend/urls.py delete mode 100644 backend/backend/wsgi.py delete mode 100755 backend/manage.py delete mode 100644 backend/posts/__init__.py delete mode 100644 backend/posts/admin.py delete mode 100644 backend/posts/apps.py delete mode 100644 backend/posts/migrations/0001_initial.py delete mode 100644 backend/posts/migrations/__init__.py delete mode 100644 backend/posts/models.py delete mode 100644 backend/posts/pagination.py delete mode 100644 backend/posts/serializers.py delete mode 100644 backend/posts/tests.py delete mode 100644 backend/posts/urls.py delete mode 100644 backend/posts/views.py delete mode 100644 backend/requirements.txt delete mode 100644 backend/requirements/common.txt delete mode 100644 backend/requirements/dev.txt delete mode 100644 backend/requirements/prod.txt diff --git a/backend/.gitignore b/backend/.gitignore deleted file mode 100644 index cdeb8c4..0000000 --- a/backend/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -__pycache__ -db.sqlite3 -.secrets.json -.vscode -.idea -migrations \ No newline at end of file diff --git a/backend/.pycrunch-config.yaml b/backend/.pycrunch-config.yaml deleted file mode 100644 index ce41c09..0000000 --- a/backend/.pycrunch-config.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# documentation https://pycrunch.com/docs/configuration-file -iscovery: - exclusions: - # (example) do not search tests in 'ignored_folder' folder -# - ignored_folder/ - # do not search tests in 'test_ignored.py' file -# - test_ignored.py -engine: - runtime: django -env: - # everything here will be passed as environment variables to the test runner - DJANGO_SETTINGS_MODULE: backend.settings.dev - DJANGO_ALLOW_ASYNC_UNSAFE: "true" \ No newline at end of file diff --git a/backend/accounts/__init__.py b/backend/accounts/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/backend/accounts/admin.py b/backend/accounts/admin.py deleted file mode 100644 index 14fa5fe..0000000 --- a/backend/accounts/admin.py +++ /dev/null @@ -1,7 +0,0 @@ -from django.contrib import admin -from .models import CustomUser - - -@admin.register(CustomUser) -class CustomUserAdmin(admin.ModelAdmin): - list_display = ["email", "name", "username", "is_active", "is_staff", "is_admin"] diff --git a/backend/accounts/apps.py b/backend/accounts/apps.py deleted file mode 100644 index dac7888..0000000 --- a/backend/accounts/apps.py +++ /dev/null @@ -1,7 +0,0 @@ -from django.apps import AppConfig - - -class AccountsConfig(AppConfig): - default_auto_field = "django.db.models.BigAutoField" - name = "accounts" - verbose_name = "account" diff --git a/backend/accounts/managers.py b/backend/accounts/managers.py deleted file mode 100644 index 168e1d0..0000000 --- a/backend/accounts/managers.py +++ /dev/null @@ -1,66 +0,0 @@ -from django.contrib.auth.base_user import AbstractBaseUser -from django.contrib.auth.models import BaseUserManager -from django.utils.translation import gettext as _ - -# https://krakensystems.co/blog/2020/custom-users-using-django-rest-framework -# https://dev.to/raghavmalawat/custom-user-manager-django-rest-framework-5578 -# https://docs.djangoproject.com/en/4.0/topics/db/managers/ - - -class CustomUserManager(BaseUserManager): - def create_user(self, email, name, username, password=None): - if not email: - raise ValueError(_("User must have an email")) - if not password: - raise ValueError(_("User must have a password")) - if not name: - raise ValueError(_("User must have a name")) - if not username: - raise ValueError(_("User must have a username")) - - user = self.model(email=self.normalize_email(email)) - user.name = name - user.username = username - user.set_password(password) # change password to hash - user.is_admin = False - user.is_staff = False - user.save(using=self._db) - return user - - def create_superuser(self, email, name, username, password=None): - if not email: - raise ValueError(_("User must have an email")) - if not password: - raise ValueError(_("User must have a password")) - if not name: - raise ValueError(_("User must have a name")) - if not username: - raise ValueError(_("User must have a username")) - - user = self.model(email=self.normalize_email(email)) - user.name = name - user.username = username - user.set_password(password) # change password to hash - user.is_admin = True - user.is_staff = True - user.save(using=self._db) - return user - - def create_staffuser(self, email, name, username, password=None): - if not email: - raise ValueError(_("User must have an email")) - if not password: - raise ValueError(_("User must have a password")) - if not name: - raise ValueError(_("User must have a name")) - if not username: - raise ValueError(_("User must have a username")) - - user = self.model(email=self.normalize_email(email)) - user.name = name - user.username = username - user.set_password(password) # change password to hash - user.is_admin = False - user.is_staff = True - user.save(using=self._db) - return user diff --git a/backend/accounts/migrations/0001_initial.py b/backend/accounts/migrations/0001_initial.py deleted file mode 100644 index 152fb1e..0000000 --- a/backend/accounts/migrations/0001_initial.py +++ /dev/null @@ -1,105 +0,0 @@ -# Generated by Django 4.0.1 on 2022-02-14 10:06 - -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ("auth", "0012_alter_user_first_name_max_length"), - ] - - operations = [ - migrations.CreateModel( - name="CustomUser", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("password", models.CharField(max_length=128, verbose_name="password")), - ( - "last_login", - models.DateTimeField( - blank=True, null=True, verbose_name="last login" - ), - ), - ( - "is_superuser", - models.BooleanField( - default=False, - help_text="Designates that this user has all permissions without explicitly assigning them.", - verbose_name="superuser status", - ), - ), - ( - "email", - models.EmailField( - max_length=50, unique=True, verbose_name="이메일 주소" - ), - ), - ("name", models.CharField(max_length=30, verbose_name="실명")), - ( - "username", - models.CharField( - blank=True, max_length=30, unique=True, verbose_name="닉네임" - ), - ), - ( - "avatar", - models.ImageField( - blank=True, - help_text="개성을 표현할 수 있는 사진을 올려주세요!", - upload_to="accounts/avatar/%Y/%m/%d", - verbose_name="프로필 사진", - ), - ), - ("is_active", models.BooleanField(default=True)), - ("is_staff", models.BooleanField(default=False)), - ("is_admin", models.BooleanField(default=False)), - ("date_joined", models.DateTimeField(auto_now_add=True)), - ("updated_on", models.DateTimeField(auto_now=True)), - ( - "follower_set", - models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL), - ), - ( - "following_set", - models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL), - ), - ( - "groups", - models.ManyToManyField( - blank=True, - help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.", - related_name="user_set", - related_query_name="user", - to="auth.Group", - verbose_name="groups", - ), - ), - ( - "user_permissions", - models.ManyToManyField( - blank=True, - help_text="Specific permissions for this user.", - related_name="user_set", - related_query_name="user", - to="auth.Permission", - verbose_name="user permissions", - ), - ), - ], - options={ - "abstract": False, - }, - ), - ] diff --git a/backend/accounts/migrations/__init__.py b/backend/accounts/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/backend/accounts/models.py b/backend/accounts/models.py deleted file mode 100644 index 34efbc5..0000000 --- a/backend/accounts/models.py +++ /dev/null @@ -1,59 +0,0 @@ -# https://medium.com/chanjongs-programming-diary/django-rest-framework로-소셜-로그인-api-구현해보기-google-kakao-github-2ccc4d49a781 -# https://wisdom-990629.tistory.com/44 -# https://dev.to/lymaa/authenticate-with-djoser-2kf7 -from django.db import models -from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin -from django.shortcuts import resolve_url -from django.utils.translation import gettext as _ -from accounts.managers import CustomUserManager - - -class CustomUser(AbstractBaseUser, PermissionsMixin): - email = models.EmailField(_("이메일 주소"), max_length=50, unique=True) - name = models.CharField(_("실명"), max_length=30) - username = models.CharField(_("닉네임"), max_length=30, unique=True, blank=True) - avatar = models.ImageField( - _("프로필 사진"), - blank=True, - upload_to="accounts/avatar/%Y/%m/%d", - help_text="개성을 표현할 수 있는 사진을 올려주세요!", - ) - follower_set = models.ManyToManyField("self", blank=True) - following_set = models.ManyToManyField("self", blank=True) - - is_active = models.BooleanField(default=True) - is_staff = models.BooleanField(default=False) # a admin user; non super-user - is_admin = models.BooleanField(default=False) - - date_joined = models.DateTimeField(auto_now_add=True) - updated_on = models.DateTimeField(auto_now=True) - - USERNAME_FIELD = "email" - REQUIRED_FIELDS = ["name", "username"] - - objects = CustomUserManager() - - @staticmethod - def has_perm(perm, obj=None): - # "Does the user have a specific permission?" - # Simplest possible answer: Yes, always - return True - - @staticmethod - def has_module_perms(app_label): - # "Does the user have permissions to view the app `app_label`?" - # Simplest possible answer: Yes, always - return True - - def __str__(self): - return "{}".format(self.email) - - def get_short_name(self): - return self.username - - @property - def avatar_url(self): - if self.avatar: - return self.avatar.url - else: - return resolve_url("pydenticon_image", self.username) diff --git a/backend/accounts/scripts/__init__.py b/backend/accounts/scripts/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/backend/accounts/scripts/test_users.py b/backend/accounts/scripts/test_users.py deleted file mode 100644 index dd9209d..0000000 --- a/backend/accounts/scripts/test_users.py +++ /dev/null @@ -1,7 +0,0 @@ -from accounts.models import CustomUser - -# manage.py runscript scriptname -# 현재로써는 어떤 상황에 쓸지 감이 안오지만 모든 유저를 초기화 시킨다던지 더미 데이터를 넣는 용도로 쓰는게 아닐까 생각함 -# 목데이터 검색어 faker, django seed -def run(): - users = CustomUser.objects.all() diff --git a/backend/accounts/serializers.py b/backend/accounts/serializers.py deleted file mode 100644 index e9e8cad..0000000 --- a/backend/accounts/serializers.py +++ /dev/null @@ -1,27 +0,0 @@ -from django.contrib.auth import get_user_model -from djoser.serializers import UserCreateSerializer -from rest_framework.serializers import ModelSerializer -from accounts.models import CustomUser - -User = get_user_model() - -# ------------- Djoser ------------- # -class UserCreateSerializer(UserCreateSerializer): - class Meta(UserCreateSerializer.Meta): - model = User - fields = ( - "id", - "name", - "username", - "email", - "follower_set", - "following_set", - ) - REQUIRED_FIELDS = ["name"] - - -# ------------- Basic User Info ------------- # -class UsernameUniqueCheckSerializer(ModelSerializer): - class Meta: - model = CustomUser - fields = ("username",) diff --git a/backend/accounts/tests.py b/backend/accounts/tests.py deleted file mode 100644 index 942e1e4..0000000 --- a/backend/accounts/tests.py +++ /dev/null @@ -1,61 +0,0 @@ -from django.test import TestCase -from accounts.models import CustomUser -from backend.settings.common import INSTALLED_APPS -from backend.settings.common import AUTH_USER_MODEL -from rest_framework.test import APITestCase -from django.urls import reverse -from rest_framework import status - -# User Setting Test -class SettingsTest(TestCase): - def test_account_is_configured(self): - assert "accounts" in INSTALLED_APPS - assert "accounts.CustomUser" == AUTH_USER_MODEL - - -# Model Test -class UserModelCreateTest(TestCase): - def setUp(self): - self.email = "test1@test.com" - self.name = "박으뜸" - self.username = "uttum" - self.password = "1212" - self.test_user = CustomUser.objects.create_user( - email=self.email, - name=self.name, - username=self.username, - password=self.password, - ) - - def test_create_user(self): - assert isinstance(self.test_user, CustomUser) - - def test_default_user_is_active(self): - assert self.test_user.is_active - - def test_default_user_is_staff(self): - assert not self.test_user.is_staff - - def test_default_user_is_superuser(self): - assert not self.test_user.is_superuser - - def test_default_email(self): - assert self.test_user.email.__str__() == self.email - - -# API Test -class UserAPITest(APITestCase): - def setUp(self): - self.email = "super@test.com" - self.name = "jun" - self.username = "lazyjun" - self.password = "1212" - self.test_superuser = CustomUser.objects.create_user( - email=self.email, - name=self.name, - username=self.username, - password=self.password, - ) - - def test_can_create_user(self): - assert isinstance(self.test_superuser, CustomUser) diff --git a/backend/accounts/urls.py b/backend/accounts/urls.py deleted file mode 100644 index 30387b7..0000000 --- a/backend/accounts/urls.py +++ /dev/null @@ -1,18 +0,0 @@ -from django.urls import path, include -from rest_framework import urls -from . import views -from rest_framework_simplejwt.views import ( - TokenObtainPairView, - TokenRefreshView, -) - -app_name = "accounts" - - -urlpatterns = [ - # path("token/", TokenObtainPairView.as_view(), name="token_obtain_pair"), - # path("token/refresh/", TokenRefreshView.as_view(), name="token_refresh"), - path("info/follow/", views.user_follow, name="user_follow"), - path("info/unfollow/", views.user_unfollow, name="user_unfollow"), - path("username/", views.UsernameUniqueCheck.as_view()), -] diff --git a/backend/accounts/views.py b/backend/accounts/views.py deleted file mode 100644 index b4ea4ce..0000000 --- a/backend/accounts/views.py +++ /dev/null @@ -1,128 +0,0 @@ -from django.contrib.auth import get_user_model -from drf_yasg.utils import swagger_auto_schema -from requests import Response -from rest_framework import serializers, status -from rest_framework.decorators import api_view -from rest_framework.generics import get_object_or_404, CreateAPIView -from rest_framework_simplejwt.views import ( - TokenBlacklistView, - TokenObtainPairView, - TokenRefreshView, - TokenVerifyView, -) -from accounts.serializers import UsernameUniqueCheckSerializer - -# format=None https://www.django-rest-framework.org/tutorial/2-requests-and-responses/#adding-optional-format-suffixes-to-our-urls -class UsernameUniqueCheck(CreateAPIView): - serializer_class = UsernameUniqueCheckSerializer - - def post(self, request, format=None): - print(request, "발발") - serializer = self.get_serializer( - data=request.data, context={"request": request} - ) - if serializer.is_valid(): - print(serializer.is_valid(), "성공") - # return Response( - # data={"detail": ["You can use this ID"]}, status=status.HTTP_200_OK - # ) - else: - detail = dict() - detail["detail"] = serializer.errors["username"] - print(serializer.is_valid(), "실패") - # return Response(data=detail, status=status.HTTP_400_BAD_REQUEST) - - -@api_view(["POST"]) -def user_follow(request): - username = request.data["username"] - follow_user = get_object_or_404(get_user_model(), username=username, is_active=True) - request.user.following_set.add(follow_user) - follow_user.follower_set.add(request.user) - return Response(status.HTTP_204.NO_CONTENT) - - -@api_view(["POST"]) -def user_unfollow(request): - username = request.data["username"] - follow_user = get_object_or_404(get_user_model(), username=username, is_active=True) - request.user.following_set.remove(follow_user) - follow_user.follower_set.remove(request.user) - return Response(status.HTTP_204.NO_CONTENT) - - -class TokenObtainPairResponseSerializer(serializers.Serializer): - access = serializers.CharField() - refresh = serializers.CharField() - - def create(self, validated_data): - raise NotImplementedError() - - def update(self, instance, validated_data): - raise NotImplementedError() - - -class DecoratedTokenObtainPairView(TokenObtainPairView): - @swagger_auto_schema( - responses={ - status.HTTP_200_OK: TokenObtainPairResponseSerializer, - } - ) - def post(self, request, *args, **kwargs): - return super().post(request, *args, **kwargs) - - -class TokenRefreshResponseSerializer(serializers.Serializer): - access = serializers.CharField() - - def create(self, validated_data): - raise NotImplementedError() - - def update(self, instance, validated_data): - raise NotImplementedError() - - -class DecoratedTokenRefreshView(TokenRefreshView): - @swagger_auto_schema( - responses={ - status.HTTP_200_OK: TokenRefreshResponseSerializer, - } - ) - def post(self, request, *args, **kwargs): - return super().post(request, *args, **kwargs) - - -class TokenVerifyResponseSerializer(serializers.Serializer): - def create(self, validated_data): - raise NotImplementedError() - - def update(self, instance, validated_data): - raise NotImplementedError() - - -class DecoratedTokenVerifyView(TokenVerifyView): - @swagger_auto_schema( - responses={ - status.HTTP_200_OK: TokenVerifyResponseSerializer, - } - ) - def post(self, request, *args, **kwargs): - return super().post(request, *args, **kwargs) - - -class TokenBlacklistResponseSerializer(serializers.Serializer): - def create(self, validated_data): - raise NotImplementedError() - - def update(self, instance, validated_data): - raise NotImplementedError() - - -class DecoratedTokenBlacklistView(TokenBlacklistView): - @swagger_auto_schema( - responses={ - status.HTTP_200_OK: TokenBlacklistResponseSerializer, - } - ) - def post(self, request, *args, **kwargs): - return super().post(request, *args, **kwargs) diff --git a/backend/backend/__init__.py b/backend/backend/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/backend/backend/asgi.py b/backend/backend/asgi.py deleted file mode 100644 index ae724ef..0000000 --- a/backend/backend/asgi.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -ASGI config for backend project. - -It exposes the ASGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/ -""" - -import os - -from django.core.asgi import get_asgi_application - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings.prod") - -application = get_asgi_application() diff --git a/backend/backend/settings/__init__.py b/backend/backend/settings/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/backend/backend/settings/common.py b/backend/backend/settings/common.py deleted file mode 100644 index 30c15ce..0000000 --- a/backend/backend/settings/common.py +++ /dev/null @@ -1,288 +0,0 @@ -""" -Django settings for backend project. - -Generated by 'django-admin startproject' using Django 4.0. - -For more information on this file, see -https://docs.djangoproject.com/en/4.0/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/4.0/ref/settings/ -""" - -from pathlib import Path -import json -from django.core.exceptions import ImproperlyConfigured -from datetime import timedelta -from django.utils.translation import gettext_lazy as _ - -# Build paths inside the project like this: BASE_DIR / 'subdir'. -BASE_DIR = Path(__file__).resolve().parent.parent.parent -secret_file = BASE_DIR / ".secrets.json" - -with open(secret_file) as f: - secrets = json.loads(f.read()) - - -def get_secret(common, secrets=secrets): - """비밀 변수를 가져오거나 명시적 예외를 반환한다.""" - try: - return secrets[common] - except KeyError: - error_msg = "Set the {} environment variable".format(common) - raise ImproperlyConfigured(error_msg) - - -SECRET_KEY = get_secret("SECRET_KEY") - - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ - -DEBUG = True - -ALLOWED_HOSTS = [] - - -# Application definition - - -DJANGO_APPS = [ - "django.contrib.admin", - "django.contrib.auth", - "django.contrib.contenttypes", - "django.contrib.sessions", - "django.contrib.messages", - "django.contrib.staticfiles", -] - -# django-allauth https://django-allauth.readthedocs.io/en/latest/configuration.html -# dj-rest-auth https://dj-rest-auth.readthedocs.io/en/latest/installation.html -# rest_framework https://www.django-rest-framework.org -# rest_framework-simplejwt https://django-rest-framework-simplejwt.readthedocs.io/en/latest/ -# django-cors-headers https://github.com/adamchainz/django-cors-headers -# djoser https://djoser.readthedocs.io/en/latest/getting_started.html - - -THIRD_PARTY_APPS = [ - "corsheaders", - "djoser", - "django_filters", - "rest_framework", - "rest_framework_simplejwt", - "rest_framework_simplejwt.token_blacklist", -] -# account: 커스텀 유저 & 회원가입 -PROJECT_APPS = [ - "accounts", - "posts", -] - -INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + PROJECT_APPS - -# https://docs.djangoproject.com/en/4.0/topics/i18n/translation/#how-django-discovers-language-preference -# translation: LocaleMiddleware가 session 뒤 common 앞이여야 한다 -MIDDLEWARE = [ - "django.middleware.security.SecurityMiddleware", - "django.contrib.sessions.middleware.SessionMiddleware", - "django.middleware.locale.LocaleMiddleware", - "corsheaders.middleware.CorsMiddleware", - "django.middleware.common.CommonMiddleware", - "django.middleware.csrf.CsrfViewMiddleware", - "django.contrib.auth.middleware.AuthenticationMiddleware", - "django.contrib.messages.middleware.MessageMiddleware", - "django.middleware.clickjacking.XFrameOptionsMiddleware", -] - -ROOT_URLCONF = "backend.urls" - -TEMPLATES = [ - { - "BACKEND": "django.template.backends.django.DjangoTemplates", - "DIRS": [], - "APP_DIRS": True, - "OPTIONS": { - "context_processors": [ - "django.template.context_processors.debug", - "django.template.context_processors.request", - "django.contrib.auth.context_processors.auth", - "django.contrib.messages.context_processors.messages", - ], - }, - }, -] - -WSGI_APPLICATION = "backend.wsgi.application" - - -# Database -# https://docs.djangoproject.com/en/4.0/ref/settings/#databases - -DATABASES = { - "default": { - "ENGINE": "django.db.backends.sqlite3", - "NAME": BASE_DIR / "db.sqlite3", - } -} - -# 프로젝트를 시작할때 사용자 정의 모델 사용(마이그레이션을 늦게 하는 이유) -# https://docs.djangoproject.com/en/4.0/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project -AUTH_USER_MODEL = "accounts.CustomUser" - -# Password validation -# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators - -AUTH_PASSWORD_VALIDATORS = [ - { - "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", - }, -] - - -# Internationalization -# https://docs.djangoproject.com/en/4.0/topics/i18n/ - -LANGUAGE_CODE = "ko" - -TIME_ZONE = "Asia/Seoul" - -USE_I18N = True # 장고 번역 시스템 활성화 여부 -USE_L10N = True # 현지화 데이터 형식 사용 여부 -USE_TZ = True # 시간대 인식 여부 - -LOCALE_PATHS = [ - BASE_DIR / "locale", -] - -# 세팅방법 https://docs.djangoproject.com/en/4.0/ref/settings/#languages -# 언어코드 https://github.com/django/django/blob/main/django/conf/global_settings.py -# 사용함수 https://docs.djangoproject.com/en/4.0/ref/utils/#django.utils.translation.gettext_lazy -# 컴파일 python manage.py compilemessages or django-admin compilemessages --ignore=cache --ignore=outdated/*/locale -# 관련문서 https://docs.djangoproject.com/en/4.0/ref/django-admin/#compilemessages - -LANGUAGES = ( - ("ko", _("Koran")), - ("ja", _("Japanese")), - ("en", _("English")), - ("zh-hans", _("Simplified Chinese")), - ("zh-hant", _("Traditional Chinese")), -) - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/4.0/howto/static-files/ - -STATIC_URL = "/static/" -STATIC_ROOT = BASE_DIR / "static" - -MEDIA_URL = "/media/" -MEDIA_ROOT = BASE_DIR / "media" - - -# Default primary key field type -# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field - -DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" - - -# https://velog.io/@ayoung0073/DRF-signup-login-jwt -## DRF -REST_FRAMEWORK = { - "DEFAULT_PERMISSION_CLASSES": [ - "rest_framework.permissions.AllowAny", - # "rest_framework.permissions.IsAuthenticated", - ], - "DEFAULT_AUTHENTICATION_CLASSES": [ - "rest_framework_simplejwt.authentication.JWTAuthentication", - ], - "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination", - "PAGE_SIZE": 100, - "DEFAULT_FILTER_BACKENDS": ["django_filters.rest_framework.DjangoFilterBackend"], -} -# JWT -SIMPLE_JWT = { - "AUTH_HEADER_TYPES": ("JWT",), - "ACCESS_TOKEN_LIFETIME": timedelta(minutes=30), - "REFRESH_TOKEN_LIFETIME": timedelta(days=1), - "ALGORITHM": "HS256", - "SIGNING_KEY": SECRET_KEY, - "LEEWAY": 0, # 만료시간에 여유를 주는 값 -} - -# DJOSER CONFIG -# uid관한 솔루션 https://protocolostomy.com/2021/05/06/user-activation-with-django-and-djoser/ # 결론: uid = pk 로 쓰기로 했다 -# 밑에 설정 많은데 전부 기본값이다 바꿀꺼 있으면 바꿀려고 기본값이지만 명시해놓음 -DJOSER = { - "LOGIN_FIELD": "email", - "USER_CREATE_PASSWORD_RETYPE": True, - # "USERNAME_CHANGED_EMAIL_CONFIRMATION": True, - # "PASSWORD_CHANGED_EMAIL_CONFIRMATION": True, - # "SEND_CONFIRMATION_EMAIL": True, - "SET_PASSWORD_RETYPE": True, - "PASSWORD_RESET_CONFIRM_URL": "password/reset/confirm/{id}/{token}", - "USERNAME_RESET_CONFIRM_URL": "email/reset/confirm/{id}/{token}", - "ACTIVATION_URL": "activate/{id}/{token}", - # "SEND_ACTIVATION_EMAIL": True, - "HIDE_USERS": True, - "SERIALIZERS": { - # SERIALIZERS - "activation": "djoser.serializers.ActivationSerializer", - "password_reset": "djoser.serializers.SendEmailResetSerializer", - "password_reset_confirm": "djoser.serializers.PasswordResetConfirmSerializer", - "password_reset_confirm_retype": "djoser.serializers.PasswordResetConfirmRetypeSerializer", - "set_password": "djoser.serializers.SetPasswordSerializer", - "set_password_retype": "djoser.serializers.SetPasswordRetypeSerializer", - "set_username": "djoser.serializers.SetUsernameSerializer", - "set_username_retype": "djoser.serializers.SetUsernameRetypeSerializer", - "username_reset": "djoser.serializers.SendEmailResetSerializer", - "username_reset_confirm": "djoser.serializers.UsernameResetConfirmSerializer", - "username_reset_confirm_retype": "djoser.serializers.UsernameResetConfirmRetypeSerializer", - "user_create": "CustomUser.serializers.UserCreateSerializer", - "user_create_password_retype": "djoser.serializers.UserCreatePasswordRetypeSerializer", - "user_delete": "djoser.serializers.UserDeleteSerializer", - "user": "djoser.serializers.UserSerializer", - "current_user": "djoser.serializers.UserSerializer", - "token": "djoser.serializers.TokenSerializer", - "token_create": "djoser.serializers.TokenCreateSerializer", - }, - "EMAILS": { - # EMAILS - "activation": "djoser.email.ActivationEmail", - "confirmation": "djoser.email.ConfirmationEmail", - "password_reset": "djoser.email.PasswordResetEmail", - "password_changed_confirmation": "djoser.email.PasswordChangedConfirmationEmail", - "username_changed_confirmation": "djoser.email.UsernameChangedConfirmationEmail", - "username_reset": "djoser.email.UsernameResetEmail", - }, - "CONSTANCE": { - # CONSTANCE - "messages": "djoser.constants.Messages", - }, - "PERMISSIONS": { - "activation": ["rest_framework.permissions.AllowAny"], - "password_reset": ["rest_framework.permissions.AllowAny"], - "password_reset_confirm": ["rest_framework.permissions.AllowAny"], - "set_password": ["djoser.permissions.CurrentUserOrAdmin"], - "username_reset": ["rest_framework.permissions.AllowAny"], - "username_reset_confirm": ["rest_framework.permissions.AllowAny"], - "set_username": ["djoser.permissions.CurrentUserOrAdmin"], - "user_create": ["rest_framework.permissions.AllowAny"], - "user_delete": ["djoser.permissions.CurrentUserOrAdmin"], - "user": ["djoser.permissions.CurrentUserOrAdmin"], - "user_list": ["djoser.permissions.CurrentUserOrAdmin"], - "token_create": ["rest_framework.permissions.AllowAny"], - "token_destroy": ["rest_framework.permissions.IsAuthenticated"], - }, -} - -CORS_ALLOWED_ORIGINS = [ - # "my_url", -] diff --git a/backend/backend/settings/dev.py b/backend/backend/settings/dev.py deleted file mode 100644 index 9906435..0000000 --- a/backend/backend/settings/dev.py +++ /dev/null @@ -1,34 +0,0 @@ -from .common import * - -# django-debug-toolbar https://django-debug-toolbar.readthedocs.io/en/latest/ -# django-extensions https://django-extensions.readthedocs.io/en/latest/installation_instructions.html -# drf-yasg https://drf-yasg.readthedocs.io/en/stable/readme.html#usage - -INSTALLED_APPS += [ - "debug_toolbar", - "django_extensions", - "drf_yasg", -] -MIDDLEWARE = [ - "debug_toolbar.middleware.DebugToolbarMiddleware", -] + MIDDLEWARE -INTERNAL_IPS = ["127.0.0.1"] - -CORS_ALLOWED_ORIGINS = [ - "http://localhost:3000", -] -SWAGGER_SETTINGS = { - "LOGIN_URL": "/admin/login", - "USE_SESSION_AUTH": True, - "SECURITY_DEFINITIONS": { - # 'Token': {'type': 'apiKey', 'name': 'Authorization', 'in': 'header'}, # 토큰 사용시 - # "basic": { - # "type": "basic", # id, password 그러나 셋팅을 바꿔야할듯 - # }, - "JWT": {"type": "apiKey", "name": "Authorization", "in": "header"}, - }, - "JSON_EDITOR": True, - "SHOW_REQUEST_HEADERS": True, - "OPERATIONS_SORTER": "alpha", - "PERSIST_AUTH": True, -} diff --git a/backend/backend/settings/prod.py b/backend/backend/settings/prod.py deleted file mode 100644 index 55e5f84..0000000 --- a/backend/backend/settings/prod.py +++ /dev/null @@ -1 +0,0 @@ -from .common import * diff --git a/backend/backend/urls.py b/backend/backend/urls.py deleted file mode 100644 index 704a176..0000000 --- a/backend/backend/urls.py +++ /dev/null @@ -1,69 +0,0 @@ -"""backend URL Configuration - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/4.0/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: path('', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') -Including another URLconf - 1. Import the include() function: from django.urls import include, path - 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) -""" -from django.conf import settings -from django.conf.urls.static import static -from django.contrib import admin -from django.urls import path, include, re_path -from django_pydenticon.views import image as pydenticon_image - -# drf-yasg -from rest_framework import permissions -from drf_yasg.views import get_schema_view -from drf_yasg import openapi - -schema_view = get_schema_view( - openapi.Info( - title="SEForum API", - default_version="v1", - description="Automate created document SEForum API", - terms_of_service="https://www.google.com/policies/terms/", - contact=openapi.Contact(email="contact@snippets.local"), - license=openapi.License(name="BSD License"), - ), - public=True, - permission_classes=(permissions.AllowAny,), -) - - -urlpatterns = [ - path("admin/", admin.site.urls), - path("users/", include("accounts.urls")), - path("posts/", include("posts.urls")), - path("", include("djoser.urls")), - path("", include("djoser.urls.jwt")), - path("avatar/image/.png", pydenticon_image, name="pydenticon_image"), - re_path( - r"^api/v1/swagger(?P\.json|\.yaml)$", - schema_view.without_ui(cache_timeout=0), - name="schema-json", - ), - re_path( - r"^api/v1/swagger/$", - schema_view.with_ui("swagger", cache_timeout=0), - name="schema-swagger-ui", - ), - re_path( - r"^api/v1/redoc/$", - schema_view.with_ui("redoc", cache_timeout=0), - name="schema-redoc", - ), -] - -if settings.DEBUG: - urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) - urlpatterns += [ - path("__debug__/", include("debug_toolbar.urls")), - ] diff --git a/backend/backend/wsgi.py b/backend/backend/wsgi.py deleted file mode 100644 index 052f653..0000000 --- a/backend/backend/wsgi.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -WSGI config for backend project. - -It exposes the WSGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/ -""" - -import os - -from django.core.wsgi import get_wsgi_application - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings.prod") - -application = get_wsgi_application() diff --git a/backend/manage.py b/backend/manage.py deleted file mode 100755 index e25be5d..0000000 --- a/backend/manage.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -"""Django's command-line utility for administrative tasks.""" -import os -import sys - - -def main(): - """Run administrative tasks.""" - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings.dev") - try: - from django.core.management import execute_from_command_line - except ImportError as exc: - raise ImportError( - "Couldn't import Django. Are you sure it's installed and " - "available on your PYTHONPATH environment variable? Did you " - "forget to activate a virtual environment?" - ) from exc - execute_from_command_line(sys.argv) - - -if __name__ == "__main__": - main() diff --git a/backend/posts/__init__.py b/backend/posts/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/backend/posts/admin.py b/backend/posts/admin.py deleted file mode 100644 index 5a2ac95..0000000 --- a/backend/posts/admin.py +++ /dev/null @@ -1,58 +0,0 @@ -# 수정 사항 -# search_fields = ["title"] -# -# def short_content(self, post): -# return post.content[:15] -from django.contrib import admin - -from .models import Post, PostComment, Comment, Tag - - -@admin.register(Post) -class PostAdmin(admin.ModelAdmin): - list_display = ( - "id", - "title", - "content", - "created_at", - "updated_at", - "category", - "author", - ) - list_filter = ("created_at", "updated_at", "category", "author") - date_hierarchy = "created_at" - - -@admin.register(PostComment) -class PostCommentAdmin(admin.ModelAdmin): - list_display = ( - "id", - "title", - "content", - "created_at", - "updated_at", - "answer", - "author", - ) - list_filter = ("created_at", "updated_at", "answer", "author") - date_hierarchy = "created_at" - - -@admin.register(Comment) -class CommentAdmin(admin.ModelAdmin): - list_display = ( - "id", - "author", - "post", - "content", - "created_at", - "updated_at", - ) - list_filter = ("author", "post", "created_at", "updated_at") - date_hierarchy = "created_at" - - -@admin.register(Tag) -class TagAdmin(admin.ModelAdmin): - list_display = ("id", "name") - search_fields = ("name",) diff --git a/backend/posts/apps.py b/backend/posts/apps.py deleted file mode 100644 index d5a8e95..0000000 --- a/backend/posts/apps.py +++ /dev/null @@ -1,7 +0,0 @@ -from django.apps import AppConfig - - -class PostssConfig(AppConfig): - default_auto_field = "django.db.models.BigAutoField" - name = "posts" - verbose_name = "posts" diff --git a/backend/posts/migrations/0001_initial.py b/backend/posts/migrations/0001_initial.py deleted file mode 100644 index d5caff7..0000000 --- a/backend/posts/migrations/0001_initial.py +++ /dev/null @@ -1,215 +0,0 @@ -# Generated by Django 4.0.1 on 2022-02-14 10:06 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name="Post", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("title", models.CharField(max_length=100, verbose_name="제목")), - ("content", models.TextField(verbose_name="내용")), - ( - "created_at", - models.DateTimeField(auto_now_add=True, verbose_name="작성일"), - ), - ("updated_at", models.DateTimeField(auto_now=True, verbose_name="수정일")), - ( - "category", - models.CharField( - choices=[ - ("free", "free"), - ("question", "question"), - ("hot", "hot"), - ("news", "news"), - ], - max_length=12, - ), - ), - ("hit", models.IntegerField(default=0)), - ( - "author", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="PostAuthor_set", - to=settings.AUTH_USER_MODEL, - verbose_name="작성자", - ), - ), - ( - "like_user_set", - models.ManyToManyField( - blank=True, - related_name="like_post_set", - to=settings.AUTH_USER_MODEL, - ), - ), - ], - options={ - "ordering": ["-id"], - }, - ), - migrations.CreateModel( - name="Tag", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("name", models.CharField(max_length=50, unique=True)), - ], - ), - migrations.CreateModel( - name="PostLikes", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "created_at", - models.DateTimeField(auto_now_add=True, verbose_name="작성일"), - ), - ("updated_at", models.DateTimeField(auto_now=True, verbose_name="수정일")), - ( - "post", - models.ForeignKey( - blank=True, - on_delete=django.db.models.deletion.CASCADE, - related_name="Post_likes", - to="posts.post", - verbose_name="좋아요(게시글)", - ), - ), - ( - "user", - models.ForeignKey( - blank=True, - on_delete=django.db.models.deletion.CASCADE, - related_name="User_likes", - to=settings.AUTH_USER_MODEL, - verbose_name="좋아요(유저)", - ), - ), - ], - ), - migrations.CreateModel( - name="PostComment", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("title", models.CharField(max_length=100, verbose_name="제목")), - ("content", models.TextField(verbose_name="내용")), - ( - "created_at", - models.DateTimeField(auto_now_add=True, verbose_name="작성일"), - ), - ("updated_at", models.DateTimeField(auto_now=True, verbose_name="수정일")), - ( - "answer", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="posts.post", - verbose_name="원글", - ), - ), - ( - "author", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="PostCommentAuthor_set", - to=settings.AUTH_USER_MODEL, - verbose_name="답변작성자", - ), - ), - ( - "tag_set", - models.ManyToManyField( - blank=True, to="posts.Tag", verbose_name="태그" - ), - ), - ], - options={ - "ordering": ["-id"], - }, - ), - migrations.AddField( - model_name="post", - name="tag_set", - field=models.ManyToManyField(blank=True, to="posts.Tag", verbose_name="태그"), - ), - migrations.CreateModel( - name="Comment", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("content", models.TextField(verbose_name="내용")), - ( - "created_at", - models.DateTimeField(auto_now_add=True, verbose_name="작성일"), - ), - ("updated_at", models.DateTimeField(auto_now=True, verbose_name="수정일")), - ( - "author", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="CommentAuthor_set", - to=settings.AUTH_USER_MODEL, - verbose_name="댓글작성자", - ), - ), - ( - "post", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="posts.post" - ), - ), - ], - options={ - "ordering": ["-id"], - }, - ), - ] diff --git a/backend/posts/migrations/__init__.py b/backend/posts/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/backend/posts/models.py b/backend/posts/models.py deleted file mode 100644 index d450838..0000000 --- a/backend/posts/models.py +++ /dev/null @@ -1,122 +0,0 @@ -from django.conf import settings -from django.db import models -from django.utils.translation import gettext as _ - - -class PostModel(models.Model): - title = models.CharField(_("제목"), max_length=100, null=False) - content = models.TextField(_("내용"), null=False) - created_at = models.DateTimeField(_("작성일"), auto_now_add=True) - updated_at = models.DateTimeField(_("수정일"), auto_now=True) - - class Meta: - abstract = True - - def __str__(self): - return f"{self.title},{self.content},{self.author}" - - -class Post(PostModel): - Choices = ( - ("free", "free"), - ("question", "question"), - ("hot", "hot"), - ("news", "news"), - ) - category = models.CharField(max_length=12, choices=Choices) - tag_set = models.ManyToManyField("Tag", blank=True, verbose_name=_("태그")) - hit = models.IntegerField(default=0) - - # 정참조 select_releated - author = models.ForeignKey( - settings.AUTH_USER_MODEL, - related_name="PostAuthor_set", - on_delete=models.CASCADE, - verbose_name=_("작성자"), - ) - # 역참조 prefetch_related - like_user_set = models.ManyToManyField( - settings.AUTH_USER_MODEL, blank=True, related_name="like_post_set" - ) - - class Meta: - ordering = ["-id"] - - -class PostComment(PostModel): - answer = models.ForeignKey( - Post, - on_delete=models.CASCADE, - verbose_name=_("원글"), - related_name="PostAnswer_set", - ) - author = models.ForeignKey( - settings.AUTH_USER_MODEL, - related_name="PostCommentAuthor_set", - on_delete=models.CASCADE, - verbose_name=_("답변작성자"), - ) - like_user_set = models.ManyToManyField( - settings.AUTH_USER_MODEL, blank=True, related_name="like_post_comment_set" - ) - - class Meta: - ordering = ["-id"] - - def __str__(self): - return f"{self.author},{self.title},{self.content}" - - -class Comment(models.Model): - author = models.ForeignKey( - settings.AUTH_USER_MODEL, - on_delete=models.CASCADE, - related_name="CommentAuthor_set", - verbose_name=_("댓글작성자"), - ) - - post = models.ForeignKey(Post, on_delete=models.CASCADE) - content = models.TextField(_("내용"), null=False) - like_user_set = models.ManyToManyField( - settings.AUTH_USER_MODEL, blank=True, related_name="like_comment_set" - ) - - created_at = models.DateTimeField(_("작성일"), auto_now_add=True) - updated_at = models.DateTimeField(_("수정일"), auto_now=True) - - class Meta: - ordering = ["-id"] - - def __str__(self): - return f"{self.author},{self.post},{self.content}" - - -class CommentReply(models.Model): - author = models.ForeignKey( - settings.AUTH_USER_MODEL, - on_delete=models.CASCADE, - related_name="CommentReplyAuthor_set", - verbose_name=_("대댓글작성자"), - ) - - post = models.ForeignKey(Comment, on_delete=models.CASCADE) - content = models.TextField(_("내용"), null=False) - like_user_set = models.ManyToManyField( - settings.AUTH_USER_MODEL, blank=True, related_name="like_comment_reply_set" - ) - - created_at = models.DateTimeField(_("작성일"), auto_now_add=True) - updated_at = models.DateTimeField(_("수정일"), auto_now=True) - - class Meta: - ordering = ["-id"] - - def __str__(self): - return f"{self.author},{self.post},{self.content}" - - -class Tag(models.Model): - name = models.CharField(max_length=50, unique=True) - - def __str__(self): - return self.name diff --git a/backend/posts/pagination.py b/backend/posts/pagination.py deleted file mode 100644 index b44768b..0000000 --- a/backend/posts/pagination.py +++ /dev/null @@ -1,24 +0,0 @@ -from rest_framework.pagination import PageNumberPagination -from rest_framework.response import Response - -DEFAULT_PAGE = 1 -DEFAULT_PAGE_SIZE = 30 - -class PostPageNumberPagination(PageNumberPagination): - page = DEFAULT_PAGE - page_size = DEFAULT_PAGE_SIZE - page_size_query_param = 'page_size' - - def get_paginated_response(self, data): - return Response({ - 'page_size': int(self.request.GET.get('page_size', self.page_size)), - 'links': { - 'next': self.get_next_link(), - 'previous': self.get_previous_link() - }, - 'current_page': int(self.request.GET.get('page', DEFAULT_PAGE)), # can not set default = self.page - # 'current_page':int(self.page.number), #덜 유연한 코드 - 'total': self.page.paginator.count, - 'last_page': self.page.paginator.num_pages, - 'results': data - }) \ No newline at end of file diff --git a/backend/posts/serializers.py b/backend/posts/serializers.py deleted file mode 100644 index 5104737..0000000 --- a/backend/posts/serializers.py +++ /dev/null @@ -1,175 +0,0 @@ -import re -from django.contrib.auth import get_user_model -from rest_framework import serializers - -from .models import Post, Comment, PostComment, CommentReply - - -class AuthorSerializer(serializers.ModelSerializer): - avatar_url = serializers.SerializerMethodField("avatar_url_field") - - class Meta: - model = get_user_model() - fields = ["username", "name", "avatar_url"] - - def avatar_url_field(self, author): - if re.match(r"^https?://", author.avatar_url): - return author.avatar_url - if "request" in self.context: - scheme = self.context["request"].scheme - host = self.context["request"].get_host() - return scheme + "://" + host + author.avatar_url - - -class PostSerializer(serializers.ModelSerializer): - author = AuthorSerializer(read_only=True) - tag_set = serializers.StringRelatedField(many=True, read_only=True) - comment = serializers.SerializerMethodField() - likes = serializers.SerializerMethodField() - isLikes = serializers.SerializerMethodField() - - def get_likes(self, post): - return post.like_user_set.count() - # return like_user_set.objects.filter(post=post).count() - - def get_comment(self, post): - return Comment.objects.filter(post=post).count() - - def get_isLikes(self, post): - if "request" in self.context: - user = self.context["request"].user - return post.like_user_set.filter(pk=user.pk).exists() - else: - return False - - class Meta: - model = Post - fields = [ - "id", - "category", - "hit", - "likes", - "isLikes", - "author", - "title", - "content", - "tag_set", - "comment", - "created_at", - "updated_at", - ] - - -class HotPostSerializer(serializers.ModelSerializer): - author = AuthorSerializer(read_only=True) - tag_set = serializers.StringRelatedField(many=True, read_only=True) - comment = serializers.SerializerMethodField() - likes = serializers.SerializerMethodField() - - def get_likes(self, post): - return post.like_user_set.count() - - def get_comment(self, post): - return Comment.objects.filter(post=post).count() - - class Meta: - model = Post - fields = [ - "id", - "category", - "hit", - # "like_user_set", - "likes", - "comment", - "author", - "title", - "content", - "tag_set", - "created_at", - "updated_at", - ] - - -class PostCommentSerializer(serializers.ModelSerializer): - author = AuthorSerializer(read_only=True) - likes = serializers.SerializerMethodField() - isLikes = serializers.SerializerMethodField() - - def get_likes(self, post): - return post.like_user_set.count() - - def get_isLikes(self, post): - if "request" in self.context: - user = self.context["request"].user - return post.like_user_set.filter(pk=user.pk).exists() - else: - return False - - class Meta: - model = PostComment - fields = [ - "id", - "author", - "title", - "content", - "likes", - "isLikes", - "created_at", - "updated_at", - ] - - -class CommentSerializer(serializers.ModelSerializer): - author = AuthorSerializer(read_only=True) - likes = serializers.SerializerMethodField() - isLikes = serializers.SerializerMethodField() - - def get_likes(self, post): - return post.like_user_set.count() - - def get_isLikes(self, post): - if "request" in self.context: - user = self.context["request"].user - return post.like_user_set.filter(pk=user.pk).exists() - else: - return False - - class Meta: - model = Comment - fields = [ - "id", - "author", - "likes", - "content", - "isLikes", - "created_at", - "updated_at", - ] - - -class CommentReplySerializer(serializers.ModelSerializer): - author = AuthorSerializer(read_only=True) - likes = serializers.SerializerMethodField() - isLikes = serializers.SerializerMethodField() - - def get_likes(self, post): - return post.like_user_set.count() - - def get_isLikes(self, post): - if "request" in self.context: - user = self.context["request"].user - return post.like_user_set.filter(pk=user.pk).exists() - else: - return False - - class Meta: - model = CommentReply - fields = [ - "id", - "author", - "likes", - "content", - "isLikes", - "created_at", - "updated_at", - ] diff --git a/backend/posts/tests.py b/backend/posts/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/backend/posts/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/backend/posts/urls.py b/backend/posts/urls.py deleted file mode 100644 index 81bedb8..0000000 --- a/backend/posts/urls.py +++ /dev/null @@ -1,17 +0,0 @@ -from django.urls import path, include, re_path -from rest_framework import routers -from rest_framework.routers import DefaultRouter -from . import views - - -app_name = "postsApp" -router = DefaultRouter() -router.register("api", views.PostViewSet, "postsApi") -router.register(r"api/(?P\d+)/postComment", views.PostCommentViewSet) -router.register(r"api/(?P\d+)/comments", views.CommentViewSet) -router.register(r"api/(?P\d+)/commentsReply", views.CommentReplyViewSet) - - -urlpatterns = [ - path("api/hot/", views.HotPost.as_view()), -] + router.urls diff --git a/backend/posts/views.py b/backend/posts/views.py deleted file mode 100644 index db2d781..0000000 --- a/backend/posts/views.py +++ /dev/null @@ -1,245 +0,0 @@ -import re -import datetime -from django.db import transaction -from django.db.models import Count -from django.utils import timezone -from rest_framework.decorators import action -from rest_framework.generics import get_object_or_404 -from rest_framework import status, generics -from rest_framework.permissions import ( - IsAuthenticatedOrReadOnly, -) -from rest_framework.viewsets import ModelViewSet -from django_filters.rest_framework import DjangoFilterBackend -from rest_framework.response import Response -from .models import Post, Comment, PostComment, Tag, CommentReply -from .serializers import ( - PostSerializer, - CommentSerializer, - PostCommentSerializer, - HotPostSerializer, - CommentReplySerializer, -) -from .pagination import PostPageNumberPagination - - -class PostViewSet(ModelViewSet): - queryset = ( - Post.objects.all() - .select_related("author") - .prefetch_related("tag_set", "like_user_set") - ) - serializer_class = PostSerializer - - filter_backends = [DjangoFilterBackend] - filterset_fields = ["id", "category", "like_user_set"] - permission_classes = [ - IsAuthenticatedOrReadOnly, - ] - pagination_class = PostPageNumberPagination - - def perform_create(self, serializer): - author = self.request.user - serializer.save(author=author) - - post = serializer.instance - - tag_name_set = self.request.data.get("content") - re_tag = re.findall(r"#([a-zA-Z\dㄱ-힣]{0,10})", tag_name_set) - - tag_list = [] - for word in re_tag: - tag_name = word.strip() - tag, __ = Tag.objects.get_or_create(name=tag_name) - tag_list.append(tag) - - post.tag_set.add(*tag_list) - - def retrieve(self, request, pk=None, *args, **kwargs): - # 조회수 cookie https://moondol-ai.tistory.com/216 - instance = get_object_or_404(self.get_queryset(), pk=pk) - # 밤 12시에 쿠키 초기화 - tomorrow = datetime.datetime.replace( - timezone.datetime.now(), hour=23, minute=59, second=0 - ) - expires = datetime.datetime.strftime(tomorrow, "%a, %d-%b-%Y %H:%M:%S GMT") - - # 쿠키 만들 준비 - serializer = self.get_serializer(instance) - response = Response(serializer.data, status=status.HTTP_200_OK) - - # 쿠키 읽기 & 생성 - if request.COOKIES.get("hit") is not None: - # 쿠키에 hit 값이 이미 있을 경우 - cookies = request.COOKES.get("hit") - print("retrieve", cookies) - cookies_list = cookies.split("|") - if str(pk) not in cookies_list: - response.set_cookie("hit", cookies + f"|{pk}", expries=expires) - with transaction.atomic(): - instance.hit += 1 - instance.save() - else: - response.set_cookie("hit", pk, expires=expires) - print("retrieve", response) - instance.hit += 1 - instance.save() - # views가 추가되면 해당 instance를 serializer에 표시 - serializer = self.get_serializer(instance) - response = Response(serializer.data, status=status.HTTP_200_OK) - return response - - def get_serializer_context(self): - context = super().get_serializer_context() - context["request"] = self.request - # context.update({"request": self.request}) - return context - - @action(detail=True, methods=["POST"]) - def like(self, request, pk): - post = self.get_object() - post.like_user_set.add(self.request.user) - return Response(status.HTTP_201_CREATED) - - @like.mapping.delete - def unlike(self, request, pk): - post = self.get_object() - post.like_user_set.remove(self.request.user) - return Response(status.HTTP_204_NO_CONTENT) - - -class HotPost(generics.ListAPIView): - queryset = Post.objects.all() - serializer_class = HotPostSerializer - permission_classes = [ - IsAuthenticatedOrReadOnly, - ] - pagination_class = PostPageNumberPagination - - def list(self, request, *args, **kwargs): - queryset = self.filter_queryset(self.get_queryset()) - timesince = timezone.now() - datetime.timedelta(days=7) - - queryset = ( - queryset.exclude(like_user_set__isnull=True) - .annotate(like=Count("like_user_set")) - .filter(created_at__gte=timesince) - .order_by("-like") - )[0:60] - - page = self.paginate_queryset(queryset) - if page is not None: - serializer = self.get_serializer(page, many=True) - return self.get_paginated_response(serializer.data) - - serializer = self.get_serializer(queryset, many=True) - return Response(serializer.data) - - -class PostCommentViewSet(ModelViewSet): - queryset = ( - PostComment.objects.all() - .select_related("author", "answer") - .prefetch_related("like_user_set") - ) - serializer_class = PostCommentSerializer - permission_classes = [ - IsAuthenticatedOrReadOnly, - ] - - def get_serializer_context(self): - context = super().get_serializer_context() - context["request"] = self.request - return context - - def get_queryset(self): - qs = super().get_queryset() - qs = qs.filter(answer__pk=self.kwargs["post_pk"]) - return qs - - def perform_create(self, serializer): - answer = get_object_or_404(Post, pk=self.kwargs["post_pk"]) - serializer.save(author=self.request.user, answer=answer) - return super().perform_create(serializer) - - @action(detail=True, methods=["POST"]) - def like(self, request, post_pk, pk): - post = self.get_object() - post.like_user_set.add(self.request.user) - return Response(status.HTTP_201_CREATED) - - @like.mapping.delete - def unlike(self, request, post_pk, pk): - post = self.get_object() - post.like_user_set.remove(self.request.user) - return Response(status.HTTP_204_NO_CONTENT) - - -class CommentViewSet(ModelViewSet): - queryset = Comment.objects.all().select_related("author", "post") - serializer_class = CommentSerializer - permission_classes = [ - IsAuthenticatedOrReadOnly, - ] - - def get_serializer_context(self): - context = super().get_serializer_context() - context["request"] = self.request - return context - - def get_queryset(self): - qs = super().get_queryset() - qs = qs.filter(post__pk=self.kwargs["post_pk"]) - return qs - - def perform_create(self, serializer): - post = get_object_or_404(Post, pk=self.kwargs["post_pk"]) - serializer.save(author=self.request.user, post=post) - return super().perform_create(serializer) - - @action(detail=True, methods=["POST"]) - def like(self, request, post_pk): - post = self.get_object() - post.like_user_set.add(self.request.user) - return Response(status.HTTP_201_CREATED) - - @like.mapping.delete - def unlike(self, request, post_pk): - post = self.get_object() - post.like_user_set.remove(self.request.user) - return Response(status.HTTP_204_NO_CONTENT) - - -class CommentReplyViewSet(ModelViewSet): - queryset = CommentReply.objects.all().select_related("author", "post") - serializer_class = CommentReplySerializer - permission_classes = [ - IsAuthenticatedOrReadOnly, - ] - - def get_serializer_context(self): - context = super().get_serializer_context() - context["request"] = self.request - return context - - def get_queryset(self): - qs = super().get_queryset() - qs = qs.filter(post__pk=self.kwargs["post_pk"]) - return qs - - def perform_create(self, serializer): - post = get_object_or_404(Comment, pk=self.kwargs["post_pk"]) - serializer.save(author=self.request.user, post=post) - return super().perform_create(serializer) - - @action(detail=True, methods=["POST"]) - def like(self, request, post_pk, pk): - post = self.get_object() - post.like_user_set.add(self.request.user) - return Response(status.HTTP_201_CREATED) - - @like.mapping.delete - def unlike(self, request, post_pk, pk): - post = self.get_object() - post.like_user_set.remove(self.request.user) - return Response(status.HTTP_204_NO_CONTENT) diff --git a/backend/requirements.txt b/backend/requirements.txt deleted file mode 100644 index 3e2d68e..0000000 --- a/backend/requirements.txt +++ /dev/null @@ -1 +0,0 @@ --r requirements/prod.txt \ No newline at end of file diff --git a/backend/requirements/common.txt b/backend/requirements/common.txt deleted file mode 100644 index 026cf46..0000000 --- a/backend/requirements/common.txt +++ /dev/null @@ -1,17 +0,0 @@ -django~=4.0.0 -django_filters -djangorestframework -djangorestframework-simplejwt -django-allauth -django-cors-headers -django-debug-toolbar -django-filter -django-extensions -django-pydenticon -djoser -drf-yasg -black -httpie -ipython -Pillow -pycrunch-engine \ No newline at end of file diff --git a/backend/requirements/dev.txt b/backend/requirements/dev.txt deleted file mode 100644 index 878414d..0000000 --- a/backend/requirements/dev.txt +++ /dev/null @@ -1,7 +0,0 @@ --r common.txt - -django-debug-toolbar -django-extensions -drf_yasg -pycrunch-engine -ipython diff --git a/backend/requirements/prod.txt b/backend/requirements/prod.txt deleted file mode 100644 index c3899b0..0000000 --- a/backend/requirements/prod.txt +++ /dev/null @@ -1 +0,0 @@ --r common.txt \ No newline at end of file