장고에서 모델 소프트 삭제 구현하기

런던행·2020년 5월 26일
1

Django 업그레이드

목록 보기
1/17
post-thumbnail

필자는 라라벨을 재미있게 다루다가 이직을 하면서 python 장고를 다루게 되었다. 라라벨에서 기본적으로 제공하는 많은 기능들은 장고에서 볼수가 없었기에 라라벨에서 기본으로 제공하는 좋은 기능들을 장고에서 솔선수범 구현하고 있어 글을 남기고자 한다.

Refrence

모델 소프트 삭제란..( soft delete )

  • 데이터베이스 레코드를 물리적으로 삭제하지 않고 삭제여부를 컬럼으로 나타내는 방식을 말한다. 이와 반대로 물리적으로 일반적으로 삭제하는 방법을 hard delete라고 말한다.

구현 순서

  1. 레코드 삭제 여부를 나타내는 deleted_at 컬럼을 정의한다.
  2. 여러 모델에서 상속 할 수 있게 mixin 클래스 기반으로 설계한다.
  3. 커스텀 매니저를 구현한다.

Mixin SoftDelete 클래스 정의

class SoftDeleteModel(models.Model):

   deleted_at = models.DateTimeField('삭제일', null=True, default=None)

   class Meta:
       abstract = True  # 상속 할수 있게 

   objects = SoftDeleteManager()  # 커스텀 매니저 

   def delete(self, using=None, keep_parents=False):
       self.deleted_at = now()
       self.save(update_fields=['deleted_at'])

   def restore(self):  # 삭제된 레코드를 복구한다.
       self.deleted_at = None
       self.save(update_fields=['deleted_at'])

SoftDeleteManage 커스텀 매니저 정의

class SoftDeleteManager(models.Manager):
    use_for_related_fields = True  # 옵션은 기본 매니저로 이 매니저를 정의한 모델이 있을 때 이 모델을 가리키는 모든 관계 참조에서 모델 매니저를 사용할 수 있도록 한다.

    def get_queryset(self):
        return super().get_queryset().filter(deleted_at__isnull=True)

사용 예

class EstimateRequestBoard(CommonModel, SoftDeleteModel):

    ANSWER_CHOICES = (
        (0, '없음'),
        (1, '완료'),
    )

    title = models.CharField(verbose_name='제목', max_length=100)
    content = models.TextField('내용')

테스트 코드

@pytest.mark.django_db
def test_softdelete():
    # Given
    mock_user = User.objects.create(username='Testuser')
    title = "test"
    content = "testcontent"

    achievement = AchievementBoard()
    achievement.title = title
    achievement.content = content
    achievement.owner = mock_user
    achievement.save()

    achievement2 = AchievementBoard()
    achievement2.title = title
    achievement2.content = content
    achievement2.owner = mock_user
    achievement2.save()

    achievement3 = AchievementBoard()
    achievement3.title = title
    achievement3.content = content
    achievement3.owner = mock_user
    achievement3.save()

    # When & Then
    achievement.delete()  # 삭제한다.
    assert 2 == AchievementBoard.objects.count()  # 삭제해서 총 2개

    # When & Then
    achievement.restore()  # 복구한다.
    assert 3 == AchievementBoard.objects.count()  # 복구해서 총 3개

정리

충분히 쉽게 장고에서도 소프트삭제를 구현할 수 있다. 위 기능은 아직 걸음마 단계이고 점차적으로 추가 할 생각이다.

profile
unit test, tdd, bdd, laravel, django, android native, vuejs, react, embedded linux, typescript

1개의 댓글

comment-user-thumbnail
2021년 7월 16일

우와.. 잘읽었습니다. 책 내시면 알려주세요! 혹시 hard delete의 경우 별도로 명시하는 것이 좋나요? 혹은 명시할 수 있는 방법이 있을까요?

답글 달기