1. Django Tutorial(Airbnb) - Custom Admin Site
40536 단어 django tutorialdjango tutorial
🌈 Custom Admin Site
🔥 Admin Site에 Model 등록 방법
🔥 Django의 Admin Panel 확장시키기
🔥 Admin Panel Option
🔥 Model의 매서드를 admin에서 활용하기
🔥 Timezone Utils 사용하기
🔥 Save Method 오버라이딩
1. Admin Site에 Model 등록 방법
- Admin Site에 Model을 나타내는 방법은 아래 2가지가 모두 가능해요. 두 방법 모두 Model을 import 하는건 동일합니다.
1) 데코레이터(@) 사용
- 데코레이터를 통해 Model을 등록하면 바로 아래 class를 자동으로 참조하기 때문에 어떤 admin class인지 명시할 필요가 없어요.
- 🔎 @admin.register(Model Class)
from django.contrib import admin from . import models @admin.register(models.User) class CustomUserAdmin(admin.ModelAdmin): pass
2) admin.site.register 사용
- admin.site.register를 이용하면 어떤 admin class를 등록하는지 모델과 함께 명시해야해요.
- 🔎 admin.site.register(Model Class, Admin Class)
from django.contrib import admin from . import models class CustomUserAdmin(admin.ModelAdmin): pass admin.site.register(models.User, CustomUserAdmin)
2. Django의 Admin Panel 확장시키기
- Django의 User 모델을 AbstractUser를 통해 확장시켰다면 Admin Panel 또한 기존 Django가 가지고 있던 admin을 상속 받아 사용할 수 있어요!
1) Django의 User Admin Panel 상속 받기
- Django의 Admin 가져오기
- 🔎 from django.contrib.auth.admin import UserAdmin
from django.contrib import admin from django.contrib.auth.admin import UserAdmin # 👈 UserAdmin이 위치한 곳 from . import models @admin.register(models.User) class CustomUserAdmin(UserAdmin): # 👈 UserAdmin 상속받음 """Custom User Admin""" pass
- Django의 Admin을 상속해오면, list_disply 및 list_filter가 자동으로 세팅되어 있어 나타나고, 검색이 가능한 Search bar도 기본 세팅되어 있어요. 물론 Custom도 가능합니다!
2) fieldsets 설정
- Admin Panel 내부로 들어가면, 확장시킨 필드(avatar, gender, bio, birthday 등)들이 모두 나타나지 않는데 fieldsets을 통해 Admin Panel 내부를 Custom 할 수 있어요!
- fieldsets 포맷 형태는 아래와 같습니다. 또한 기존 Django Admin이 갖고 있던 field까지 모두 표시하기 위해 UserAdmin.fieldsets과 우리가 생성한 fieldsets을 합쳐줄께요:)
from django.contrib import admin from django.contrib.auth.admin import UserAdmin from . import models # Register your models here. @admin.register(models.User) class CustomUserAdmin(UserAdmin): """Custom User Admin""" fieldsets = UserAdmin.fieldsets + ( ( "Custom Profile", # 👈 분류 title { "fields": ( "avatar", "gender", "bio", "birthdate", "language", "currency", "superhost", ) }, ), )
3. Admin Panel Option
1) list_diplsy 옵션
- list_diplsy에 튜플로 필드명을 입력하면 Admin Panel에 나타낼 필드값을 지정할 수 있어요.
- 🔎 list_display = ("필드명1", "필드명2", "필드명3", )
- 단, ManyToManyField는 list_display에 필드값으로 사용할 수 없기 때문에 Admin Panel에 나타내고자한다면 매서드를 생성하여 list_display에 넣어줍니다.
- 여기서 self와 obj를 전달하는데요,, self는 Admin Class인 RoomAdmin 가르키고, obj는 admin의 현재 row를 의미합니다.
- [매서드명].short_description로 Admin Panel에 출력될 필드 이름을 수정할 수 있어요!
@admin.register(models.Room) class RoomAdmin(admin.ModelAdmin): """Room Admin Dfinition""" list_display = ( "name", "country", "city", "price", "guests", "beds", "bedrooms", "baths", "check_out", "check_in", "instant_book", "count_amenities", ) def count_amenities(self, obj): return obj.amenities.count() # 👈 현재 row의 amenities를 갯수를 반환 count_amenities.short_description = "amenities" # 👈 count_amenities을 amenities로 변경
2) list_filter 옵션
- list_filter은 우측에 필터 기능을 추가시켜 줍니다.
- 🔎 list_filter = ("필드명1", "필드명2", "필드명3", )
@admin.register(models.Room) class RoomAdmin(admin.ModelAdmin): """Room Admin Dfinition""" list_filter = ( "instant_book", "host__superhost", "room_type", "amenities", "facilities", "house_rule", "city", "country", )
3) search_filter 옵션
- search_filter를 사용하면 search bar를 간편하게 만들 수 있어요.
- 🔎 search_filter = ("필드명1", "필드명2", "필드명3", )
- search_filter는 default로 icontains방식으로 데이터를 찾아주며, Prefix를 사용하여 검색어를 어떤 기준으로 찾을지 결정할 수 있어요.
- None(icontains) : 둔감한 기준(대소문자 구별 안함)으로 입력한 검색어가 포함된 object를 반환
- ^(startswith) : ^옵션을 같이 넣어주면, 요청한 검색어로 시작되는 object를 반환
- =(iexact) : 요청한 검색어와 정확히 일치하는 object를 반환
- 생성된 필드 중 ForeignKey가 있다면, 더블 언더스코어(
__
)를 통해 참조하는 대상의 모델로 접근이 가능합니다. 이런 문법 스타일은 list_disply, list_filter 등에서도 사용 가능해요!
- 🔎 search_fields = ("^host__username", )
@admin.register(models.Room) class RoomAdmin(admin.ModelAdmin): """Room Admin Dfinition""" list_display = ( "name", "country", "city", "price", ) search_fields = ( "city", "^host__username", "=price", )
4) filter_horizontal 옵션
- filter_horizontal 옵션은 ManyToManyField의 Admin Panel 내부에 적용되는 기능이에요.
- filter_horizontal 옵션을 사용하면, 필드의 스타일을 편리하게 변경해줘요.
@admin.register(models.Room) class RoomAdmin(admin.ModelAdmin): """Room Admin Dfinition""" filter_horizontal = ( "amenities", "facilities", "house_rule", )
👉 default
👉 filter_horizontal
5) ordering 옵션
- ordering 옵션에 필드값을 지정하면, Admin Panel에 정렬 버튼(삼각형 버튼)을 생성해줍니다.
@admin.register(models.Room) class RoomAdmin(admin.ModelAdmin): """Room Admin Dfinition""" ordering = ( "name", "price", )
6) raw_id_fields 옵션
- Admin Panel 내부로 들어가볼 까요? 사용자가 늘어난다면, 필드값이 많아져 스크롤의 압박으로 불편을 유발합니다:)
- 이럴 경우, raw_id_fields로 해당 필드를 지정해주면 팝업창을 만들어줘요. 원하는 값을 찾기 쉬워집니다!
@admin.register(models.Room) class RoomAdmin(admin.ModelAdmin): """Room Admin Dfinition""" raw_id_fields = ("host",)
4. Model의 매서드를 admin에서 활용하기
- Admin Panel에는 Model에서 선언한 필드값만 나타낼 수 있는 것은 아니에요. 필요에 의해서 admin.py 또는 models.py에 매서드를 만들고 이를 자유롭게 list_display에 추가할 수 있답니다. 각 Review에 대한 "평점 정보"를 Model에서 매서드 만들고 list_display에 추가해 볼께요:)
- "평점 정보"를 Admin Panel만이 아닌 일반 사용자도 접근 가능한 Templates 영역에서도 사용할 예정이라면 Model 영역(models.py)에서 매서드를 생성해주는 것이 좋아요. Model에서 생성된 매서드는 Admin Panel 뿐만아니라 Templates 영역에서도 사용 가능하기 때문이죠!
- 만약 Admin Panel에서만 해당 매서드를 사용할 계획이라면 admin.py에 매서드를 생성해주면 됩니다.
1) reviews/models.py
- 각 Review Object의 평점의 평균을 구해서 반환하는 매서드 작성했어요.
- 각 객실의 점수 : accuracy, communication, cleanliness, location, check_in, value
- 해당 매서드명을 list_display에 추가만하면 Admin Panel에서 각 Reivew에 대한 평정 정보를 볼 수 있답니다.
- short_description은 admin.py 및 models.py 모두 사용 가능합니다.
- 🔎 rating_average.short_description = "Avg."
from django.db import models from core import models as core_models # Create your models here. class Review(core_models.TimeStampedModel): """Review Model Definition""" review = models.TextField() accuracy = models.IntegerField() communication = models.IntegerField() cleanliness = models.IntegerField() location = models.IntegerField() check_in = models.IntegerField() value = models.IntegerField() user = models.ForeignKey( "users.User", related_name="reviews", on_delete=models.CASCADE ) room = models.ForeignKey( "rooms.Room", related_name="reviews", on_delete=models.CASCADE ) def __str__(self): return f"{self.review} - {self.room}" def rating_average(self): avg = ( self.accuracy + self.communication + self.cleanliness + self.location + self.check_in + self.value ) / 6 return round(avg, 2) # 👈 소수점 2째자리까지 나타낼거에요:) rating_average.short_description = "Avg."
2) reviews.admin.py
__str__
매직매서가 list_display에 덮어씌어져 Admin Panel에 보이지 않을 때는 당황하지 말고,, "list_display"에__str__
를 추가해주면 되요!from django.contrib import admin from . import models # Register your models here. @admin.register(models.Review) class ReviewAdmin(admin.ModelAdmin): """Review Admin Definition""" list_display = ( "__str__", "rating_average", )
- 각 객실이 여러 사람에 의해 이미 이용했었을 수 있고, 그렇다면 평점도 여러개가 존재할꺼에요. 이번에는 각 객실에 대한 여러 평점 정보의 평균을 Admin Panel과 Front 영역에서 나타내고자 해요.
- Review 모델은 ForeignKey로 Room을 참조하고 있기 때문에 Room에서는 related_name의 값을 통해서 각 객실을 가르키는 모든 Review들의 정보에 역으로 접근할 수 있어요:)
3) rooms/models.py
- 각 객실을 참조하는 모든 Review 정보를 all_reviews에 담았어요. "self.reviews.all()"에서 reviews는 어디서 왓을까요? room필드(reviews/models.py)에 related_name 값입니다.
- 🔎 all_reviews = self.reviews.all()
- 이제 all_reviews에 각 객실을 가르키는 모든 Review를 담아두었어요. rating_average() 매서드를 통해 평점 값을 모두 합한 뒤, all_reviews의 길이만큼 나눠 반환하면 각 객실에 대한 평점 정보 제공해줘요.
- 🔎 all_rating += review.rating_average()
class Room(core_models.TimeStampedModel): """Room Model Definifion""" def total_rating(self): all_reviews = self.reviews.all() all_rating = 0 if len(all_reviews) != 0: for review in all_reviews: all_rating += review.rating_average() # 👈 매서드도 접근 가능 return all_rating / len(all_reviews) else: # 👈 Review가 1개도 작성되지 않은 객실이 존재할 경우를 대비한 방어코드에요:) return 0 total_rating.short_description = "rating"
5. Timezone Utils 사용하기
1) Timezone Utils
- Python의 Time 라이브러리를 사용하지 않고, TimeZone을 사용하는 이유는 settgins.py에서 설정된 TimeZone이 Django의 서버의 시간을 관장하고 있기 때문이에요.
- 🔎 from django.utils import timezone
- 반환값을 Boolean 아이콘으로 표시하는 방법
- 🔎 in_progress.boolean = True
from django.db import models from django.utils import timezone # 👈 timezone util을 가져오기 from core import models as core_models # Create your models here. class Reservation(core_models.TimeStampedModel): """Reservation Model Definition""" STATUS_PENDING = "pending" STATUS_CONFIRMED = "confirmed" STATUS_CANCELED = "canceled" STATUS_CHOICES = ( (STATUS_PENDING, "Pending"), (STATUS_CONFIRMED, "Confirmed"), (STATUS_CANCELED, "Canceled"), ) status = models.CharField( max_length=12, choices=STATUS_CHOICES, default=STATUS_PENDING ) check_in = models.DateField() check_out = models.DateField() guest = models.ForeignKey( "users.User", related_name="reservations", on_delete=models.CASCADE ) room = models.ForeignKey( "rooms.Room", related_name="reservations", on_delete=models.CASCADE ) def __str__(self): return f"{self.room} - {self.check_in}" def in_progress(self): now = timezone.now().date() # 👈 현재 서버 날짜 return now >= self.check_in and now <= self.check_out in_progress.boolean = True # 👈 return값을 True, False가 아닌 아이콘으로 나타내줘요. def is_finished(self): now = timezone.now().date() # 👈 현재 서버 날짜 return now > self.check_out is_finished.boolean = True # 👈 return값을 True, False가 아닌 아이콘으로 나타내줘요.
6. Save Method 오버라이딩
- Save Method를 가로채야하는 상황이 발생하는데요,, 그 이유에 대해서 알아볼께요.
- "users/models.py"의 city 필드는 host가 입력한 값이 저장되기 때문에 같은 장소임에 불구하고 대소문자 등의 차이가 발생할 수 있어요. 어떤 host는 Seoul로 입력하고, 또 다른 host는 seoul로 입력할 수 있겟죠.
- country처럼 정해진 선택 목록을 제공할 수도 있으나, city는 양이 방대하고 모든 city를 제공하면 실제 사용되지 않는 불필요한 값이 존재할거에요.
- 이에 Model 값의 등록은 사용자에게 맡기지만, 동일한 값인 경우 최대한 값들을 통일하여 저장될 수 있게할 필요가 있어요. 이럴 때, save() 매서드가 실행되서 값을 저장하기 전에 Save Envent를 가로채서 해당 값을 수정한 뒤, 저장시킬 수 있어요!
1) rooms.models.py
- form에 입력한 정보가 DB에 해당 필드로 저장되는 것이 save() 매서드의 역할이에요. 즉, Save Event를 가로챈다는 것은 기존에 save() 매서드를 오버라이딩하여 값을 다룬 뒤, super()를 통해 save()의 본래 기능을 실행시키는 것 입니다. 저장되기 전에 잠시 값을 가로채서 조치를 취하고 저장시키는 거죠!
from django.db import models from django_countries.fields import CountryField from core import models as core_models # Create your models here. class Room(core_models.TimeStampedModel): """Room Model Definifion""" ... ... city = models.CharField(max_length=80) ... ... def save(self, *args, **kwargs): self.city = str.capitalize(self.city) # 👈 앞글자를 대분자로 바꾸고 super().save(*args, **kwargs) # 👈 원래 save 매서드를 실행
Author And Source
이 문제에 관하여(1. Django Tutorial(Airbnb) - Custom Admin Site), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@jewon119/Django-기초-Custom-Admin-Site저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)