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과 같이 참조 불가능
- relation_name에서의 오류 → 참조관계에서의 오류
# 이런 방식으로는 역관계에서도 데이터에 접근 가능
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 |