Unknown part annotation(Django Part)

Error Coder·2023년 1월 15일
0

Part 1. Learned In Code

# bookings -> serializers.py File
if Booking.objects.filter(check_in__lte=data['check_in'], check_out__gte=data['check_out'],).exists():
# check in과 check out 사이의 예약을 확인할 순 있는데 check_in, out 이후에 존재하는 booking을 확인 못함
# Booking.objects.filter(check_in__gte=data['check_in'], check_out__lte=data['check_out'],).exists()
# bookings -> models.py
check_out = models.DateField(null=True, blank=True,) 
# null, blank를 True로 하는 것은 사용될 때도 있고 사용 안될때도 있기 때문
# Categories -> views.py
class CategoryViewSet(ModelViewSet):
    # Viewset은 두 가지를 알아야함
    # serializer가 뭔지 알아야 하고 Viewset의 object가 뭔지 알아야함, 사용하기에 좋지는 않음
    serializer_class = CategorySerializer
    queryset = Category.objects.all()
# common -> models.py
# 이 class는 djagno에서 model을 configure할 때 사용함
# abstract model -> django가 이 model을 봐도 db에 저장안함(데이터로 사용x)
    class Meta:
        abstract = True
# common -> permissions.py
message = "You need to be logged in for this!" # message는 permission이 user를 차단했을때 보임

    def has_permission(self, source: typing.Any, info: Info, **kwargs):
        return info.context.request.user.is_authenticated # user is authenticated가 True나 False를 반환해줌
# config -> authentications.py
def authenticate(self, request):
        username = request.headers.get('Trust-Me') # username이 TrustMe Header에 존재하면 user가 인증을 시도했다는 의미
        if not username:
            return None
        try:
            user = User.objects.get(username=username)
            return (user, None) # user = request.user
        except User.DoesNotExist:
            raise AuthenticationFailed(f"No user {username}") # user가 username을 headers에 보냈지만 잘못된 정보임
# config -> settings.py
path("api/v3/categories/", include("categories.urls")), # api를 나타내는 새로운 url 이름을 지어줌(버전까지 나타내주는 것도 중요)
# experiences -> models.py
host = models.ForeignKey("users.User", on_delete=models.CASCADE, related_name="experiences",)
# cascade는 카테고리가 삭제되면 이 experiences도 삭제된다는걸 말함
# SET_NULL은 categories의 category가 삭제되면 experiences의 카테고리를 null로 만듦
    category = models.ForeignKey("categories.Category", null=True, blank=True, on_delete=models.SET_NULL, related_name="experiences",)
# medias -> models.py
file = models.URLField() # 파일은 장고에서 손대지 않을 거기 때문에 url필드로 수정함
# medias -> views.py
def delete(self, request, pk):
    photo = self.get_object(pk)
    # 만약 사진이 방을 가지고 주인이 있고 주인이 요청한 유저와 다르다면 권한 거부
    # 사진이 경험을 가지고 있고 경험의 주인과 요청 보낸 유저가 다르다면 권한 거부
    if (photo.room and photo.room.owner != request.user) or (photo.experience and photo.experience.host != request.user):
        raise PermissionDenied
    photo.delete()
    return Response(status=HTTP_200_OK)

# 유저가 인증되야 하는 APIView에서 인증을 확인하지 않아도 가능하게 할 수 있음
# reviews -> admin.py
# 커스텀 필터를 만드려면 admin.SimpleListFilter를 상속받는 클래스를 만들어야함
# 2가지를 명시해야함
# parameter_name은 url에 표시됨
class WordFilter(admin.SimpleListFilter):

    title = "Filter by words!"

    parameter_name = 'word'

    # lookups와 queryset 2가지 메소드를 만들어야함
    # lookups은 튜플의 리스트를 리턴해야 하는 함수, 튜플의 첫번째 요소는 url에 나타남, 두번째 요소는 유저가 보고 클릭하게 되는 텍스트임
    # queryset은 필터링된 review를 리턴해야 하는 메소드
    # url에 있는 값을 가져오기 위해 self.value를 호출하기만 하면 됨
    def lookups(self, request, model_admin):
        return [("good", "Good"), ("great", "Great"), ("awesome","Awesome"),]

    def queryset(self, request, reviews):
        # url에 보이는 word를 줌
        word = self.value()
        if word:
            # payload에 word를 포함하는 review를 필터링함, word는 url안에 있던 것
            return reviews.filter(payload__contains=word)
        # 만약 url에 어떠한 단어가 없어도 모든 리뷰를 리턴하도록 해야 함 -? '모두'라는 필터를 선택했을 때 발생
        else:
            reviews
# reviews -> models.py
# Reviews는 한번의 experience를 가짐
# experience는 많은 reviews를 가질 수 있음
user = models.ForeignKey("users.User", on_delete=models.CASCADE, related_name="reviews",)
    room = models.ForeignKey("rooms.Room", null=True, blank=True, on_delete=models.SET_NULL, related_name="reviews",)
    experience = models.ForeignKey("experiences.Experience", null=True, blank=True, on_delete=models.CASCADE, related_name="reviews",)
    payload = models.TextField()
    rating = models.PositiveIntegerField()

    def __str__(self) -> str:
        return f"{self.user} / {self.rating}⭐"
# rooms -> admin.py
@admin.action(description="Set all prices to zero")

# action은 3개의 매개변수가 반드시 필요함
# model_admin은 액션을 호출한 클래스
# request 객체는 이 액션을 호출한 유저 정보를 갖고 있음
# rooms(queryset) 선택된 방에 대한 내용을 갖고 있음
def reset_prices(model_admin, request, rooms): 
    for room in rooms.all():
        room.price = 0
        room.save()
    

@admin.register(Room)
class RoomAdmin(admin.ModelAdmin):

    actions = (reset_prices,)

    # list_display에서 모델의 속성 뿐만 아니라 메소드도 적을 수 있음
    list_display = (
        "name",
        "price",
        "kind",
        "total_amenities",
        "rating",
        "owner",
        "created_at",
    )

    list_filter = (
        "country",
        "city",
        "pet_friendly",
        "kind",
        "amenities",
        "created_at",
    )

    # ^(startswoth) ->시작하는 단어로 검색하고 싶음
    # =(exact)는 완전히 동일한 것으로 검색하고 싶음
    # 아무것도 적지 않으면 contains
    search_fields = ("name", "^price", "=owner__username",)
# rooms -> models.py
created_at = models.DateTimeField(auto_now_add=True) # auto_now_add는 필드의 값을 해당 object가 처음 생성되었을 때 시간으로 설정함 -> room이 만들어지면 django는 이 방이 만들어진 date를 이 부분에 넣음
updated_at = models.DateTimeField(auto_now=True) # auto_now는 저장될 때마다 해당 필드를 현재 date로 설정함

def rating(room):
        count = room.reviews.count() 
# reviews -> related_name --> review 모델에서 room을 가리키는 foreign key에 부여함
# rooms -> serializer.py
class RoomDetailSerializer(ModelSerializer):

    # owner를 가져올 때 TinyUserSerializer를 사용하라고 알려줌, read_only -> 우리가 방을 생성할 때 serializer는 owner에 대한 정보를 요구하지 않음
    owner = TinyUserSerializer(read_only=True) 
    amenities = AmenitySerializer(read_only=True, many=True)
    category = CategorySerializer(read_only=True) # array가 아니고 숫자 하나면 many=True를 사용X
    rating = serializers.SerializerMethodField() # potato의 값을 계산할 method를 만들라고함
    is_owner = serializers.SerializerMethodField()
    is_liked = serializers.SerializerMethodField()
    photos = PhotoSerializer(many=True, read_only=True)

    class Meta:
        model = Room
        fields = "__all__"

    def get_rating(self, room): # 이름앞에 무조건 get_을 붙여야 함
        print(self.context)
        return room.rating()

    def get_is_owner(self, room):
        request = self.context['request']
        return room.owner == request.user

    def get_is_liked(self, room):
        request = self.context['request']
        return Wishlist.objects.filter(user=request.user, rooms__pk=room.pk).exists() # 유저가 여러 개의 wishlist를 가질 수 있기 때문에 filter 사용
# rooms -> views.py
def post(self, request):
        # 사용자의 데이터로 serializer를 만들 때는 serializer는 사용자의 데이터가 amenity object가 원하는 데이터에 준수하는지 검증해야 함
        serializer = serializers.AmenitySerializer(data=request.data)
        if serializer.is_valid():
            # True일 경우 serializer.save를 해서 ModelSerializer가 자동으로 amenity를 만들게 해야함
            amenity = serializer.save()
            # 새로 만들어진 것을 serialize한 다음 리턴해줌
            return Response(serializers.AmenitySerializer(amenity).data,)
        else:
            return Response(serializer.errors, status=HTTP_400_BAD_REQUEST,)

# partial은 부분 업데이트로 name 또는 description을 변경할 수 있음(둘 다X, 둘 중에 하나)
        serializer = serializers.AmenitySerializer(amenity, data=request.data, partial=True,)

def get(self, request, pk):
        try:
            page = request.query_params.get("page", 1) # 페이지가 url에 있지 않다면 기본값으로 페이지 1을 요청함
            page = int(page) # 페이지가 url에 있고 숫자로 되어 있다면 문자열을 실제 숫자 타입으로 바꿔야 함, 페이지가 그냥 문자라면 함수는 작동 안함
        except ValueError:
            page = 1 # url에 페이지가 없거나 유저가 잘못된 페이지를 보내는 상황이어도 페이지는 1이 됨
        page_size = 3
        start = (page - 1) * page_size
        end = start + page_size
        room = self.get_object(pk)
        serializer = ReviewSerializer(room.reviews.all()[start:end], many=True,) # 처음부터 모든 리뷰를 업로드 후 자르는 방식이 아닌, 시작과 끝 지점을 갖고 가서 db에서 살펴보는 방식
        return Response(serializer.data)

# Tip)
# import 할 때 django package에서 오는 것들을 우선시함
# 그 다음은 third party(이외의 것) package에서 import함 ex) rest_framework 등
# 다음은 같은 앱의 것들을 import 함, .로 시작하는 뜻은 같은 앱, 같은 폴더 안에 있다는 뜻
# 다음은 커스텀 앱에 관한 것들을 import함

 # bookings = Booking.objects.filter(room=room, kind=Booking.BookingKindChoices.ROOM, check_in__gt=now,) 
 # 우리 서버 위치의 현지시각을 localtime을 이용해 구하고 check_in 날짜가 우리가 있는 곳의 현재 날짜보다 큰 booking을 찾고 있음
# users -> models.py
# models.py를 수정할 때마다 migratiob을 만들고 migrate 해야함
# 코드에 있는 모델 구조와 db 구조를 서로 동기화 하기 위해서임

class User(AbstractUser):

    class GenderChoices(models.TextChoices):
        MALE = ("male", "Male")
        FEMALE = ("female", "Female")
    class LanguageChoices(models.TextChoices):
        KR = ("kr", "Korean")
        EN = ("en", "English") # db 값은 max_length 값보다 작아야함
    class CurrencyChoices(models.TextChoices):
        WON = "won", "Korean Won"
        USD = "usd", "Dollar"

    first_name = models.CharField(max_length=150, editable=False)
    last_name = models.CharField(max_length=150, editable=False)
    avatar = models.URLField(blank=True,) # blank=True는 form에서 필드가 필수적이지 않게 해줌
    name = models.CharField(max_length=150, default="")
    is_host = models.BooleanField(default=False) # 모든 사용자는 is_host 값을 False로 받게됨
    gender = models.CharField(max_length=10, choices=GenderChoices.choices,)
    language = models.CharField(max_length=2, choices=LanguageChoices.choices,)
    currency = models.CharField(max_length=5, choices=CurrencyChoices.choices,)
# users -> urls.py
path("token-login", obtain_auth_token), # username과 password를 보내면 token 반환
path("@<str:username>", views.PublicUser.as_view()), # 위 코드보다 먼저 쓰면 username을 me로 보기 때문에 에러 발생
# users -> views.py
permission_classes = [IsAuthenticated] # /me가 로그인한 user의 정보는 private 해야 하기 때문

class Users(APIView):

    def post(self, request):
        password = request.data.get('password')
        if not password:
            raise ParseError
        serializer = serializers.PrivateUserSerializer(data=request.data)
        if serializer.is_valid():
            user = serializer.save()
            user.set_password(password)
            user.save() # hash화된 비밀번호가 필요하기 때문
            # user.password = password(X) - 절대 X(중요!)
            serializer = serializers.PrivateUserSerializer(user)
            return Response(serializer.data)
        else:
            return Response(serializer.errors)
 ------------------------------------------------
if user.check_password(old_password):
				    user.set_password(new_password) # new_password를 hash할 때만 작동
            user.save()
# wishlists -> serializers.py
class Meta:
        model = Wishlist
        # 유저에게 위시리스트의 이름과 안에 있는 방을 보여줌(유저는 표시X) -> 위시리스트를 보고 있는 유저가 위시리스트의 소유자이기 때문
        fields = ("pk", "name", "rooms",)
# wishlists -> views.py
class Wishlists(APIView):

    permission_classes = [IsAuthenticated]

    def get(self, request):
        all_wishlists = Wishlist.objects.filter(user=request.user) # 동일한 유저가 갖고 있는 위시리스트만 찾기 위해 filter 사용
        serializer = WishlistSerializer(all_wishlists, many=True, context={"request" : request},)
        return Response(serializer.data)

    def post(self, request):
        serializer = WishlistSerializer(data=request.data)
        if serializer.is_valid():
            wishlist = serializer.save(user=request.user,) # 호출될 때 모델(위시리스트)을 받음
            serializer = WishlistSerializer(wishlist)
            return Response(serializer.data)
        else:
            return Response(serializer.errors)

# def put(self, request, pk, room_pk):
#         wishlist = self.get_list(pk ,request.user)
#         room = self.get_room(room_pk)
#         if wishlist.rooms.filter(pk=room_pk).exists(): 
#             wishlist.rooms.remove(room)
#         else:
#             wishlist.rooms.add(room)
#         return Response(status=HTTP_200_OK)

# ManyToManyField(room의 list)로 들어가서 wishlist 내부의 roomlist로 접근해서 filter, db에서 room의 pk랑 일치하는 pk를 갖는 room이 있는지 확인
# room pk는 user가 url에 적은 room_pk로부터 확인 가능
# wishlist.rooms이 user가 등록, 삭제하려는 pk랑 일치하는 방을 갖고 있는지 확인하는 것
# 그냥 조건에 맞는 list만 반환하는 filter() 대신 exists()를 추가해 존재하는지 확인할 수 있음
# list에 room이 있으면, list에서 그 room을 지울 거고 그렇지 않으면 추가함
from rest_framework.serializers import ModelSerializer
from rest_framework import serializers
from .models import Amenity, Room
from users.serializers import TinyUserSerializer
from reviews.serializers import ReviewSerializer
from categories.serializers import CategorySerializer
from medias.serializers import PhotoSerializer
from wishlists.models import Wishlist

class AmenitySerializer(ModelSerializer):
    class Meta:
        model = Amenity
        fields = ("name", "description",)

class RoomDetailSerializer(ModelSerializer):

    # owner를 가져올 때 TinyUserSerializer를 사용하라고 알려줌, read_only -> 우리가 방을 생성할 때 serializer는 owner에 대한 정보를 요구하지 않음
    owner = TinyUserSerializer(read_only=True) 
    amenities = AmenitySerializer(read_only=True, many=True)
    category = CategorySerializer(read_only=True) # array가 아니고 숫자 하나면 many=True를 사용X
    rating = serializers.SerializerMethodField() # potato의 값을 계산할 method를 만들라고함
    is_owner = serializers.SerializerMethodField()
    is_liked = serializers.SerializerMethodField()
    photos = PhotoSerializer(many=True, read_only=True)

    class Meta:
        model = Room
        fields = "__all__"

    def get_rating(self, room): # 이름앞에 무조건 get_을 붙여야 함
        print(self.context)
        return room.rating()

    def get_is_owner(self, room):
        request = self.context['request']
        return room.owner == request.user

    def get_is_liked(self, room):
        request = self.context['request']
        return Wishlist.objects.filter(user=request.user, rooms__pk=room.pk).exists() # 유저가 여러 개의 wishlist를 가질 수 있기 때문에 filter 사용

class RoomListSerializer(ModelSerializer):

    rating = serializers.SerializerMethodField()
    is_owner = serializers.SerializerMethodField()
    photos = PhotoSerializer(many=True, read_only=True)

    class Meta:
        model = Room
        fields = ("pk", "name", "country", "city", "price", "rating", "is_owner","photos",)

    def get_rating(self, room): 
        return room.rating()

    def get_is_owner(self, room):
        request = self.context['request']
        return room.owner == request.user
profile
개발자 지망생

0개의 댓글