장고 - annotate를 이용한 숙소 정렬 기능 구현하기
- 이번 포스트에서는 annotate를 이용하여 원하는 기준으로 정렬하는 방법에 대해 알아볼 것이다.
ex) yanoljamvp_project
1. models.py 작성
- 경로 : stay > models.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196# 숙소 정보 입력할 모델
class Stay(models.Model):
# 모텔, 호텔,리조트, 펜션/풀빌라, 게스트하우스 선택
category = models.ForeignKey(Category, on_delete=models.SET, null=True, blank=True, related_name="stays")
# 전경 사진(대표 사진) - 이미지 1개
mainImage = models.CharField(max_length=200, blank=True, null=True)
# 방 모든 사진 - 이미지 여러개
urlImage = models.TextField(blank=True, null=True)
# 숙소 이름(ex. 역삼 바레)
name = models.CharField(max_length=50)
# 유저 아이디(ex. positipman)
username = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, related_name="stays")
# 숙소 위치(ex. 서울특별시 강남구 봉은사로 428)
location = models.CharField(max_length=100)
# 숙소 소개
introduce = models.TextField(blank=True)
# 초특가호텔 --> 초특가할인 변경 (사장이 당일특가로 가격 40% 이상 할인 시, 초특가할인 true로 설정)
# ForeignKey로 연결되어 있는 아래의 Room 모델에서 초특가할인 여부 확인
# 편의시설 및 서비스 항목 (상기 SERVICE_CHOICES 참고)
serviceKinds = MultiSelectField(choices=SERVICE_CHOICES, null=True, blank=True)
# 편의시설 및 서비스 설명
serviceIntroduce = models.TextField(blank=True)
# 이용안내
serviceNotice = models.TextField(blank=True)
# 픽업안내
pickupNotice = models.TextField(blank=True)
# 숙소 리스트에 보여질 위치
directions = models.TextField(blank=True)
# 찾아오시는 길
route = models.TextField(blank=True, null=True)
# 찜하기
like = models.ManyToManyField(User, blank=True, related_name="like_stay")
# 검색 관련 키워드
keywords = TaggableManager()
# Stay 객체 생성일 자동 저장
created = models.DateTimeField(auto_now_add=True)
# Stay 객체 수정일 자동 저장
updated = models.DateTimeField(auto_now=True)
# 관리자페이지에서 저장될 객체의 순서 기준 설정
class Meta:
ordering = ['category']
# 관리자페이지에서 보여지는 객체 이름 설정
def __str__(self):
return f"{self.category} - {self.name}"
# room --> 멀티 이미지 구현 필요
class Room(models.Model):
# 숙소 선택(호텔, 모텔, 펜션 외)
stay = models.ForeignKey(Stay, on_delete=models.CASCADE, related_name="rooms")
# 룸 이름
name = models.CharField(max_length=50)
urlImage = models.TextField(blank=True, null=True)
# 누가 어느 방을 예약했는지 확인
reserved = models.ManyToManyField(User, blank=True, related_name="reserved")
booker_set = models.ManyToManyField(User, through='Reservation')
# 대실 이용시간(ex. 3) --> 0이면 숙박만 가능
hoursAvailable = models.IntegerField(default=0, blank=True, null=True)
# 대실 운영시간(ex. 23) --> ~23:00
hoursUntil = models.IntegerField(default=0, blank=True, null=True)
# 숙박 체크인 가능 시간(ex. 22(오후 10시))
daysCheckIn = models.IntegerField(default=0)
# 숙박 체크아웃 마감시간(ex. 11(오전 11시))
daysCheckOut = models.IntegerField(default=0)
# 기준 인원
standardPersonnel = models.IntegerField()
# 최대 인원
maximumPersonnel = models.IntegerField()
# 대실 예약가
hoursPrice = models.CharField(max_length=50, blank=True)
# 숙박 예약가
daysPrice = models.CharField(max_length=50)
# 데이터 있으면, 해당 할인가로 표시
# views.py에서 할인률 40% 이상이면, 초특가할인으로 지정하도록 구현할 것
# 대실 예약가 할인
saleHoursPrice = models.CharField(max_length=50, blank=True)
# 숙박 예약가 할인
saleDaysPrice = models.CharField(max_length=50, blank=True)
# 기본정보
basicInfo = models.TextField(blank=True)
# 예약공지
reservationNotice = models.TextField(blank=True)
# 취소규정
cancelRegulation = models.TextField(null=True)
def __str__(self):
return f"{self.stay} - {self.name}"
# 댓글 저장할 모델
class Comment(models.Model):
stay = models.ForeignKey(Stay, on_delete=models.CASCADE, related_name='comments')
room = models.ForeignKey(Room, on_delete=models.SET_NULL, blank=True, null=True)
reservation = models.OneToOneField('Reservation', on_delete=models.SET_NULL, blank=True, null=True)
# 로그인한 유저
username = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name="comments")
# 댓글 내용
text = models.TextField(default="")
# 댓글 작성 시, 자동으로 댓글 작성한 날짜 저장
created = models.DateTimeField(auto_now_add=True)
# 댓글 수정 시, 자동으로 댓글 수정한 날짜 저장
updated = models.DateTimeField(auto_now=True)
# 대댓글 기능 구현 위해 대댓글 작성할 특정 댓글 선택
parentComment = models.ForeignKey("self", on_delete=models.CASCADE, default="", blank=True, null=True)
# 평가항목 별 점수 선택
evaluationItems1 = models.IntegerField(default=5) # 친절도
evaluationItems2 = models.IntegerField(default=5) # 청결도
evaluationItems3 = models.IntegerField(default=5) # 편의성
evaluationItems4 = models.IntegerField(default=5) # 서비스 만족도
def __str__(self):
return f"{self.stay}-{self.username}님의 댓글"
# 유저가 예약할 때 필요한 정보 저장
class Reservation(models.Model):
# 예약할 룸을 가진 숙소
stay = models.ForeignKey(Stay, on_delete=models.SET_NULL, null=True, blank=True, related_name="reservations")
# 예약할 룸
room = models.ForeignKey(Room, on_delete=models.SET_NULL, null=True, blank=True, related_name="reservations")
# 최종 결제금액
finalPrice = models.CharField(max_length=50, default="")
# 대실일 경우, views.py에서 현재 시간이 만약 14시 30분이라면 15시부터 선택가능하도록
checkIn = models.DateTimeField(blank=True, null=True)
# 대실일 경우, views.py에서 해당 룸의 대실시간 고려하여 checkOut 시간 자동 저장
checkOut = models.DateTimeField(blank=True, null=True)
# 로그인한 유저아이디
username = models.ForeignKey(User, on_delete=models.CASCADE, related_name="reservations")
# 예약자 이름(views.py에서 default로 유저 이름 자동 설정) --> 변경 가능
booker = models.CharField(max_length=20)
# 예약자 폰 번호(views.py에서 default로 유저 폰번호 자동 설정) --> 변경 가능
phoneNumber = models.CharField(max_length=30)
wayToGo = models.CharField(max_length=10, blank=True, null=True)
# 대실, 숙박 선택(대실 혹은 숙박 예약하기 클릭 시, 해당 항목 True로 자동 변경)
checkHours = models.BooleanField(default=False)
checkDays = models.BooleanField(default=False)
# 예약한 날짜 자동 저장
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.username.username} has reserved the {self.room} in {self.stay}"
2. apis.py 작성
- 경로 : stay > apis.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33class StayListApi(generics.ListAPI):
serializer_class = StaySerializer
permission_classes = [AllowAny,]
def get_queryset(self):
stays = Stay.objects.all()
# 후기 많은 순 (review)
if self.request.query_params.get('review', None):
stays = stays.annotate(
review=Count('comments')
).order_by('-review')
# 찜 많은 순 (wish)
if self.request.query_params.get('wish', None):
stays = stays.annotate(
wish=Count('like')
).order_by('-wish')
# 예약가 낮은 순(priceLow)
if self.request.query_params.get('priceLow', None):
stays = stays.annotate(
# daysPrice가 varchar 타입이므로, Integer 타입으로 변경하고, 해당 숙소에서 제일 최저가의 방의 숙박 예약가를 'price' 변수가 참조하게 한다.
price=Min(Cast('rooms__daysPrice', IntegerField())),
).order_by('price')
# 예약가 높은 순(priceHigh)
if self.request.query_params.get('priceHigh', None):
stays = stays.annotate(
price=Min(Cast('rooms__daysPrice', IntegerField())),
).order_by('-price')
queryset = stays
return queryset
3. serializers.py
- 경로 : stay > serializers.py
1
2
3
4class StaySerializer(serializers.ModelSerializer):
class Meta:
model = Stay
fields = '__all__'
4. urls.py
- 경로 : stay > urls.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16from django.urls import path, include
from .views import *
from .apis import *
app_name = "stay"
urlpatterns_apis = [
...
path('stay/', StayListApi.as_view()),
...
]
urlpatterns = [
...
path('api/', include(urlpatterns_apis)),
]
Posted