12. TDD와 REST API 만들기
unittest를 기반으로 동작TestCase 또는 APITestCase를 상속받아 작성from django.test import TestCase
from .models import MyModel
class MyModelTest(TestCase):
def test_model_creation(self):
my_model = MyModel.objects.create(name="Test")
self.assertEqual(my_model.name, "Test")
def test_example(self):
response = self.client.get('/my-url/')
self.assertEqual(response.status_code, 200)
from django.http import HttpResponse
def my_view(request):
return HttpResponse("Hello, World!")
class MyModelTest(TestCase):
def test_str_method(self):
my_model = MyModel(name="Test")
self.assertEqual(str(my_model), "Test")
from django.urls import reverse
class MyViewTest(TestCase):
def test_view_url_exists(self):
response = self.client.get(reverse('my_view'))
self.assertEqual(response.status_code, 200)
tests.py 에 있던 코드들이 __init__.py로 이동__init__.py를 다시 만들거나 잘라내기 해서 코드를 옮긴다.



self.assertEqual(response.context.get('blog_list'), blog_list.count())
해결
AssertionError: 3 != 2
from .forms import MyForm
class MyFormTest(TestCase):
def test_valid_form(self):
form = MyForm(data={'name': 'Test'})
self.assertTrue(form.is_valid())
def test_invalid_form(self):
form = MyForm(data={'name': ''})
self.assertFalse(form.is_valid())
from django.db.models.signals import post_save
from .models import MyModel
class MySignalTest(TestCase):
def test_post_save_signal(self):
with self.assertLogs('myapp', level='INFO') as cm:
MyModel.objects.create(name="Test")
self.assertIn('Signal triggered', cm.output[0])
python manage.py test python manage.py test <app name>

is_active 추가 및 content 의 TextField가 TimeField여서 오류 였음

의도적으로 틀린값을 주었을 때 어디서 오류가 나고 고쳐야하는지 나옴

setUp
함수 이름 test_ 필수

행동을 HTTP Method(get / post / put / patch / delete)를 사용하여 표현
/슬래시는 계층 관계를 나타낼때 사용
마지막 슬래시는 포함하지 않는다
Resource들은 명사를 사용합니다 -> /blog/create/
_보다 -을 권장
소문자를 사용함
파일 확장자는 포함하지 않음
응답은 JSON이 대세
GET -> http://localhost:8000/avengers/hulk
GET -> http://localhost:8000/blog/
POST -> http://localhost:8000/blog/ => JSON
PUT,PATCH -> http://localhost:8000/blog/1 => JSON
DELETE -> http://localhost:8000/blog/1 => JSON

APPEND_SLASH

admin페이지 이용 - python manage.py createsuperuser



from rest_framework import serializers
from .models import MyModel
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = '__all__'
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class MyAPIView(APIView):
def get(self, request):
data = {"message": "Hello, World!"}
return Response(data, status=status.HTTP_200_OK)
def post(self, request):
data = request.data
return Response(data, status=status.HTTP_201_CREATED)
from rest_framework import generics
from .models import MyModel
from .serializers import MyModelSerializer
class MyModelListCreateView(generics.ListCreateAPIView):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer
from rest_framework import viewsets
from .models import MyModel
from .serializers import MyModelSerializer
class MyModelViewSet(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer
from rest_framework.routers import DefaultRouter
from .views import MyModelViewSet
router = DefaultRouter()
router.register(r'mymodels', MyModelViewSet)
urlpatterns = [
path('', include(router.urls)),
]
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
class MyAPIView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request):
return Response({"message": "Authenticated!"})
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
class MyAPIView(APIView):
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
def get(self, request):
return Response({"message": "Token authenticated!"})
from rest_framework.throttling import UserRateThrottle
from rest_framework.views import APIView
class MyAPIView(APIView):
throttle_classes = [UserRateThrottle]
def get(self, request):
return Response({"message": "Throttled request!"})
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
}



ViewSet은 Django REST Framework에서 제공하는 강력한 기능ViewSet은 기본적으로 Django의 뷰와 비슷함ViewSet: 모든 CRUD 작업을 직접 정의할 수 있는 기본 ViewSet.ModelViewSet: ViewSet을 상속받아 모델의 CRUD 작업을 자동으로 처리ReadOnlyModelViewSet: 읽기 전용 작업(리스트, 상세 조회)만 처리ModelViewSet은 가장 많이 사용되는 ViewSet, 모델의 모든 CRUD 작업을 처리queryset과 serializer_class만 정의하면, 나머지 작업은 자동으로 처리됨from rest_framework import viewsets
from .models import MyModel
from .serializers import MyModelSerializer
class MyModelViewSet(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer
ReadOnlyModelViewSet은 읽기 전용 API를 제공하며, list와 retrieve 작업만 지원from rest_framework import viewsets
from .models import MyModel
from .serializers import MyModelSerializer
class MyModelReadOnlyViewSet(viewsets.ReadOnlyModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer
from rest_framework import viewsets
from rest_framework.response import Response
class CustomViewSet(viewsets.ViewSet):
def list(self, request):
data = {"message": "This is a list view"}
return Response(data)
def retrieve(self, request, pk=None):
data = {"message": f"This is the detail view of {pk}"}
return Response(data)
Router는 URL 라우팅을 자동으로 처리해주는 Django REST Framework의 기능SimpleRouter는 가장 기본적인 Router로, ViewSet과 연결하여 기본적인 CRUD URL을 자동으로 생성python코드 복사
from rest_framework.routers import SimpleRouter
from .views import MyModelViewSet
router = SimpleRouter()
router.register(r'mymodels', MyModelViewSet)
urlpatterns = router.urls
GET /mymodels/ → list 뷰GET /mymodels/{pk}/ → retrieve 뷰POST /mymodels/ → create 뷰PUT /mymodels/{pk}/ → update 뷰DELETE /mymodels/{pk}/ → destroy 뷰DefaultRouter는 SimpleRouter의 기능에 더해, 기본 API 루트 엔드포인트를 추가로 생성 해줌/ 에 API 루트 엔드포인트가 추가되어 사용자가 어떤 엔드포인트를 사용할 수 있는지 쉽게 파악가능rom rest_framework.routers import DefaultRouter
from .views import MyModelViewSet
router = DefaultRouter()
router.register(r'mymodels', MyModelViewSet)
urlpatterns = router.urls
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import MyModelViewSet, custom_view
router = DefaultRouter()
router.register(r'mymodels', MyModelViewSet)
urlpatterns = [
path('', include(router.urls)),
path('custom/', custom_view),
