본문 바로가기

PYTHON-BACK

#파이썬 30일차_게시판 만들기2

728x90

29일차 이어서 진행

 
  • View + 기본 Permission
    • 개발할 프로필 관련 기능
      • 읽어오기, 수정하기 → generics.RetrieveUpdateAPIView를 이용하여 기능 구현 가능
    • 요구되는 권한
      • 프로필 조회: 모두
      • 프로필 수정: 해당 프로필의 소유자만 가능 → permisstion_class 필드 설정을 통해 구현
    • API마다 필요한 권한이 다른 경우
      • 권한이 미리 조합된 클래스 활용
      • 직접 권한 클래스를 만들어서 설정
    • Django Rest Framework에서 제공하는 권한 종류의 예시
      • AllowAny: 모든 요청을 통과시킴. 어떤 인증도 불필요함
      • IsAuthenticated: 인증된 경우에만 통과시킴. 즉 우리가 선언한 인증 방법으로 인증을 통과한 요청만 가능한 권한
      • IsAdminUser: 관리자인 경우에만 통과
# users/views.py

from .serializers import RegisterSerializer, LoginSerializer, ProfileSerializer
from .models import Profile
from rest_framework import generics

class ProfileView(generics.RetrieveUpdateAPIView):
    queryset = Profile.objects.all()
    serializer_class = ProfileSerializer
# users/permissions.py

from rest_framework import permissions

class CustomReadOnly(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            return True
        return obj.author == request.user
# users/urls.py
from django.urls import path
from .views import RegisterView, LoginView, ProfileView

urlpatterns = [
    path('register/', RegisterView.as_view()),
    path('login/', LoginView.as_view()),
    path('profile/<int:pk>/', ProfileView.as_view())
]

 

  • Admin 페이지 등록
    • User 모델만 관리자 페이지에 등록하게 되면 프로필 모델은 나타나지 않음
    • 프로필 모델을 따로 등록하면 관리자 페이지에서는 볼 수 있지만 유저 테이블과 프로필 테이블이 분리되어 있으므로 불편함
    • 아래와 같은 방법으로 두 모델이 같은 모델인 것처럼 함께 볼 수 있음

 

# user/admin.py
from django.contrib import admin

from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User
from .models import Profile


class ProfileInline(admin.StackedInline):
    model = Profile
    can_delete = False
    verbose_name_plural = "profile"


class UserAdmin(BaseUserAdmin):
    inlines = (ProfileInline, )


admin.site.unregister(User)
admin.site.register(User, UserAdmin)
# Migration
python manage.py makemigrations
python manage.py migrate
python manage.py runserver

Django REST Framework + React.js 게시판 만들기 (2): 게시글 기능

 

1. 게시글 기능 정리

 
  • 게시글 생성
  • 게시글 1개 가져오기
  • 게시글 목록 가져오기(가져오는 개수 제한하기)
  • 게시글 수정하기
  • 게시글 삭제하기
  • 게시글 좋아요 기능
  • 게시글 필터링(좋아요 누른 글/내가 작성한 글)
  • 게시글 각 기능마다 권한 설정

2. 게시글 모델 만들기 & 마이그레이션

 

 

2.1 Start App

python manage.py startapp posts

 

 

2.2 settings.py 설정

 

# board/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'rest_framework.authtoken',
    'users',
    'corsheaders',
    'posts',
]

 

2.3 Models

  • 모델의 필드 구성
    • 저자
    • 저자 프로필
    • 제목
    • 카테고리
    • 본문
    • 이미지 → 이미지가 없을 때는 default.png 파일 표시
    • 좋아요 누른 사람들 → 다대다(ManyToMany)
    • 글이 올라간 시간
# posts/models.py
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
from users.models import Profile

class Post(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    profile = models.ForeignKey(Profile, on_delete=models.CASCADE, blank=True)
    title = models.CharField(max_length=128)
    category = models.CharField(max_length=128)
    body = models.TextField()
    image = models.ImageField(upload_to='post/', default='default.png')
    likes = models.ManyToManyField(User)
    published_date = models.DateTimeField(default=timezone.now)

 

  • 마이그레이션
  • 마이그레이션 시 오류 발생
    • author, likes가 모두 User를 참조하는 중 → 오류 발생

마이그레이션 시 오류 발생

  • author, likes가 모두 User를 참조하는 중 → 오류 발생

  • 관련 내용
    • relation_name에서의 오류 → 참조관계에서의 오류
      • 저자 → Users 모델을 ForeignKey로 참조 중(author와 연관) → post.author.username과 같이 참조 가능
      • User 모델에서는 post라는 이름을 모름 → user.post.title과 같이 참조 불가능
 # 이런 방식으로는 역관계에서도 데이터에 접근 가능
                user = User.objects.get(pk=1)
                posts = user.post_set.all()

 

  • 여기서 post_set 대신 사용하는 것이 related_name
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
  • 위와 같이 이름을 지정하고
user = User.objects.get(pk=1)
posts = user.posts.all()
  • 이렇게 하면 유저가 작성한 글들을 확인할 수 있음
  • 위의 오류에서는 author, likes가 모두 User를 참조하는 중
    → 둘 다 related_name을 지정하지 않고 역관계로 User에서 author, likes에 참조하려고 하므로
    → user.post_set.all()이 되어 어떤 것을 참조해야 하는지 구분할 수 없음
    → 오류 발생
# posts/models.py
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
from users.models import Profile

class Post(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
    profile = models.ForeignKey(Profile, on_delete=models.CASCADE, blank=True)
    title = models.CharField(max_length=128)
    category = models.CharField(max_length=128)
    body = models.TextField()
    image = models.ImageField(upload_to='post/', default='default.png')
    likes = models.ManyToManyField(User, related_name='like_posts', blank=True)
    published_date = models.DateTimeField(default=timezone.now)
# Migration
python manage.py makemigrations
python manage.py migrate

 

3. 게시글 기능

3.1 Serializers

 

  • PostSerializer()
    • 해당 게시글에 대한 모든 정보를 JSON으로 변환하여 전달하는 역할을 수행
    • profile 필드를 작성하지 않으면 profile 필드에는 profile의 PK 값만 나타나므로 ProfileSerializer를 포함하도록 함
      • 이런 형태를 Nested Serializer라고 함
  • PostCreateSerializer()
    • 게시물을 등록할 때 유저는 제목, 카테고리, 본문, 이미지 등만 입력함
    • 나머지는 시스템이 알아서 채워주거나 빈칸으로 남겨둠
    • 시리얼라이저는 유저가 입력한 데이터를 검증하고 Django 데이터로 변환하여 저장하게 하는 역할을 수행

 

# posts/serializers.py
from rest_framework import serializers

from users.serializers import ProfileSerializer
from .models import Post


class PostSerializer(serializers.ModelSerializer):
    profile = ProfileSerializer(read_only=True)

    class Meta:
        model = Post
        fields = ("pk", "profile", "title", "body", "image", "published_date", "likes")


class PostCreateSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ("title", "category", "body", "image")

 

3.2 Views(CRUD) + 권한

 
  • 게시글은 모든 CRUD 기능이 포함되어 있으므로 ViewSet을 사용하여 쉽게 적용 가능함
  • 단, 각기 다른 시리얼라이저를 적재적소에 활용하도록 코드를 작성해야 함
  • 게시글 생성 시 유저가 입력해 주지 않는 저자 정보를 같이 넣을 수 있도록 함

  • 요구되는 권한
    • 게시글의 조회: 모든 사람
    • 게시글의 생성: 인증된 유저만 가능
    • 게시글의 수정/삭제: 해당 글의 작성자만 가능
  • User에서의 CustomReadOnly와 다른 점
    • 각 객체별 권한 뿐만 아니라 전체 객체에 대한 권한도 포함해야 함(목록 조회/생성)
      • has_permission()을 함께 구현
# posts/permissions.py
from rest_framework import permissions


class CustomReadOnly(permissions.BasePermission):
    def has_permission(self, request, view):
        if request.method == 'GET':
            return True
        return request.user.is_authenticated

    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            return True
        return obj.author == request.user

3.3 URL

 
  • ViewSet을 사용하면 라우터가 함께 따라옴
    • 라우터에 등록된 url을 활용함
# posts/urls.py
from django.urls import path
from rest_framework import routers

from .views import PostViewSet

router = routers.SimpleRouter()
router.register('posts', PostViewSet)

urlpatterns = router.urls
  • 프로젝트의 urls.py에는 라우터가 이미 posts를 설정해줌
    • posts를 설정하면 실제 주소는 localhost:8000/posts/posts/와 같이 중복됨
#board/urls.py
from django.urls import path, include
from django.contrib import admin

from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls),
    path('users/', include('users.urls')),
    path('', include('posts.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

 

  • 실행
python manage.py runserver

 

 

 

 

728x90

'PYTHON-BACK' 카테고리의 다른 글

#파이썬 31일차_이커머스 클론코딩1  (0) 2024.08.16
#파이썬 31일차_게시판 만들기3  (0) 2024.08.16
#파이썬 29일차_게시판 만들기1  (0) 2024.08.13
#파이썬 28일차_MYSQL5  (0) 2024.08.12
#파이썬 27일차_MYSQL4  (0) 2024.08.09