API_Server IN Django +5

LEE EUI JOO·2023년 2월 6일
0

Web Programming

목록 보기
7/17
post-custom-banner

ToDo API Server

  • ❗️API_Server IN Django +3 참고❗️

  • 프로젝트 생성

  • 필요한 의존성 라이브러리(패키지) 설치

  • Django 프로젝트 생성

  • Django 애플리케이션 생성

  • settings.py를 수정해서 기본 설정

  • 확인

  • DB 접속도 확인


models.py

from django.db import models

# Create your models here.
class Todo(models.Model):
    title = models.CharField(max_length=100)
    description = models.TextField(blank=True)
    created = models.DateTimeField(auto_now_add=True)
    complete = models.BooleanField(default=False)
    important = models.BooleanField(default=False)
    # 디버깅을 위해서 사용 - print로 인스턴스를 출력하면 호출되는 메서드

    def __str__(self):
        return self.title

마이그레이션 진행

(venv)  euijoolee@EUIJOOui-MacBookAir  ~/PycharmProjects/todoapi/mytodo  python manage.py makemigrations    
Migrations for 'mytodoapp':
  mytodoapp/migrations/0001_initial.py
    - Create model Todo
(venv)  euijoolee@EUIJOOui-MacBookAir  ~/PycharmProjects/todoapi/mytodo  python manage.py migrate           
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, mytodoapp, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying mytodoapp.0001_initial... OK
  Applying sessions.0001_initial... OK


create superuser

(venv)  euijoolee@EUIJOOui-MacBookAir  ~/PycharmProjects/todoapi/mytodo  python manage.py createsuperuser 
Username (leave blank to use 'euijoolee'): euijoo
Email address: euijoo@naver.com
Password: 
Password (again): 
The password is too similar to the username.
Bypass password validation and create user anyway? [y/N]: y
  • admin.py 수정
from django.contrib import admin
from .models import Todo

admin.site.register(Todo)

# Register your models here.

애플리케이션을 실행하고 /admin으로 접속 확인


전체 조회

  • 전제 조회해서 확인할 항목 : id, title, complete, important

  • 전체 조회를 위한 시리얼라이저 생성 - serializers.py

from rest_framework import serializers
from .models import Todo

class TodoSimpleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Todo
        fields = ('id', 'title', 'complete','important')
  • 전체 조회를 위한 view 클래스 생성 - views.py
from rest_framework import status

from rest_framework.response import Response
from rest_framework.views import APIView

from .models import Todo

from .serializers import TodoSimpleSerializer

class TodoAPIView(APIView):
    def get(self, request):
        todos = Todo.objects.filter(complete=False)
        serializer = TodoSimpleSerializer(todos,many=True)
        return Response(serializer.data,status=status.HTTP_200_OK)
  • 전체 조회를 위한 url 연결 - mytodo/urls.py
"""
from django.contrib import admin
from django.urls import path

from django.contrib import admin
from django.urls import path

from mytodoapp.views import TodosAPIView
urlpatterns = [
    path('admin/', admin.site.urls),
    path('todo', TodosAPIView.as_view()),
    
]


데이터 삽입

  • 삽입에서 입력 항목 : title, description, complete, import
    • id는 선택할 수 없음(auto_increment라서)
  • 삽입을 위한 Serializer를 생성 - serializers.py
from rest_framework import serializers
from .models import Todo

class TodoSimpleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Todo
        fields = ('id', 'title', 'complete','important')

class TodoCreateSerializer(serializers.ModelSerializer):
    class Meta:
        model = Todo
        fields = ('title', 'description', 'complete','important')
  • views.py 에 삽입을 처리하도록 작성
from rest_framework import status

from rest_framework.response import Response
from rest_framework.views import APIView

from .models import Todo

from .serializers import TodoSimpleSerializer, TodoCreateSerializer

class TodoAPIView(APIView):
    def get(self, request):
        todos = Todo.objects.filter(complete=False)
        serializer = TodoSimpleSerializer(todos,many=True)
        return Response(serializer.data,status=status.HTTP_200_OK)
        # url 이 필요없음
    def post(self,request):
        # 파라미터를 가져와서 serializer를 생성
        serializer = TodoCreateSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status.HTTP_201_CREATED)
        return Response(serializer.errors, status.HTTP_400_BAD_REQUEST)


상세 보기 🔎

  • 기본키를 넘겨받아서 기본키에 해당하는 데이터를 찾아서 리턴

    • id, title, description, created, complete, important
  • serializers.py 수정

from rest_framework import serializers
from .models import Todo

class TodoSimpleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Todo
        fields = ('id', 'title', 'complete','important')

class TodoCreateSerializer(serializers.ModelSerializer):
    class Meta:
        model = Todo
        fields = ('title', 'description', 'complete','important')

class TodoDetailSerializer(serializers.ModelSerializer):
    class Meta:
        model = Todo
        fields = ('id', 'title', 'description', 'created', 'complete', 'important')

❗️ 목록(list)를 리턴하는 경우 : None(null)을 리턴해서는 안됨❗️

  • 목록을 리턴하는 함수를 호출하는 경우 일반적으로 반복문으로 처리하는데 반복문에 null을 대입하면 예외가 발생하므로 목록을 리턴할 때는 데이터가 없으면 빈 목록([])를 리턴해줘야 한다

  • 데이터의 존재 여부는 데이터 개수가 0인지로 확인

  • 하나의 데이터를 리턴하는 경우 : 데이터가 존재하지 않으면 Null(null)을 리턴

  • views.py

from rest_framework import status

from rest_framework.response import Response
from rest_framework.views import APIView

from .models import Todo

from .serializers import TodoSimpleSerializer, TodoCreateSerializer

class TodosAPIView(APIView):
    def get(self, request):
        todos = Todo.objects.filter(complete=False)
        serializer = TodoSimpleSerializer(todos, many=True)
        return Response(serializer.data,status=status.HTTP_200_OK)
    def post(self,request):
        # 파라미터를 가져와서 serializer를 생성
        serializer = TodoCreateSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status.HTTP_201_CREATED)
        return Response(serializer.errors, status.HTTP_400_BAD_REQUEST)

from .serializers import TodoDetailSerializer
from rest_framework.generics import get_object_or_404
class TodoAPIView(APIView):
    def get(self,request,pk):
        # id 가 pk 인 데이터를 조회하고 없으면 404 에러 발생
        todo = get_object_or_404(Todo, id=pk)
        serializer = TodoDetailSerializer(todo)
        return Response(serializer.data,status=status.HTTP_200_OK)

상세보기를 위한 URL 연결 – mytodo/urls.py

  • 실행하고 브라우저에서 확인 - localhost:8000/todo/id번호
  • 없는 번호를 사용하면 404 에러이고 존재하는 번호일 때는 데이터를 출력
"""
from django.contrib import admin
from django.urls import path

from django.contrib import admin
from django.urls import path

from mytodoapp.views import TodosAPIView,TodoAPIView
urlpatterns = [
    path('admin/', admin.site.urls),
    path('todo', TodosAPIView.as_view()),
    path('todo/<int:pk>',TodoAPIView.as_view()),
]


데이터 수정

  • 데이터가 존재하는지 확인하고 존재하면 수정

  • ORM 에서는 데이터 삽입 과 수정이 동일한 방식으로 발생

    • 기본키의 값이 존재하면 수정이고 존재하지 않거나 없으면 삽입 - Upsert

views.py 에 수정을 위한 코드를 추가

from rest_framework import status

from rest_framework.response import Response
from rest_framework.views import APIView

from .models import Todo

from .serializers import TodoSimpleSerializer, TodoCreateSerializer

class TodosAPIView(APIView):
    def get(self, request):
        todos = Todo.objects.filter(complete=False)
        serializer = TodoSimpleSerializer(todos, many=True)
        return Response(serializer.data,status=status.HTTP_200_OK)
    def post(self,request):
        # 파라미터를 가져와서 serializer를 생성
        serializer = TodoCreateSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status.HTTP_201_CREATED)
        return Response(serializer.errors, status.HTTP_400_BAD_REQUEST)
        
        

from .serializers import TodoDetailSerializer
from rest_framework.generics import get_object_or_404
class TodoAPIView(APIView):
    def get(self, request, pk):
        # id 가 pk 인 데이터를 조회하고 없으면 404 에러 발생
        todo = get_object_or_404(Todo, id=pk)
        serializer = TodoDetailSerializer(todo)
        return Response(serializer.data,status=status.HTTP_200_OK)
        
    def put(self, request, pk):
        # id가 pk 인 데이터를 조회하고 없으면 404 에러 발생
        todo = get_object_or_404(Todo, id=pk)
        serializer = TodoCreateSerializer(todo, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_200_OK)
        return Response(serializer.data, status=status.HTTP_400_BAD_REQUEST)

애플리케이션을 실행하고 변경을 확인

  • 서울 → 광주


완료로 수정, 완료된 목록을 조회

  • views.py 파일에 처리하기 위한 클래스를 생성
from rest_framework import status

from rest_framework.response import Response
from rest_framework.views import APIView

from .models import Todo

from .serializers import TodoSimpleSerializer, TodoCreateSerializer

class TodosAPIView(APIView):
    def get(self, request):
        todos = Todo.objects.filter(complete=False)
        serializer = TodoSimpleSerializer(todos, many=True)
        return Response(serializer.data,status=status.HTTP_200_OK)
    def post(self,request):
        # 파라미터를 가져와서 serializer를 생성
        serializer = TodoCreateSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status.HTTP_201_CREATED)
        return Response(serializer.errors, status.HTTP_400_BAD_REQUEST)

from .serializers import TodoDetailSerializer
from rest_framework.generics import get_object_or_404
class TodoAPIView(APIView):
    def get(self, request, pk):
        # id 가 pk 인 데이터를 조회하고 없으면 404 에러 발생
        todo = get_object_or_404(Todo, id=pk)
        serializer = TodoDetailSerializer(todo)
        return Response(serializer.data,status=status.HTTP_200_OK)

    def put(self, request, pk):
        # id가 pk 인 데이터를 조회하고 없으면 404 에러 발생
        todo = get_object_or_404(Todo, id=pk)
        serializer = TodoCreateSerializer(todo, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_200_OK)
        return Response(serializer.data, status=status.HTTP_400_BAD_REQUEST)

#

class DoneTodosAPIView(APIView):
    def get(self, request):
        dones = Todo.objects.filter(complete=True)
        serializer = TodoSimpleSerializer(dones,many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)

# 완료로 수정하기 위한 처리

class DoneTodoAPIView(APIView):
    def get(self, request, pk):
        done = get_object_or_404(Todo, id=pk)
        done.complete = True
        done.save()
        serializer = TodoDetailSerializer(done)
        return Response(serializer.data, status=status.HTTP_200_OK)

완료를 위한 URL 연결

  • urls.py
"""
from django.contrib import admin
from django.urls import path

from django.contrib import admin
from django.urls import path

from mytodoapp.views import TodosAPIView, TodoAPIView, DoneTodosAPIView, DoneTodoAPIView
urlpatterns = [
    path('admin/', admin.site.urls),
    path('todo', TodosAPIView.as_view()),
    path('todo/<int:pk>',TodoAPIView.as_view()),
    path('done', DoneTodosAPIView.as_view()),
    path('done/<int:pk>',DoneTodoAPIView.as_view()),

]

브라우저에서 테스트

  • pk = 1

  • 완료된 목록


TDD - ' 기능이 먼저다 '

  • Test Driven Development 의 약자로 테스트 주도 개발로 번역
  • 개발에서 진행을 담당하는 주체를 테스트에 두는 방법

테스트 종류

  • Unit Test : 함수 단위(메서드) 단위로 테스트

  • 통합 테스트 : 함수들을 모아서 전체적으로 제대로 구동 되는지 테스트

  • 인수 테스트 : 개발 환경과 운영환경이 다른 경우 개발 환경에서 운영환경으로 이전할 때 수행

  • 블랙 박스 테스트 : 기능 테스트

  • 화이트 박스 테스트 : 구조 테스트

  • 알파 테스트 : 개발자의 장소에서 사용자가 테스트

  • 베타 테스트 : 사용자의 환경에서 사용자가 테스트


단위 테스트 프로세스

  • 구현하려는 기능에 대한 테스트 코드를 작성 - 모양만 생성

  • 테스트 실행 - 실패

  • 테스트를 통과할 수 있는 최소한의 기능을 구현

  • 테스트를 실행시키고 통과시키면 코드를 정리

  • 모든 기능을 구현할 때 까지 이를 반복


구현 과정 - MVC

Model ➔ Service ➔ Controller ➔ View

  • service 까지는 별도의 프로그램이 필요 없음

예) 도서 정보를 삽입하는 작업의 테스트

  • test.py 라는 파일에 테스트를 위한 테스트 코드를 작성

from django.test import TestCase
from .models import Book   #사실 에러
# Create your tests here.
class ModelTest(TestCase):
    # 테스트를 수행하기 전에 호출되는 함수
    def setup(self):
        self.book_title = 'harry poter'
        self.book_author= 'lee'
        self.book = Book(
            title=self.book_title, author = self.book_author)

        # 테스트를 위한 함수

    def test_model_can_create_a_bucketlist(self):
        old_count = Book.objects.count()
        self.book.save()
        new_count = Book.objects.count()
        self.assertNotEqual(old_count, new_count)

테스트 코드 실행 - 에러 발생

$ python manage.py test

  • Book 이 없기 때문에 에러가 발생했기 때문에 Book을 만들어 줘야한다!

모델 역할을 수행할 클래스 생성

  • models.py
from django.db import models

# Create your models here.
class Todo(models.Model):
    title = models.CharField(max_length=100)
    description = models.TextField(blank=True)
    created = models.DateTimeField(auto_now_add=True)
    complete = models.BooleanField(default=False)
    important = models.BooleanField(default=False)
    # 디버깅을 위해서 사용 - print로 인스턴스를 출력하면 호출되는 메서드

    def __str__(self):
        return self.title
        
#### Book 클래스
class Book(models.Model):
    title = models.CharField(max_length=128)
    author = models.CharField(max_length=128)

마이그레이션 실행

$ python manage.py makemigrations
$ python manage.py migrate


다시 TEST 실행

$ python manage.py test
  • 에러

  • test_todoapidb 는 mariadb terminal 상에는 보이지 않음
  • grant all privileges 명령어를 사용하여 해결!


다시 테스트

  • 에러 - 현재 애플리케이션에서는 /api/books/ 를 처리하는 로직이 없기 때문
  • 요청을 정상적으로 처리할 수 있도록 serializers 를 생성
from rest_framework import serializers
from .models import Todo

class TodoSimpleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Todo
        fields = ('id', 'title', 'complete','important')

class TodoCreateSerializer(serializers.ModelSerializer):
    class Meta:
        model = Todo
        fields = ('title', 'description', 'complete','important')

class TodoDetailSerializer(serializers.ModelSerializer):
    class Meta:
        model = Todo
        fields = ('id', 'title', 'description', 'created', 'complete', 'important')

from.models import Book

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ('id','title','author')
  • views.py 파일에 삽입 요청을 처리할 로직을 생성
from rest_framework import status

from rest_framework.response import Response
from rest_framework.views import APIView

from .models import Todo

from .serializers import TodoSimpleSerializer, TodoCreateSerializer

class TodosAPIView(APIView):
    def get(self, request):
        todos = Todo.objects.filter(complete=False)
        serializer = TodoSimpleSerializer(todos, many=True)
        return Response(serializer.data,status=status.HTTP_200_OK)
    def post(self,request):
        # 파라미터를 가져와서 serializer를 생성
        serializer = TodoCreateSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status.HTTP_201_CREATED)
        return Response(serializer.errors, status.HTTP_400_BAD_REQUEST)

from .serializers import TodoDetailSerializer
from rest_framework.generics import get_object_or_404
class TodoAPIView(APIView):
    def get(self, request, pk):
        # id 가 pk 인 데이터를 조회하고 없으면 404 에러 발생
        todo = get_object_or_404(Todo, id=pk)
        serializer = TodoDetailSerializer(todo)
        return Response(serializer.data,status=status.HTTP_200_OK)

    def put(self, request, pk):
        # id가 pk 인 데이터를 조회하고 없으면 404 에러 발생
        todo = get_object_or_404(Todo, id=pk)
        serializer = TodoCreateSerializer(todo, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_200_OK)
        return Response(serializer.data, status=status.HTTP_400_BAD_REQUEST)

#

class DoneTodosAPIView(APIView):
    def get(self, request):
        dones = Todo.objects.filter(complete=True)
        serializer = TodoSimpleSerializer(dones,many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)

# 완료로 수정하기 위한 처리

class DoneTodoAPIView(APIView):
    def get(self, request, pk):
        done = get_object_or_404(Todo, id=pk)
        done.complete = True
        done.save()
        serializer = TodoDetailSerializer(done)
        return Response(serializer.data, status=status.HTTP_200_OK)

from .serializers import BookSerializer
from .models import Book
from rest_framework import generics

class CreateView(generics.CreateView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def perform_create(self,serializer):
        serializer.save()
  • urls.py 파일에 URL 처리를 생성
"""
from django.contrib import admin
from django.urls import path

from django.contrib import admin
from django.urls import path

from mytodoapp.views import TodosAPIView, TodoAPIView, DoneTodosAPIView, DoneTodoAPIView
from mytodoapp.views import CreateView
urlpatterns = [
    path('admin/', admin.site.urls),
    path('todo', TodosAPIView.as_view()),
    path('todo/<int:pk>',TodoAPIView.as_view()),
    path('done', DoneTodosAPIView.as_view()),
    path('done/<int:pk>',DoneTodoAPIView.as_view()),
    path('api/books/',CreateView.as_view())

]

  • 일반적으로는 Model에 필요한 모든 작업을 테스트 한 후 view로 넘어가는 방식이 있고 하나의 요청을 전부 테스트 한 다음 다른 요청으로 넘어가는 방식이 있다.
profile
무럭무럭 자라볼까
post-custom-banner

0개의 댓글