git add . 을 무지성으로 쓰다보니 .DS_store가 생겼따.
find . -name .DS_Store -print0 | xargs -0 git rm --ignore-unmatch -f
git global config를 쓴 좋은 글이 있다.
echo ".DS_Store" >> ~/.gitignore_global
echo "._.DS_Store" >> ~/.gitignore_global
echo "**/.DS_Store" >> ~/.gitignore_global
echo "**/._.DS_Store" >> ~/.gitignore_global
git config --global core.excludesfile ~/.gitignore_global
아니면
com.apple.desktopservices DSDontWriteNetworkStores true
이렇게 하면 mac에서 DS_Store가 생기는 걸 아예 방지할 수 있다고 한다. 이런 세팅들은 fig에다가 저장시켜 놓으면 좋다.
암튼 본론으로 돌아가자. graphql을 쓸려고 생각햇는데 굳이 restapi를 사용하지 않을 이유도 없어서 django restframework로 방향을 바꿧다.
/blog-backend/blog-api
python3 manage.py startapp blog
poetry add djangorestframework
pk를 통해 api를 만드는 건 쟝고에선 일도 아니다. generics가 다 알아서 해주기 때문이다. 그러나 좀 까탈스러운게 post를 요청할 때 id가 아닌 slug를 통해 불러와야 더 user friendly하기 때문에 약간 생각할 거리가 있다는거다.
우리가 만들어야 되는 모델을 생각하자. Tag는 20자 정도가 안 넘어가는 text field로 get과 post 이외의 기능이 필요해보이지 않는다. Post는 좀 들어가는 게 많고 CRUD를 다 할 수 있어야 된다.
대충 정리햇으니 blog란 app을 만들고 여기다가 tag랑 post모델을 만들어주고 url conf, view를 다 만들거다.
blog/models.py
from django.db import models
from django.utils.text import slugify
import uuid
class Tag(models.Model):
name = models.TextField(max_length=20, unique=True)
def __str__(self) -> str:
return self.name
class Post(models.Model):
class Meta:
ordering = ["-published"]
slug = models.SlugField(max_length=50, unique=True, null=True)
uuid = models.UUIDField(default=uuid.uuid4, editable=False)
title = models.TextField(max_length=50, unique=True)
subtitle = models.TextField(max_length=150, blank=True)
body = models.TextField()
meta_description = models.TextField(max_length=150, blank=True)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
published = models.DateTimeField(blank=True, null=True)
is_published = models.BooleanField(default=False)
tags = models.ManyToManyField(Tag, blank=True)
def generate_slug(self):
return slugify(self.title)
def save(self, *args, **kwargs):
if not self.id:
self.slug = self.generate_slug()
super(Post, self).save(*args, **kwargs)
slug는 개인이 만들기보다 title을 받으면 자동으로 만들어지도록 했다.
대충 이런 식이다.
blog/serializers.py
from rest_framework import serializers
from .models import Tag, Post
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = "__all__"
def validate_empty_values(self, data):
return super().validate_empty_values(data)
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = "__all__"
lookup_field = "slug"
진짜 저 lookup_field가 뭔지 몰라서 온갖 serializer는 다 시도해봤다. 보통 구글링하면 다 나오는데 drf에서 id 대신 slug를 쓰는게 너무 쉽다고 생각이 되는 건지 아니면 별로 안 좋은 방법이라 생각이 되는건지 결과가 좀 적었다. docs를 tutorial말고 세부 구현 사항을 읽게 돼서 오히려 좋은 거 같기도 하는데 어쨋든 좀 힘들었다.
blog/views.py
from rest_framework import generics, viewsets
from .models import Tag, Post
from .serializers import TagSerializer, PostSerializer
class TagList(generics.ListCreateAPIView):
queryset = Tag.objects.all()
serializer_class = TagSerializer
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
lookup_field = "slug"
blog/urls.py
from django.urls import path
from blog import views
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r"posts", views.PostViewSet)
urlpatterns = [
path("tags/", views.TagList.as_view()),
]
urlpatterns += router.urls
insomnia로 GET을 써보니 잘 된다.
일단 post api url은 /posts/slugify-title로 세팅했다. 이제 남은 고민거리는 후에 마크다운에 파일들 경로를 어떻게 잡을 것인가였는데 이 글을 velog에서 쓰면서 힌트를 얻었다. cdn domain/images/post/post-uuid/filename 식으로 마크다운에 표시를 한다. cdn에 대해 아는 게 별로 없는데 가벼운 용량은 무료로 호스팅해주는 데가 있는지 찾아볼 예정.
또, sqlite를 쓰기 싫기 때문에 postgresql로 마이그레이션 하기, comments 모델 만들기, 내부에서 검색되게 query path를 추가하는 등을 해야 하지만 일단 다음 스텝으로는 받아온 post response를 post 페이지에 띄우는 걸 해볼려고 한다.