설표의 장고




티스토리 이사 프로젝트)model 작성하기





( 수정됨)


유저 모델을 작성했으니 이번에는 게시글을 작성하는데 사용할 모델을 작성해야합니다.
지난 번에 티스토리 백업 파일을 살펴보며 티스토리에서 작성된 게시물에 필요한 속성들을 알아보았습니다.

이전에 확인한 파일을 기반으로 다음과 같은 모델을 작성했습니다.

# seolpyo_tistory/models/post.py

from django.conf import settings
from django.db import models
from django.shortcuts import resolve_url
from django.utils import timezone
from django.utils.text import slugify


use_slug = getattr(settings, 'TISTORY_SLUG', False)

class BaseCategory(models.Model):
    class Meta:
        abstract = True
        verbose_name = '카테고리'
        verbose_name_plural = '카테고리'

    name = models.CharField(
        verbose_name='카테고리명',
        unique=True,
        max_length=10,
    )

    parent = models.ForeignKey(
        'self',
        on_delete=models.PROTECT,
        related_name='tistory_category_set',
        null=True,
    )

    def __str__(self):
        if self.parent: return f'{self.parent}/{self.name}'
        return self.name

    def get_absolute_url(self):
        if self.name == '공지사항': return resolve_url('seolpyo_tistory:list_notice')
        if self.name == '페이지': return '#'
        if self.parent: return resolve_url('seolpyo_tistory:category', self.parent.name, self.name)
        return resolve_url('seolpyo_tistory:category', self.name)

class Category(BaseCategory):
    pass


class BaseTag(models.Model):
    class Meta:
        abstract = True
        verbose_name = '태그'
        verbose_name_plural = '태그'

    name = models.CharField(
        verbose_name='태그명',
        unique=True,
        max_length=19,
    )

    def __str__(self): return self.name

    def get_absolute_url(self): return resolve_url('seolpyo_tistory:tag', self.name)

class Tag(BaseTag):
    pass


def default_category():
    for i in ['공지사항', '페이지', '서식',]: Category.objects.get_or_create(name=i)
    return


class PostManager(models.Manager):
    def get_queryset(self): return super().get_queryset().prefetch_related('author', 'category', 'tags')

class BasePost(models.Model):
    class Meta:
        abstract = True
        verbose_name = '티스토리/글'
        verbose_name_plural = '글'
        ordering = ['-date_post', '-pk']

    objects = PostManager()

    author = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.DO_NOTHING,
        related_name='tistory_post_set',
        verbose_name='작성자',
        editable=False,
    )

    category = models.ForeignKey(
        Category,
        on_delete=models.CASCADE,
        related_name='tistory_post_set',
        verbose_name='카테고리',
        blank=True,
        null=True,
        default=default_category,
    )

    date_post = models.DateTimeField(
        verbose_name='발행시간',
        default=timezone.localtime,
    )

    slug = models.SlugField(
        verbose_name='슬러그',
        unique=True,
        editable=use_slug,
        null=True,
        allow_unicode=True,
    )

    password = models.CharField(
        verbose_name='비밀번호',
        max_length=20,
        blank=True,
    )

    is_private = models.BooleanField(
        verbose_name='비공개로 설정',
        default=False,
    )

    title = models.CharField(
        verbose_name='제목',
        max_length=29,
    )

    slug_title = models.SlugField(
        verbose_name='슬러그(자동생성)',
        unique=True,
        editable=False,
        allow_unicode=True,
    )

    content = models.TextField(
        verbose_name='내용',
        max_length=9999,
    )

    tags = models.ManyToManyField(
        Tag,
        related_name='tistory_post_set',
        verbose_name='태그',
        blank=True,
    )

    def __str__(self): return self.title

    def get_absolute_url(self):
        if self.category and self.category.name == '공지사항': return resolve_url('seolpyo_tistory:notice', self.pk)
        if self.category and self.category.name == '페이지': return resolve_url('seolpyo_tistory:page', self.pk)
        if self.category and self.category.name == '서식': return resolve_url('seolpyo_tistory:template', self.pk)
        if use_slug: return resolve_url('seolpyo_tistory:detail_slug', self.slug if self.slug else self.slug_title)
        return resolve_url('seolpyo_tistory:detail', self.pk)

    def save(self, *args, **kwargs):
        self.slug_title = slugify(self.title, allow_unicode=True)
        if self.category and self.category.name == '서식': self.is_private = True
        return super().save(*args, **kwargs)

class Post(BasePost):
    pass

태그와 카테고리

태그와 카테고리를 각각 ManyToManyField와 ForeignKey로 연결하고, 카테고리의 경우 부모 카테고리를 선택할 수 있도록 하는 것으로 하위 카테고리를 구현했습니다.

게시글

단순히 제목과 내용, 발행시간 정도만 있으면 될 줄 알았으나, 티스토리에서 게시글, 서식, 공지사항, 페이지를 모두 같은 객체로 보관하고 있기 때문에 처음 생각보다 사용하는 필드가 많아졌습니다.

각 필드들의 설명은 다음과 같습니다.

  • author: 작성자 정보입니다.
  • title : 게시글의 제목입니다. 티스토리의 경우 제목 글자수에 따로 제한을 두지 않는 것으로 보이지만, 제목이 너무 길면 가독성이 떨어지고 지나치게 길게 작성할 이유가 없기 때문에 최대 29글자로 제한을 두었습니다.
  • content: 게시글의 내용입니다. max_length가 필요할까 싶긴 하지만, 일단 9,999자로 제한을 해두었습니다.
  • date_post: 게시글의 발행시간입니다. 글 작성을 요청한 시간이 기본값으로 지정됩니다.
  • slug: 문자 주소를 사용하는 경우 사용하게 되는 슬러그입니다. 문자 주소 사용 여부는 settings.py에서 제어 가능한 변수를 이용하게 됩니다.
  • slug_title: 티스토리에서 사용하는 slug(문자 주소)인데, 티스토리에서는 문자 주소를 지정하지 않는 경우 게시글 제목을 슬러그로 변환하여 사용합니다.
    이를 반영하여 추가한 필드입니다.
  • password: 보호 글에서 사용하는 조회 비밀번호입니다.
  • category: 카테고리를 선택합니다. 공지사항, 페이지, 서식은 모두 카테고리를 통해 분류합니다.



이 글의 댓글 기능은 일부러 막아놓았습니다. 궁금한 내용이 있다면 게시판을 이용해주세요!


공감 : 0