일기 대신 코드 슬쩍

[백엔드][장고][Django] Chapter #10 본문

Backend/Django

[백엔드][장고][Django] Chapter #10

코코자 2023. 7. 18. 15:29

#10.0 Introduction

  • django REST 프레임워크 이용할 것임
  • poetry shell해서 가상환경 진입하고, poetry add djangorestframework로 설치
  • settings에서 System_apps은 장고가 설치되면서 같이 있는 앱이고, custom_apps은 우리가 만든 우리의 어플리케이션임
THIRD_PARTY_APPS = [
    "rest_framework",
]

CUSTOM_APPS = [
    "common.apps.CommonConfig",
    "users.apps.UsersConfig",
    "rooms.apps.RoomsConfig",
    "experiences.apps.ExperiencesConfig",
    "categories.apps.CategoriesConfig",
    "reviews.apps.ReviewsConfig",
    "wishlists.apps.WishlistsConfig",
    "bookings.apps.BookingsConfig",
    "medias.apps.MediasConfig",
    "direct_messages.apps.DirectMessagesConfig"

]
SYSTEM_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles'
]

INSTALLED_APPS = SYSTEM_APPS + THIRD_PARTY_APPS + CUSTOM_APPS
  • 이렇게하면 REST 프레임워크 설치 끝
  • JSON: 하나의 데이터 형식, 코드임

#10.1 JsonResponse

  • categories에 대한 API를 만들어볼 것임
  • config urls로 가서 경로 추가
path("categories/", include("categories.urls"))
  • categories의 urls.py에
from django.urls import path

urlpatterns = [
    path("",)

]

이렇게 해놓고

  • categories의 view로 가서
from django.shortcuts import render

def categories(request):
    pass
  • 함수에 대한 기능은 차차 작성할 것임
  • urls.py이렇게 수정
from django.urls import path
from . import views

urlpatterns = [
    path("",views.categories)

]

  • 먼저 모든 카테고리를 가져올 것임
  • views.py
from .models import Category
from django.http import JsonResponse

def categories(request):
    all_categories = Category.objects.all()
    return JsonResponse({'ok':True})
  • 서버를 열고 categories/로 가면.. 오류가 발생하네?
from django.http import JsonResponse
from .models import Category

def categories(request):
    all_categories = Category.objects.all()
    return JsonResponse({'ok':True,
                          'categories':all_categories,
                         }
                         )

#10.2 api_view

  • django serialization 프레임워크는 장고 모델을 다른 포맷을 변환해주는 방법을 제공함
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Category


@api_view()
def categories(request):
    return Response(
        {
            "ok": True,
            "categories": Category.objects.all(),
        }
    )
  • 다음 시간에 이런 에러를 해결해볼 것임~

#10.3 Serializer

  • category에 대한 serializer를 만들 것임
from rest_framework import serializers


class CategorySerializer(serializers.Serializer):

    pk = serializers.IntegerField()
    name = serializers.CharField(required=True)
    kind = serializers.CharField()
    created_at = serializers.DateTimeField()

  • views도 수정
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Category
from .serializers import CategorySerializer


@api_view()
def categories(request):
    all_categories = Category.objects.all()
    serializer = CategorySerializer(all_categories, many=True)
    return Response(
        {
            "ok": True,
            "categories": serializer.data,
        }
    )

#10.4 POST Requests

  • serializer는 user에게서 온 data를 받아서 우리의 데이터베이스에 넣을 수 있는 django 객체로 바꿔줌
  • urls.py
from django.urls import path
from . import views

urlpatterns = [
    path("",views.categories),
    path("<int:pk>", views.category),

]

  • views.py
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Category
from .serializers import CategorySerializer


@api_view(["GET", "POST"])
def categories(request):
    if request.method == "GET":
        all_categories = Category.objects.all()
        serializer = CategorySerializer(all_categories, many=True)
        return Response(serializer.data)
    elif request.method == "POST":
        Category.objects.create(
            name=request.data["name"],
            kind=request.data["kind"],
        )
        return Response({"created": True})


@api_view()
def category(request, pk):
    category = Category.objects.get(pk=pk)
    serializer = CategorySerializer(category)
    return Response(serializer.data)

#10.5 is_valid()

  • serializer에게 데이터의 형태를 좀 더 자세히 설명해줌으로써 serializer가 장고 모델에서 JSON으로 번역할 수 있게 함
  • serializer가 검증도 해줌
  • serializer.py
from rest_framework import serializers


class CategorySerializer(serializers.Serializer):

        pk = serializers.IntegerField(read_only=True)
        name = serializers.CharField(required=True,
                                    max_length=50,
                                    )
        kind = serializers.CharField(max_length=15,
                                    )
        created_at = serializers.DateTimeField(read_only=True)

  • views.py
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Category
from .serializers import CategorySerializer


@api_view(["GET", "POST"])
def categories(request):
    if request.method == "GET":
        all_categories = Category.objects.all()
        serializer = CategorySerializer(all_categories, many=True)
        return Response(serializer.data)
    elif request.method == "POST":
        serializer = CategorySerializer(data=request.data)
        if serializer.is_valid():
            return Response({"created": True})
        else:
            return Response(serializer.errors)


@api_view()
def category(request, pk):
    category = Category.objects.get(pk=pk)
    serializer = CategorySerializer(category)
    return Response(serializer.data)

#10.6 save()

  • 우리는 serializer를 이용해 django 세계의 데이터를 가져와서 JSON으로 번역하는 방법을 배웠고,
  • user의 데이터를 받아서 데이터베이스에 저장할 수 있는 category로 번역하는 방법도 배움
  • serializer는 save 메소드를 가지고 있음
  • **는 딕셔너리를 가져옴
  • 이런 느낌
  • serializer.py
from rest_framework import serializers
from .models import Category

class CategorySerializer(serializers.Serializer):

        pk = serializers.IntegerField(read_only=True)
        name = serializers.CharField(required=True,
                                    max_length=50,
                                    )
        kind = serializers.ChoiceField(choices=Category.CategoryKindChoices.choices)

        created_at = serializers.DateTimeField(read_only=True)

        def create(self, validated_data):
            return Category.objects.create(**validated_data)

  • views.py
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Category
from .serializers import CategorySerializer


@api_view(["GET", "POST"])
def categories(request):
    if request.method == "GET":
        all_categories = Category.objects.all()
        serializer = CategorySerializer(all_categories, many=True)
        return Response(serializer.data)
    elif request.method == "POST":
        serializer = CategorySerializer(data=request.data)
        if serializer.is_valid():
            new_category = serializer.save()
            return Response(
                CategorySerializer(new_category).data,
            )
        else:
            return Response(serializer.errors)


@api_view()
def category(request, pk):
    category = Category.objects.get(pk=pk)
    serializer = CategorySerializer(category)
    return Response(serializer.data)

#10.7 update()

  • 장고 REST 프레임워크에 GET와 PUT 요청을 허용
  • PUT 요청은 POST요청이랑 비슷함
  • partial=True : serializer는 여기로 들어오는 데이터가 완벽한 형태가 아닐 수도 있다는 것을 알게 됨
  • serializer.py
from rest_framework import serializers
from .models import Category

class CategorySerializer(serializers.Serializer):

        pk = serializers.IntegerField(read_only=True)
        name = serializers.CharField(required=True,
                                    max_length=50,
                                    )
        kind = serializers.ChoiceField(choices=Category.CategoryKindChoices.choices)

        created_at = serializers.DateTimeField(read_only=True)

        def create(self, validated_data):
            return Category.objects.create(**validated_data)
        
        def update(self, instance, validated_data):
            instance.name = validated_data.get("name", instance.name)
            instance.kind = validated_data.get("kind", instance.kind)
            instance.save()
            return instance

  • views.py
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Category
from .serializers import CategorySerializer
from rest_framework.exceptions import NotFound


@api_view(["GET", "POST"])
def categories(request):
    if request.method == "GET":
        all_categories = Category.objects.all()
        serializer = CategorySerializer(all_categories, many=True)
        return Response(serializer.data)
    elif request.method == "POST":
        serializer = CategorySerializer(data=request.data)
        if serializer.is_valid():
            new_category = serializer.save()
            return Response(
                CategorySerializer(new_category).data,
            )
        else:
            return Response(serializer.errors)


@api_view(["GET", "PUT"])
def category(request, pk):
        try:
            category = Category.objects.get(pk=pk)
        except Category.DoesNotExist:
            raise NotFound

        if request.method == "GET":
            serializer = CategorySerializer(category)
            return Response(serializer.data)
        elif request.method == "PUT":
            serializer = CategorySerializer(
                            category,
                            data=request.data,
                            partial=True,
                            )
            if serializer.is_valid():
                updated_category = serializer.save()
                return Response(CategorySerializer(updated_category).data)
            else:
                return Response(serializer.errors)

#10.8 DELETE

  • views.py
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Category
from .serializers import CategorySerializer
from rest_framework.exceptions import NotFound
from rest_framework.status import HTTP_204_NO_CONTENT

@api_view(["GET", "POST"])
def categories(request):
    if request.method == "GET":
        all_categories = Category.objects.all()
        serializer = CategorySerializer(all_categories, many=True)
        return Response(serializer.data)
    elif request.method == "POST":
        serializer = CategorySerializer(data=request.data)
        if serializer.is_valid():
            new_category = serializer.save()
            return Response(
                CategorySerializer(new_category).data,
            )
        else:
            return Response(serializer.errors)


@api_view(["GET", "PUT", "DELETE"])
def category(request, pk):
        try:
            category = Category.objects.get(pk=pk)
        except Category.DoesNotExist:
            raise NotFound

        if request.method == "GET":
            serializer = CategorySerializer(category)
            return Response(serializer.data)
        elif request.method == "PUT":
            serializer = CategorySerializer(
                            category,
                            data=request.data,
                            partial=True,
                            )
            if serializer.is_valid():
                updated_category = serializer.save()
                return Response(CategorySerializer(updated_category).data)
            else:
                return Response(serializer.errors)
        elif request.method == "DELETE":
            category.delete()
            return Response(status=HTTP_204_NO_CONTENT)

  • config-urls.py
"""
URL configuration for config project.

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/4.2/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
    path('admin/', admin.site.urls),
    path("api/v1/rooms/", include("rooms.urls")),
    path("api/v1/categories/", include("categories.urls"))
]

#10.9 Recap

  • serializer.save는 create메소드를 실행시킴
  • 사용자가 준 검증된 데이터와 함꼐 create 호출 된다.

#10.10 APIView

  • views.py
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Category
from .serializers import CategorySerializer
from rest_framework.exceptions import NotFound
from rest_framework.status import HTTP_204_NO_CONTENT
from rest_framework.views import APIView

class Categories(APIView):
    def get(self, request):
        all_categories = Category.objects.all()
        serializer = CategorySerializer(all_categories, many=True)
        return Response(serializer.data)
    
    def post(self, request):    
        serializer = CategorySerializer(data=request.data)
        if serializer.is_valid():
            new_category = serializer.save()
            return Response(
                CategorySerializer(new_category).data,
            )
        else:
            return Response(serializer.errors)


class CategoryDetail(APIView):
    def get_object(self, pk):
        try:
            return Category.objects.get(pk=pk)
        except Category.DoesNotExist:
            raise NotFound        
    
    def get(self, request, pk):
        serializer = CategorySerializer(self.get_object(pk))
        return Response(serializer.data)
    
    def put(self, request, pk):
        serializer = CategorySerializer(
            self.get_object(pk),
            data=request.data,
            partial=True,
        )
        if serializer.is_valid():
            updated_category = serializer.save()
            return Response(CategorySerializer(updated_category).data)
        else:
            return Response(serializer.errors)

    def delete(self, request, pk):
        self.get_object(pk).delete()
        return Response(status=HTTP_204_NO_CONTENT)

  • categories-urls.py
from django.urls import path
from . import views

urlpatterns = [
    path("", views.Categories.as_view()),
    path("<int:pk>", views.CategoryDetail.as_view()),

]

#10.11 ModelSerializer

  • 이렇게나 간단해질 수 있는 거구나…
  • serializer.py
from rest_framework import serializers
from .models import Category

class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = "__all__"

  • views.py

from rest_framework.decorators import api_view
from rest_framework.exceptions import NotFound
from rest_framework.response import Response
from rest_framework.status import HTTP_204_NO_CONTENT
from rest_framework.views import APIView
from .models import Category
from .serializers import CategorySerializer
class Categories(APIView):
    def get(self, request):
        all_categories = Category.objects.all()
        serializer = CategorySerializer(
            all_categories,
            many=True,
        )
        return Response(serializer.data)

    def post(self, request):
        serializer = CategorySerializer(data=request.data)
        if serializer.is_valid():
            new_category = serializer.save()
            return Response(
                CategorySerializer(new_category).data,
            )
        else:
            return Response(serializer.errors)
class CategoryDetail(APIView):
    def get_object(self, pk):
        try:
            return Category.objects.get(pk=pk)
        except Category.DoesNotExist:
            raise NotFound
    def get(self, request, pk):
        serializer = CategorySerializer(self.get_object(pk))
        return Response(serializer.data)
    def put(self, request, pk):
        serializer = CategorySerializer(
            self.get_object(pk),
            data=request.data,
            partial=True,
        )
        if serializer.is_valid():
            updated_category = serializer.save()
            return Response(
                CategorySerializer(updated_category).data,
            )
        else:
            return Response(serializer.errors)

    def delete(self, request, pk):
        self.get_object(pk).delete()
        return Response(status=HTTP_204_NO_CONTENT)

#10.12 ModelViewSet

  • viewset? 말도 안 되게 좋은 기능
  • views에 작성한 거 다 지워
  • modelviewset을 import해
  • 코드는 더 간단하지만 우리가 서버에서 직접 html코드로 안 써도 되게 html 폼까지 만들어줌
  • urls.py
from django.urls import path
from . import views

urlpatterns = [
    path(
        "",
        views.CategoryViewSet.as_view(
            {
                "get": "list",
                "post": "create",
            }
        ),
    ),
    path(
        "<int:pk>",
        views.CategoryViewSet.as_view(
            {
                "get": "retrieve",
                "put": "partial_update",
                "delete": "destroy",
            }
        ),
    ),
]

  • views.py
from rest_framework.decorators import api_view
from rest_framework.exceptions import NotFound
from rest_framework.response import Response
from rest_framework.status import HTTP_204_NO_CONTENT
from rest_framework.views import APIView
from .models import Category
from .serializers import CategorySerializer
from rest_framework.viewsets import ModelViewSet

class CategoryViewSet(ModelViewSet):




    serializer_class = CategorySerializer

    queryset = Category.objects.all()

#10.13 Conclusions

  • 우리는 urls.py를 통해 viewset으로 http 메소드랑 viewset메소드랑 연결해야 했음
  • 근데 라우터를 이용해서 한 번에 처리할 수 있음
  • 근데 너무 다 해줘서 개발자가 원하는 기능을 세세하게 구현하려면 직접하는게 나을거다.