Fat Models 를 잘 써보자!
일단 https://www.softkraft.co/django-best-practises/ 이 링크를 참고했을 때 느낀점은 다음과 같다.
-> 우선 manager 를 통해 view 에 있는 코드를 잘 관리하도록 바꿔보자!, view 에 너무 많은 로직이 들어갔다...
그래서 후보군 model methods, classmethods, properties, manager methods 인데..
view 가 아닌 manager 나 models 에 로직을 넣는 방식으로 가도록 코드를 개편해보겠다!!
일단 내가 작성한 코드는 전부 이렇게 views.py 에 모든 로직이 들어가있다.
바꿔보자!!
https://docs.djangoproject.com/en/5.0/topics/db/managers/
Managers | Django documentation
The web framework for perfectionists with deadlines.
docs.djangoproject.com
일단 개편을 위한 공식문서..
와 적당한 오픈 소스를 발견했다!
https://github.com/adlnet/ADL_LRS/blob/c441821b9d0f92e0ca263cf446b2223b67e84b19/lrs/models.py#L58
ADL_LRS/lrs/models.py at c441821b9d0f92e0ca263cf446b2223b67e84b19 · adlnet/ADL_LRS
ADL's Open Source Learning Record Store (LRS) is used to store learning data collected with the Experience API. - adlnet/ADL_LRS
github.com
일단 이 코드를 보고 대략 느낌을 파악했다!
일단 위의 오픈소스를 확인했을 때 모든 로직이 model에 들어가 있었다.
사실상 views.py 에서는 요청을 받으면 거의 Return 만 하는 정도였다.
일단 짜봤다. (with copilot)
이 코드를 보니 정말 내가 있는 모든 코드를 여기에 넣어서 함께 진행할 수 있다는 자신감이 생겼다! (중복 코드 삭제!!)
그런데 도입하려고 여러가지 자료를 보면서 생각해 봤는데 이런 문제가 있었다.
문제사항
- subtodo, todo, category 에서 delete와 read의 로직은 완벽히 겹친다. (Todo의 get 제외 <- 조금 다르다)
- create, update는 파라미터만 제외하면 겹친다.
그래서 고민하고 있었던 점
1. 투두, 서브투두, 카테고리를 각각 매니저를 만들어야하나?
2. read, delete 는 추상 카테고리로 만들고 각각의 매니저에 상속받도록 해서 create, update 를 구현해야하나?
그래서 룰루루팀장님께 물어본 결과는 이렇다!
일단 코드를 슥스슥 보시더니 validation 이 필요한 친구는 serializer 에 빼고, 쿼리문을 통해 가져오는 친구들은 manager 로 넣어보시는 건 어떠신가요?
그래서 일단 결정난대로 적용해볼 예정이다!
문제 상황 1
일단 우리 todo 가 start_date, end_date를 받는 상황인데
하나가 null 이면 아래의 쿼리에서 안받아지는 문제를 확인했다.
그래서 받아지도록 수정했다.
지금 보니 end_date 보다 작아야 된다고 되어있어서 제대로 불러와지지 않는 문제가 있었다.
나는 항상 date 없는 걸로 테스트 해서 그런가보다. 이젠 정확히 테스트 해야지
엄청나게 깔끔해진 것을 확인할 수 있다!!
일단 Model manager 상태
class TodosManager(models.Manager):
def delete(self, instance):
instance.deleted_at = timezone.now()
instance.save()
return instance
def delete_many(self, instances):
for instance in instances:
instance.deleted_at = timezone.now()
instance.save()
return instances
def get_queryset(self):
return super().get_queryset().filter(deleted_at__isnull=True)
def get_with_id(self, id):
return self.get_queryset().get(id=id)
def get_with_user_id(self, user_id):
return self.get_queryset().filter(user_id=user_id)
def get_with_date(self, user_id, start_date, end_date):
return self.get_queryset().filter(user_id=user_id, start_date__gte=start_date, end_date__lte=end_date)
def get_subtodo(self, todo_id):
return self.get_queryset().filter(todo=todo_id)
def get_inbox(self, user_id):
return Todo.objects.filter(
user_id=user_id,
deleted_at__isnull=True
).annotate(
children_count=Count('children', filter=Q(children__deleted_at__isnull=True, children__date__isnull=True))
).filter(
Q(end_date__isnull=True, start_date__isnull=True) | Q(children_count__gt=0)
).prefetch_related(
Prefetch('children', queryset=SubTodo.objects.filter(deleted_at__isnull=True, date__isnull=True).order_by('order'))
)
def get_today_with_date(self, user_id, start_date, end_date):
return Todo.objects.filter(
user_id = user_id,
deleted_at__isnull=True
).filter(
Q(Q(end_date__gte=end_date) | Q(end_date__isnull = True))
& Q(Q(start_date__lte=start_date) | Q(start_date__isnull = True))
& Q(Q(end_date__isnull = False) | Q(start_date__isnull = False))
).filter(
Q(end_date__isnull = False) | Q(start_date__isnull = False)
).order_by('order').prefetch_related(
Prefetch('children', queryset=SubTodo.objects.filter(deleted_at__isnull=True, date__isnull=False).order_by('order'))
)
def get_today(self, user_id):
return Todo.objects.filter(
user_id = user_id, deleted_at__isnull=True
).filter(
Q(end_date__isnull = False) | Q(start_date__isnull = False)
).order_by('order').prefetch_related(
Prefetch('children', queryset=SubTodo.objects.filter(deleted_at__isnull=True, date__isnull=False).order_by('order'))
)
view가 진짜 간단해져서 일단 계속 해볼 예정이다!
get 이 manager 에서 많은 비중을 차지하니 더이상 별로 크게 안 늘어날 것 같다.
일단 update, post, delete 를 내일 적당히 계속 변경해봐야겠다. 생각보다 리팩토링 시간이 많이 걸리는 구나.. + 테스트 코드 작성
매니저와 시리얼라이저 차이가 갑자기 헷갈려서.. 내일 한번 지대루 찾아서 다시 정리해봐야 겠다.
'소프트웨어 마에스트로 > BackEnd(Django)' 카테고리의 다른 글
[백엔드] logging 및 api 관련 기록 (0) | 2024.08.05 |
---|---|
[백엔드] fat model 적용기 2 + 테스트케이스(pytest) (5) | 2024.08.02 |
[백엔드] django chatgpt prompting + api 제작기 (0) | 2024.08.01 |
[백엔드] 코드의 미를 위하여 (fat model) + django logging (0) | 2024.07.31 |
[백엔드] django serializer 관련 여러가지 요청사항 (0) | 2024.07.30 |