모델을 빌드하여 해당 모델이 DB Table이 되도록 만들어야 한다.
모든 게시물 데이터를 저장하여 api가 반응 응용 프로그램에 게시 데이터를 제공하여
DB 내부의 모든 게시물을 모델을 이용하여 만들거나, 내부에 저장할 수 있도록 만든다.
models.py
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
class Category(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Post(models.Model):
'''
모델 관리자 PostObject
DB에서 데이터를 전송해주고 모두 출력한다고 가정하면,
처음에는 해당 데이터를 필터링하여 게시된 게시물을 표시하여
여기에 이 사용자 지정 관리자를 생성하므로 기본적으로 쿼리를 생성할 때
데이터에서 모든 개체를 실행하는 대신 게시물 객체를 실행할 수 있다.
=> 그니까 '게시된(published)' 게시물만 쏙쏙 골라먹겠다는 뜻. 나중에 따로 filtering 안 해줘도 됨.
'''
class PostObjects(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status='published')
options = (
('draft', 'Draft'),
('published', 'Published'),
)
category = models.ForeignKey(
Category, on_delete=models.PROTECT, default=1
)
title = models.CharField(max_length=250) # 제목
excerpt = models.TextField(null = True) # 발췌문
content = models.TextField()
# 슬러그는 지금 단계에선 사실 필요없지만, 슬러그는 본질적으로 URL이므로
# 제목을 슬러그화하여 각 게시물의 고유 식별자를 사용하지 않는 방법.
slug = models.SlugField(max_length=250, unique_for_date='published')
published = models.DateTimeField(default=timezone.now)
# 사용자가 새 게시물을 작성하면 해당 게시물과 현재 유저가 서로 종속됨
# CASCADE 속성을 줌으로써 유저가 삭제되면 게시물도 DB 상에서 모두 제거한다.
author = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='blog_posts'
)
# 때론 바로 게시가 되는 걸 원치 않을 수도 있으므로 options을 준다.
status = models.CharField(
max_length=10, choices=options, default='published'
)
objects = models.Manger() # default manager
postobjects = PostObjects() # custom manager
class Meta:
ordering = ('-published', )
def __str__(self):
return self.title
프로젝트를 진행하면서 계속 확장되겠지만 기본적인 모델은 이렇게 구성되어 있다.
CASCADE나 on_delete=model.PROTECT속성에 대해서는 데이터 베이스를 다뤄보지 않았다면
이해하기가 어려운 부분일 거라 생각한다.
사실 막상 해보면 어려운 개념이 아니다.
애초에 DB라는 건 특성을 나타내는 속성들의 집합으로 이루어진 개체와 다른 개체 사이의
관계성에 대한 정의와 상호 간의 제약 조건을 기술한 구조다.
무슨 말이냐면 내가 계정을 지우면 이 블로그도 날아가야 하는 것이 맞다.
하지만 카테고리를 날려버렸다고 해서 그 안의 게시물을 다 지워버리기 보다는
게시물들은 그냥 카테고리가 지정되지 않은 채로 save하고 카테고리만 날려도 되니까
옵션으로 설정을 정해준 것이다.
대충 api처럼 쓸 수 있게 작업하는 거 같은데 확실한지는 모르겠다..
blog_api > views.py
from rest_framework import generics
from blog.models import Post
from .serializers import PostSerializer
# 게시물 목록 뷰가 될 첫 번째 뷰 빌드 (본질적 우리에게 보여줄 항목)
# 어지간한 건 알아서 다 해주는데, 정상 작동을 위해 몇 가지 정보를 우리가 좀 알려줄 필요가 있음
class PostList(generics.ListCreateAPIView):
queryset = Post.postobjects.all() # 해당 데이터를 여기서 사용하겠다.
serializer_class = PostSerializer # 직렬 변환기 (프론트에서 사용할 수 있는 형식으로 모두 변환)
pass
# 게시물 세부 정보 뷰. (검색과 삭제)
class PostDetail(generics.RetrieveDestroyAPIView):
pass
blog_api > serialize.py
from rest_framework import serializers
from blog.models import Post
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = (
'id',
'title',
'author',
'excerpt',
'content',
'status'
)
현재 PostList에서 queryset으로 Post.postobjects.all()을 지정해주면 get_queryset() 중복 이슈가 발생하는데
어떻게 해결해야할지 모르겠어서 그냥 Post.objects.all()을 통해 기본 생성자를 사용하였다.
서버를 실행시켜서 apl/ 로 들어가보면 다음과 같은 화면을 확인할 수 있다.
이 화면에서 말해주는 정보는 우리가 http 200을 사용하고 있다고 한다.
여기서 200은 http 응답코드이며, 현재 매우 정상적으로 액세스 했음을 알려준다. (대충 잘 했다는 뜻)
중요한 것 GET, POST인다.
GET은 DB에서 데이터를 가져오기 위해 만들 수 있는 요청이고,
POST는 실제로 서버에 데이터를 보내는 방법이다.
혹시나 싶어 아무 값이나 넣어봤지만 바로 외래키 관련 이슈가 발생한다.
카테고리가 default로 1의 값을 가지려 하는데, 우리가 아직 카테고리 번호 1을
실제로 구축하지 않았기 때문에 이런 문제가 발생한 것이다.
그렇다면 관리자 권한으로 카테고리에 엑세스 가능한지 확인하여 수동으로 진행하면 된다.
blog > admin.py
from django.contrib import admin
from . import models
# Register your models here.
@admin.register(models.Post)
class AuthorAdmin(admin.ModelAdmin):
list_display = ('title', 'id', 'status', 'slug', 'author')
prepopulated_fields = {'slug' : ('title',),}
admin.site.register(models.Category)
이렇게 되면 첫 번째 카테고리에 id번호 1이 될 것이므로 자동으로 django에 할당된다.
이제 드디어 정상적으로 게시가 되고 있고, 코드가 정상적으로 돌아가는 것을 알 수 있다.
다시 blog_api > views.py로 돌아가서 PostDetail을 마저 작성해주자.
from rest_framework import generics
from blog.models import Post
from .serializers import PostSerializer
# 게시물 목록 뷰가 될 첫 번째 뷰 빌드 (본질적 우리에게 보여줄 항목)
# 어지간한 건 알아서 다 해주는데, 정상 작동을 위해 몇 가지 정보를 우리가 좀 알려줄 필요가 있음
class PostList(generics.ListCreateAPIView):
queryset = Post.objects.all() # 해당 데이터를 여기서 사용하겠다.
serializer_class = PostSerializer # 직렬 변환기 (프론트에서 사용할 수 있는 형식으로 모두 변환)
# 게시물 세부 정보 뷰. (검색과 삭제)
class PostDetail(generics.RetrieveDestroyAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
이렇게 되면 게시물의 id값을 통해서 개별 게시물을 볼 수 있게 된다.
api/1로 접근하자 id가 1인 게시물의 데이터를 보여준다.
또한 앞서 설명했듯이 RetrieveDestroyAPIView는 Delete 기능을 지원한다고 했었다.
api 우상단에 제대로 그 기능을 지원하는 것을 확인 가능하다.
만약 RetrieveAPIView로 고치면 Delete 버튼이 사라진다.
권한을 부여하는 방법은 세 가지가 있다.
1. 프로젝트 전체에 정의하는 방법
2. 뷰(?)에 권한을 정의하는 방법
3. 개체당 서로 다른 권한을 적용하는 방법
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES' : [
'rest_framework.permissions.IsAuthenticated',
]
}
setting.py 최하단에 이렇게 정의하였는데 IsAuthenticated가 관리자 계정만 api를 보는 것을 허용한다.
하지만 우리가 만들고자 하는 최종 프로그램은 이를 AllowAny로 허용한 채로 보안 이슈를 해결해야한다.
(뭐라는 건데 대체)