Django restframework(DRF) - simple CRUD with model, view, serializer and Unit Test

정현우·2021년 11월 29일
6

Django Basic to Advanced

목록 보기
18/40

DRF - simple CRUD with model, view, serializer

앞서나온 내용을 바탕으로 model을 하나 만들고, 얼마나 심플하게 CRUD를 만들 수 있는지 한 번 직접해보자. 그리고 testing까지 해보자. ORM과 시리얼라이저를 통해 아주 깔끔하고 짧게 CRUD를 만들고 test까지 마무리 할 수 있다.

model

출처 : Django Book(https://djangobook.com/)

django model 만들기

  • app을 create하는 부분은 생략하고, 로직 자체에 집중하겠다. python manage.py startapp

  • 만든 app의 디렉토리 -> models.py에서 from django.db import models에서 models의 Model을 활용하자.

class SearchDomain(models.Model):
    domain_column = models.CharField(blank=False, null=False, unique=True, max_length=20)
    value = models.CharField(blank=False, null=False, max_length=1024)
    created_at = models.DateField(auto_now_add=True)

    def __str__(self):
        return str({
            "domain_column": self.domain_column,
            "vlaue": self.value
        })

model makemigrations

  • (가상환경은 당연하다) python manage.py makemigrations app_name 으로 우리가 만든 app의 수정된 Model 값을 마이그레이션 시켜줄 py 파일을 만들어 준다.

  • 앱 디렉토리 하위에 migrations 폴더에 아래와 같이 파일이 생성되는 것을 볼 수 있다.

  • django.db의 migrations.Migration을 활용해서 우리가 만든 model -> migration python file -> DB schema create 로 흘러간다.

  • 최소한의 자세한 사항은 공식 문서에서 migrations를 꼭 읽어보자, 미리 알고 있으면 좋은 django cli 명령어는 python manage.py showmigrationspython manage.py sqlmigrate 이다.

    • python manage.py showmigrations 는 DB 변경사항 목록과 상태를 출력한다.
    • python manage.py sqlmigrate 는 실행할 SQL 명령문을 출력한다. 어떤 명령문을 실행할지 확인할 떄 사용하고, 튜닝이 안된 쿼리나 슬로우 쿼리 여부를 확인할 수 있다.
    • python manage.py dumpdata 현재 DB의 내용을 백업할 때 사용한다.
    • python manage.py loaddata 백업 파일에서 DB로 내용을 복구 할 때 사용한다.
    • 역시 더 자세한 내용은 공식문서에 잘 나와 있다.

model migrate

  • python manage.py migrate [app_name: 생략가능] 으로 우리가 만든 makemigrations file 기반으로 migrate를 진행 해준다.

  • 규모가 조금 있을 것 같다면, python manage.py sqlmigrate으로 sql 쿼리 체크 하고 슬로우 쿼리나 튜닝 대상을 꼭 체크하는 것을 추천한다.

  • DB가 바뀌어도, 해당 DB에 migration log가 없으면, 전체를 (즉 migration 파일이 만들어진 순서대로) 순차적으로 실행한다. table에 대한 create - drop이 잦다면, 버전 관리도 분명 고려 대상이다.

    • 다른 말로 하면, 해당 파일과 로그만 잘 컨트롤 하면, 버전 자체를 롤백을 할 수 있다. 사실 공식적으로 버전 롤백하는 방법이 있지만, hot fix의 경우, 또는 이후 버전 자체를 고려 할 필요가 없어졌다면 히스토리로 가지고 있을 필요가 없다. 아래와 같은 순서로 우린 마이그레이션 흔적을 지울 수 있다.
  1. 마이그레이션 file 지움
  2. select * from django_migrations; 를 통해 어떤 앱의 마이그래션 진행되었는지 다 지움
  3. 캐시 지움
  4. 디비에서 Drop table로 타겟 테이블 다 지움;
  5. 이제 마치 처음 하는 것 마냥 makemigration → migrate 성공

view, serializer

제네릭

  • 함수형 말고 클래스형으로, 제네릭 상속 받아서 view API를 만들어 보자. DRF의 생산성을 최고점으로 끌어올려주는게 generics 다. 추상화 되어 있는 부분이 많기때문에 초반 러닝 커브는 있지만, 어느정도만 익숙해져도 사용하기 편하다.

  • 우리가 만든 app -> view.py에 아래와 같이 코드를 짜보자.

from rest_framework import generics

# search domain model Create(post), Read(get)
class SearchDomainListCreateAPIView(generics.ListCreateAPIView):
    queryset = SearchDomain.objects.all().order_by('-id')
    serializer_class = SearchDomainSerializer
    
    # SearchDomainSerializer 은 우리가 시리얼 라이저 따로 만들어서 임포트 해줘야 한다. 
  • 이렇게만 코드를 짜두고 시리얼라이저만 만들어주면 create(post), read(get)은 끝이다. generics.ListCreateAPIView 이 다 해주기 때문이다. 코드가 참 깔끔해져 버리고, 가시성도 확 와닿는다. 커맨드(컨트롤) + 좌클릭 으로 상속 받는 class를 따라가 대표적인 오버라이딩 전용 메소드(함수)들은 꼭 한 번 확인 하고 사용하길 바란다.

  • 무엇을 오버라이딩 해서 사용해야 하나?

    • post(create)는 CreateAPIView
    • get(read-many)는 ListAPIView
    • get과 post는 ListCreateAPIView
    • get(read-one)은 RetrieveAPIView
    • put(update-one)은 UpdateAPIView
    • delete(delet-one)은 DestroyAPIView
    • get(read-one)과 put(update-one)은 RetrieveUpdateAPIView
    • get, put, patch, delete는 RetrieveUpdateDestroyAPIView
  • 사실 update-one, many경우와 부분 update를 위해 partial=True를 활용하는 등 세부적으로 꼭 살펴봐야할 부분은 있다. 물론 create many도 말이다.

    • 위 사항에서 검색 키워드는 "drf patch partial" 과 "drf create many" 정도로 찾아보면 많이 나온다.

보통 ListCreateAPIView과 url에서 <int:pk> 또는 querystring 값을 request.GET.get("query") 활용해서 RetrieveUpdateDestroyAPIView 두 제네릭 클래스를 하나의 모델에 대해 만들어 둔다. 이렇게만 해두면 하나의 모델에 대해 CRUD는 끝이다. 생산성 하나는 기가막힌다.

  • 사실 api를 만들다 보면, 오버라이딩을 해서 사용할 경우가 대부분이다. 그리고 auth에 대해 인가된 사용자만 허용하게 하고 싶은 경우도 기본적으로 고민해야 할 것이다. rest-auth 키워드를 통해 찾아보자.

통신에 사용할 시리얼라이저

  • 필자는 spring에서 직접 만들어서 사용하는 http reqeust - response class와 비슷하다고 많이 느꼈다.

  • view에서 사용하는 SearchDomainSerializer를 만들자. django는 serializer를 기본적으로 사용하지 않기 때문에 serializers.py를 만들자.

from rest_framework import serializers
from search.models import SearchDomain

class SearchDomainSerializer(serializers.ModelSerializer):
    class Meta:
        model = SearchDomain
        fields = "__all__"
  • 기본적으로 serializers.ModelSerializer를 사용한다. 아주 기본적인 시리얼 라이저 형태이고, 커맨드(컨트롤) + 좌클릭 으로 상속 받는 class를 따라가보면 model의 filed 형태에 따라 매핑이 되어있는 것을 확인할 수 있다.

  • 이제 request <-> response 모두 json 형태로 가능하다. json형태의 data를 우리는 시리얼라이저를 통해 python dict(더 정확하게는 model object) 처럼 사용이 가능해졌다. restAPI 가 위 간단한 model / view / serializer 코드로 끝이 났다.

  • url에 path('admin/searchdomain/', SearchDomainListCreateAPIView.as_view(), name='search-domain-list-createAPI') 로 해당 view를 매핑해주면 url 세팅도 끝이다.


Testing

DRF - URLPatternsTestCase

  • DRF 공식 문서의 test에 대한 부분 에서 URLPatternsTestCase
    기준
    으로 작성한다. test에 들어가기 앞서 우리가 setting에서 DB에 사용하는 user를 "test_원래DB이름" schema (DB)에 접근 권한을 줘야한다.
GRANT ALL ON test_원래DB이름.* TO '유저'@'%';
flush privileges;
commit;
  • 필자는 mysql 5.X 버전이라 위와 같이 권한을 준다. 그리고 아래 코드를 만든 app -> test.py에 작성해주자.
from django.test import TestCase
from django.urls import reverse, include, path

# 또는 직접 view를 import
from search.views import SearchDomainListCreateAPIView

from user.models import User

from rest_framework.test import APITestCase, APIClient, URLPatternsTestCase
from rest_framework.views import status

class SearchDomainListCreateAPIViewTestCase(APITestCase, URLPatternsTestCase):

    urlpatterns = [
        path('api/search/', include('search.urls')),
    ]
        
    def test_get_search_domain_data(self):
        # user = User.objects.get(username='여기에서 auth있는 user')
        # client = APIClient()
        # client.force_authenticate(user=user)

        url = reverse(SearchDomainListCreateAPIView)
        response = self.client.get(url, format='json')
        print(dir(response))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
  • python manage.py test app_name으로 테스트 진행을 하자!
System check identified no issues (0 silenced).
..
----------------------------------------------------------------------
Ran 1 tests in 0.377s

OK

DRF - DB 없이 간단한 unit test 하기

  • 테스트 DB없이 Unit Test 하기 in Django 글을 참조하며 Got an error creating the test database: (1044, "Access denied for user ... 에 대해 고려 없이 테스트 코드를 작성해보자.

  • 먼저, django에서 제공하는 from django.test import TestCase를 바로 사용하려면 django는 DB에 test_dbname... 우리가 설정한 db이름 앞에 test 접두사를 붙여 DB를 만들고 테스트를 진행하려고 한다. AWS의 RDS를 사용하면 사실 이런 부분이 '귀찮다'

    • 정석대로라면, DBMS의 django user에게 권한을 추가하면 된다. 보통 모든 권한을 줘버린다 ㅎ
  • 그리고 우리가 test 전용 db가 있다면, (가령 개발계 DB로 테스트를 할 것이다) 해당 부분을 우리가 오버라이딩과 직접 설정값을 활용해서 테스트할 수 있다.


마무리

  • 물론 더 깊은 얘기들이 있고, 실제 활용할때 알아야 할 부분은 더 많다. 하지만 기본적으로 위와 같은 뼈대에서 출발해 이것 저것 수정하며 알아보면 된다.

  • 코드 자체에 대해서 더 깊은 사용은 ORM에 대한 이해도 필요하고, view에서 제네릭과 관련된 queryset, pagenation과 permission_classes = [IsAdminUser] 등으로 사용하는 rest-auth 가 더 기본적으로 알아야 할 부분이다. 물론 '리펙토링'을 해야 할 부분에 대해서도 말이다.

  • 더 깊은 내용은 시리즈를 더 진행하며 단계적으로 정리할 예정이다.

  • 해당 내용을 https://kimdoky.github.io/django/2018/08/21/drf-tda-1/ 와 같이 비교하며 읽으면 좋을 듯하다.

profile
도메인 중심의 개발, 깊이의 가치를 이해하고 “문제 해결” 에 몰두하는 개발자가 되고싶습니다. 그러기 위해 항상 새로운 것에 도전하고 노력하는 개발자가 되고 싶습니다!

0개의 댓글