반응형
단축 url 프로젝트를 진행하면서 조회수 관련해서 통계를 내는 부분이 있었는데, 그 부분에 관련된 예제이다.
Models.py
from django.db import models
from core.models import TimeStampedModel
class ShortURL(TimeStampedModel):
user = models.ForeignKey("users.User", related_name="user_url", on_delete=models.CASCADE)
origin_url = models.URLField(verbose_name="원본 url")
short_url = models.CharField(max_length=100, verbose_name="단축 url")
request_count = models.IntegerField(default=0, verbose_name="요청 횟수")
expiration_date = models.DateTimeField(null=True, blank=True, verbose_name="만료일")
is_active = models.BooleanField(default=True, verbose_name="활성화 여부")
class Meta:
db_table = "short_url"
class DailyCount(models.Model):
short_url = models.ForeignKey(ShortURL, related_name="daily_counts", on_delete=models.CASCADE)
date = models.DateField(verbose_name="날짜")
count = models.IntegerField(default=1, verbose_name="요청 횟수")
class Meta:
db_table = "daily_count"
unique_together = ("short_url", "date")
indexes = [models.Index(fields=["short_url", "date"])]
원래 존재하던 ShortURL모델과 관계를 맺는 DailyCount라는 모델을 생성하였다.
unique_together: 여러 개의 필드 조합이 함께 유일함을 유지하기 위해서 short_url과 date필드의 조합으로 지정해 주었다. 즉, 각각의 필드에 유니크 옵션을 주는 것이 아니라 url정보와 날짜의 조합에 유니크 옵션을 주었다는 이야기이다.
indexs: 데이터 베이스에서 특정 필드를 빠르게 검색하기 위한 인덱스이다. short_url과 date필드를 복합 인덱스로 사용하므로써 검색 속도를 올릴 수 있도록 하였다.
ViewSet
class ShortenerViewSet(
CreateModelMixin,
RetrieveModelMixin,
UpdateModelMixin,
DestroyModelMixin,
MappingViewSetMixin,
SimpleJWTMixin,
GenericViewSet,
):
serializer_action_map = {
...
"statistics": ShortenerCreateSerializer,
}
queryset = ShortURL.objects.filter(is_active=True)
lookup_field = "short_url"
...
def statistics(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
res = {
"origin_url": instance.origin_url,
"request_count": instance.request_count,
"daily_count": serializer.get_daily_count(instance),
"weekly_count": serializer.get_weekly_count(instance),
}
return Response(res)
viewset는 다른 일반적인 viewset과 차이가 없이 구현하였다. 조금의 차이점이라면 serializer.data가 아니라 원하는 데이터를 추출하여 res 딕셔너리에 넣어서 전송해 주었다는 점이다.
serializers.py
def get_weekly_count(self, instance):
today = timezone.now().date()
seven_days_ago = today - timedelta(days=7)
counts = DailyCount.objects.filter(short_url=instance, date__gte=seven_days_ago)
weekly_count = sum(count.count for count in counts)
return weekly_count
def get_daily_count(self, instance):
today = timezone.now().date()
seven_days_ago = today - timedelta(days=7)
counts = DailyCount.objects.filter(short_url=instance, date__gte=seven_days_ago)
result = {count.date.isoformat(): count.count for count in counts}
return result
def plus_daily_count(self, instance):
today = timezone.now().date()
try:
daily_count = DailyCount.objects.get(short_url=instance, date=today)
daily_count.count += 1
daily_count.save()
except DailyCount.DoesNotExist:
DailyCount.objects.create(short_url=instance, date=today)
get_weekly_count: 주간 조회수를 계산하는 메서드로, 현재 날짜를 시준으로 7일전 부터 오늘까지의 일간 조회수를 가져와 합산하여 주간 조회수를 반환한다.
get_daily_count: 일간 조회수를 가지고와 날짜와 조회수를 딕셔너리 형태로 반환한다.
plus_daily_count: 조회시 일간 조회수를 증가시킨다. 현재 날짜에 해당하는 DailyCount가 이미 존재하면 조회수를 증가시키고 그렇지 않으면 새로운 객체를 생성하여 조회수를 1로 초기화한다.
반응형
'Django > DRF' 카테고리의 다른 글
[Django] 동적 이미지 생성, CSV & 엑셀 응답을 처리하는 View 만들기 (0) | 2024.06.10 |
---|---|
[Django] timezone 모듈과 datetime 모듈의 차이 & 사용법 (1) | 2024.06.04 |
[JWT] Custom JWT 관련 middleware와 backend (0) | 2024.05.31 |
[회고록] 효율적인 Django 시작하기: initialize_django 프로젝트 (0) | 2024.05.27 |
[Custom] Custom Logging Mixin With CRUD Mixin (0) | 2024.05.23 |