단일 모델에서 데이터를 조회해서 직렬화(Serialization)하여 JSON으로 응답하기를 반복합니다. 결국 핵심은 1. 데이터 조회 2. 직렬화 3. JSON으로 응답 입니다
ModelSerializer 결국 Model의 여러가지 필드들을 어떻게 직렬화해서 데이터의 포맷을 잡을지가 핵심입니다. Django의 Model → ModelForm 사용과 굉장히 유사한 형태입니다.
자 다시 시작하는 마음으로 urls., views 전부 제거 참고로 templates는 이제 안 쓰기에 제거
그리고 urls과 views를 새롭게 만들어 줍니다.(이 DRF의 함수로 만드는 views는 API 데코레이터 아시죠? DRF의 함수의 경우 이거 안 쓰면 무조건 에러)
아, 참고로 ()안에 아무것도 안 넣으면 기본적으로 GET입니다. 참고로 저거 말고 다른 방식으로 왔을 경우 405 출력
자, 이제 views를 적기전 기억나시나요 3가지 규칙 1. 데이터 조회 2. 직렬화 3. JSON으로 응답 어떻게? 1. article = Article.objects.all() 2. serializer = ArticleSerializer(article, many=True) 3. json_data = serializer.data -> 이렇게 데이터로 접근이 가능합니다. 그리고 return Response(json_data)
자 이제 하나씩 해봅시다 1. Article 목록 조회 (List)
그리고 postman으로 200OK까지 확인
그러면 이제 상세 조회를 해봅시다. 그런데 알아야 할 점이 있습니다. 이제 템플릿이 거의 필요가 없게 되면서, 사실상 app_namespace, 혹은 urls names까지 사실상 필요가 없게 됩니다(하지만 난 쓸것이여). 그리고 추가할 게 저 ArticleSerializer입니다. 사실 저건 어렵게 생각할 게 아니라, models에서 가져온 데이터를 Serializer로 한번 더 덮어서 주는거랑 비슷합니다. 예를 들면 공장에서 키켓 과자의 안쪽을 만들었다고 하면(models) 그것을 초콜릿으로 한번 더 씌어주는(Serializer)거랑 비슷합니다.
그럼 이제 상세 페이지도 한번 만들어 봅시다. 2. Article 상세 조회 (Detail)
자 이것이 바로 상세 페이지인데, 혹시 모르니 get_object_or_404입니다. 이걸 왜 써? 하실 수 있는데, 만약 pk가 저희가 가지고 있지 않는 999999라고 들어오면, 저기서 바로 에러가 납니다. 그렇기에 저걸 써 주는 겁니다. 만약 objects를 가져오는데, 없다면 404를 띄어라 이 말입니다.(첫번째 인자로 가져올 objects(Article), 두번째로 데이터)
자 그러면 이제 확인하러 postman으로 고고
문제가 없는것을 확인할 수 있습니다.
3. Article 생성 (Create) 먼저 models을 봐야합니다.
자, 이것들은 전부 필수적으로 존재해야 하며, 또한 자동적으로 값을 만드는 필드를 제외하면, 값을 무조건 적으로 줘야합니다.(만약 줄 수 없는 상황을 위해서 null =True을 줍니다.
null=True의 의미와 역할 데이터베이스 레벨에서의 설정: null=True는 데이터베이스에서 해당 필드가 NULL 값을 허용하도록 정의합니다. 즉, 이 필드가 비워질 수 있음을 의미합니다.
기본값: 만약 null=True를 설정하지 않으면, Django는 기본적으로 필드에 NOT NULL 제약 조건을 추가합니다. 이는 데이터베이스에 값이 반드시 있어야 한다는 의미입니다.
즉, 여기서는 title, content만 받으면 된다는 소리 입니다. 그럼 이걸 어떻게 request로 보내고, response를 받으란 소리인가? -JSON으로 주고 받아야 합니다!(참고로 Form도 가능합니다)
그래서 코드로 돌아오면, 저희는 새로운 urls를 만들 필요가 없습니다. 왜냐하면 처음으로 돌아가서, GET이면 조회, POST면 뭐다? 새로운 생성이라고 했습니다. 즉, article_list에서 조회말고 POST로 들어온다면 새로운 값을 조회말고 추가할 수 있기 때문입니다.
이렇게 말입니다. 그런데 궁금하지 않나요? 원래 title = request.POST.get('title') 막 이렇게 하는데 말입니다. 사실
저 request.data에서 data에는 거의 모든 데이터들을 담고 있습니다. 즉, request.data를 data에 담궈서 .get으로 가져오는 것은 똑같다는 말입니다. 자, 그런데 밑에 빈 딕셔너리는 뭐죠?라고 하실 수 있습니다. 자, 저 부분은 POST입니다. 즉, 값을 생성하는 파트입니다. 중요한 점은 데이터 생성과 응답은 별개입니다. POST 요청이 들어오면, 코드에서 데이터를 받아서 새로운 글(Article)을 이미 생성을 하였고, 응답은 단지 결과를 알려주는 것입니다. 즉, 데이터를 생성한 후, Response({})로 빈 JSON 응답을 보내준 것뿐이며, 즉, 응답 내용에는 생성된 데이터가 없어도, 데이터 생성 자체는 위 코드로 끝난 것입니다.
자, 아무튼 여기서 중요한 점이 있습니다. 사실 serializers를 사용해 더 간편하게 사용할 수 있습니다(완전히 form처럼)
is_valid? 이거 폼에서 쓸 수 있는거 아닌가? 맞습니다 Serializer는 거의 form처럼 사용이 가능하기에, 저렇게 사용이 가능한 것 입니다. 그런데 도대체 저 Response가 뭔지 궁금하지 않으신가요? HttpResponse도 그렇구요(참고로 저기 201은 써도되고 안 써도 됩니다 어차피 둘 다 성공을 의미하기 때문 암튼 저걸 쓰면 성공을 할 때 200이 아니라 201로 띄우기 됩니다..) 1. HttpResponse HttpResponse는 서버에서 사용합니다. 서버는 클라이언트(웹 브라우저 등)의 요청에 대해 응답을 보내기 위해 HttpResponse를 사용합니다. 그리고 HttpResponse는 HTML이나 일반 텍스트를 응답으로 보낼 때 사용됩니다. 즉, 클라이언트가 요청한 HTML 페이지나 단순한 텍스트 응답을 보내는 데 사용됩니다. 즉, 클라이언트(웹 브라우저 등)가 HTTP 요청을 보내면, 서버는 이에 대한 응답을 HttpResponse로 돌려줍니다.
그리고 클라이언트가 요청을 보내면 서버는 응답을 반환합니다. 이 응답을 HttpResponse 객체로 만들어 클라이언트에게 보냅니다. 즉, HTML, 일반 텍스트를 보내기 위해 사용됩니다. 예를 들어, 웹 페이지에 들어가면 HTML 코드를 서버가 반환하고, 클라이언트(브라우저)는 그 HTML 코드를 웹 페이지로 렌더링합니다. 예시:
from django.http import HttpResponse
def hello_view(request):
return HttpResponse("<h1>Hello, World!</h1>")
여기서 사용자가 /hello URL을 요청하면 서버는 HTML 코드를 반환하고, 브라우저는 이를 웹 페이지로 렌더링하여 "Hello, World!"를 표시합니다.
2. Response (Django REST Framework) 보통 서버에서 사용되며, Django REST Framework (DRF)를 사용하여 API 응답을 보낼 때 Response를 사용합니다. 그리고 주로 JSON 형식으로 데이터를 응답하는 데 사용됩니다. 만약 클라이언트가 API 요청을 보내면, 서버는 이 요청을 처리하고 Response를 사용하여 JSON 데이터나 직렬화된 객체를 클라이언트에게 반환합니다. 즉, 클라이언트가 API 요청을 보내면, 서버는 Response 객체로 응답을 보냅니다.
추가적으로 JSON 데이터, 직렬화된 객체. 예를 들어, 사용자가 API로 게시글 목록을 요청하면, 서버는 JSON 형식으로 게시글 데이터를 응답합니다.
예시 코드:
from rest_framework.response import Response
from rest_framework.decorators import api_view
from .models import Article
from .serializers import ArticleSerializer
@api_view(['GET'])
def article_list(request):
articles = Article.objects.all()
serializer = ArticleSerializer(articles, many=True)
return Response(serializer.data)
사용자가 GET /api/articles API 요청을 보내면, 서버는 JSON 형식으로 게시글 목록을 응답합니다.
3. JsonResponse 주로 서버에서 사용되며, JsonResponse는 Django에서 JSON 응답을 간단하게 반환할 때 사용됩니다.
주로 딕셔너리나 리스트 형태의 데이터를 JSON 형식으로 변환하여 응답합니다. 예를 들어, 로그인 성공 메시지나 오류 메시지를 JSON 형식으로 보내는 데 사용됩니다. 즉, 클라이언트가 HTTP 요청을 보내면, 서버는 JsonResponse로 데이터를 JSON 형식으로 반환합니다.
버는 JSON 응답을 보내며, 주로 딕셔너리 형태의 데이터를 클라이언트에 JSON 형식으로 응답합니다. 예시 코드:
from django.http import JsonResponse
def login_view(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'admin' and password == 'password123':
return JsonResponse({'message': 'Login successful'}, status=200)
else:
return JsonResponse({'message': 'Invalid credentials'}, status=400)
사용자가 로그인 폼을 제출하면 서버는 JsonResponse를 사용해 로그인 성공/실패 메시지를 JSON 형식으로 반환합니다.
요약하면, HttpResponse는 웹 페이지 응답을 HTML로 보낼 때 사용됩니다. Response는 REST API에서 JSON 데이터를 반환할 때 사용됩니다. JsonResponse는 간단한 JSON 응답을 반환할 때 사용됩니다.
즉, 우리가 알아야 할 점은 클라이언트가 아니라 서버의 입장에서 생각하면서 코드를 짜는 게 중요한 포인트입니다. 웹 개발에서 서버는 클라이언트의 요청을 받고 응답을 보내는 역할을 합니다.
그렇기 때문에, Response(serializer.data, status =201)은 결국 이 값을 클라이언트에 전해라 라는 뜻과 비슷합니다. 그런데 코드에서 만약 is_valid하지 않다면?
serializer.errors를 통해 에러를 발생시키게 하면 됩니다. 아 참고로
아까 말했듯이, 이렇게도 보낼 수 있습니다.
여기서 혹시 모르니 한번더 forms.py와 models.py의 차이점 models.py: 데이터베이스와 관련된 "데이터 구조"를 정의합니다. models.py는 데이터를 어떻게 저장할지를 정의하는 곳이며, 여기서 작성한 클래스는 Django ORM(Object-Relational Mapping)을 통해 데이터베이스 테이블로 변환이 됩니다. 그리고 주로 데이터의 구조와 제약 조건을 설정합니다. 예시:
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=100) # 제목
content = models.TextField() # 내용
created_at = models.DateTimeField(auto_now_add=True) # 생성 시간
설명: title: 제목을 저장하기 위한 짧은 문자열 필드. content: 긴 텍스트를 저장하기 위한 필드. created_at: 글이 생성된 시간을 자동으로 저장. 결과적으로 이러한 데이터의 형식이 나옵니다 forms.py: 사용자가 데이터를 입력할 수 있는 "폼"을 정의하고, 데이터를 검증하는 역할을 합니다. forms.py는 사용자가 데이터를 입력하거나 수정할 때 사용할 "폼"을 정의해. 폼은 데이터를 입력받고, 검증(validation)하고, 필요하면 수정하는 데 사용이 됩니다. 예시:
from django import forms
class ArticleForm(forms.Form):
title = forms.CharField(max_length=100) # 제목 필드
content = forms.CharField(widget=forms.Textarea) # 내용 필드
설명: title:사용자가 글 제목을 입력할 수 있는 필드. content: 내용을 입력할 수 있는 필드 (텍스트박스 형태). 폼은 사용자가 입력한 데이터가 올바른지 확인하고, 문제가 있다면 오류 메시지를 보여줘.
예를 들어: 제목이 너무 길거나 비어 있으면 에러를 반환.
모델과 폼을 함께 사용하는 이유 모델(models.py)은 데이터를 저장하고 관리하는 백엔드의 설계도이며, 폼(forms.py)은 사용자로부터 데이터를 입력받고 검증하는 프론트엔드의 인터페이스입니다.
이 둘을 함께 사용하면, 모델에서 정의된 데이터 구조를 기반으로 폼을 자동으로 생성하고, 입력 데이터를 데이터베이스에 쉽게 저장할 수 있습니다.
(1) ModelForm ModelForm은 Django에서 제공하는 특별한 폼 클래스로, 모델 기반 폼을 쉽게 만들게 해줍니다. models.py에 정의된 모델을 읽어서, 폼을 자동으로 생성. 데이터 검증과 저장이 쉽게 연결됨.
(2) 뷰(View)에서의 처리 흐름 사용자가 데이터를 입력해 폼을 제출한다. 폼은 데이터를 검증(is_valid())하고, 유효하면 모델을 통해 데이터를 저장한다. 잘못된 입력이면, 에러 메시지를 포함한 폼을 다시 보여준다.
예시: 블로그 게시글 작성 models.py: 데이터 모델 정의
from django.db import models
class BlogPost(models.Model):
title = models.CharField(max_length=100) # 제목 필드
content = models.TextField() # 내용 필드
created_at = models.DateTimeField(auto_now_add=True) # 작성 시간 자동 저장
def __str__(self):
return self.title
BlogPost 모델은 게시글 제목(title), 내용(content), 작성 시간(created_at)을 저장.
forms.py: 모델 기반 폼 정의
from django import forms
from .models import BlogPost
class BlogPostForm(forms.ModelForm):
class Meta:
model = BlogPost # BlogPost 모델 기반 폼
fields = ['title', 'content'] # 포함할 필드 선택
ModelForm 설명: model = BlogPost: BlogPost 모델을 기반으로 폼을 생성. fields = ['title', 'content']: 폼에 포함할 필드를 지정. (사용자가 created_at은 입력하지 못하도록 제외)
views.py: 폼을 처리하는 뷰
from django.shortcuts import render, redirect
from .forms import BlogPostForm
def create_blog_post(request):
if request.method == 'POST':
form = BlogPostForm(request.POST) # 사용자가 보낸 데이터로 폼 생성
if form.is_valid(): # 데이터 검증
form.save() # 폼의 데이터를 BlogPost 모델에 저장
return redirect('blog_list') # 글 목록 페이지로 이동
else:
form = BlogPostForm() # 빈 폼 생성
return render(request, 'create_blog_post.html', {'form': form})
뷰 설명: GET 요청: 사용자가 처음 페이지를 방문하면, 빈 폼을 생성해 사용자에게 보여줌. POST 요청: 사용자가 데이터를 입력해 제출하면, 폼에 데이터를 바인딩. form.is_valid()로 입력된 데이터가 유효한지 확인. 유효하면, form.save()로 데이터를 BlogPost 모델에 저장. 리다이렉션: 저장이 완료되면, 글 목록 페이지로 이동.
그런데 만약 form이 아니라 커스텀을 통해(예: label, input 등) 값을 전달했다면 어떻게 받아야 할까요? 참고로 사용자가 HTML을 직접 커스터마이즈하고 싶다면, Django 폼 필드를 수동으로 렌더링해야 합니다.
레이블을 직접 만들고 텍스트 필드를 커스터마이즈 forms.py: BlogPostForm
from django import forms
from .models import BlogPost
class BlogPostForm(forms.ModelForm):
class Meta:
model = BlogPost
fields = ['title', 'content'] # 필드 정의
labels = {
'title': 'Enter your custom title:', # 기본 레이블 변경 가능
'content': 'Write your custom content:'
}
create_blog_post.html: 템플릿에서 커스터마이즈된 HTML
<!DOCTYPE html>
<html>
<head>
<title>Create Blog Post</title>
</head>
<body>
<h1>Create a New Blog Post</h1>
<form method="post">
{% csrf_token %} <!-- CSRF 토큰 -->
<!-- 커스텀 레이블 -->
<label for="id_title">Custom Title Label:</label>
<input type="text" id="id_title" name="title" value="{{ form.title.value|default:'' }}">
<!-- 커스텀 텍스트 영역 -->
<label for="id_content">Custom Content Label:</label>
<textarea id="id_content" name="content">{{ form.content.value|default:'' }}</textarea>
<!-- 제출 버튼 -->
<button type="submit">Submit</button>
</form>
</body>
</html>
대충 이렇게 한다라고 이해하시면 편합니다.
자, 그러면 postman을 써봅시다.
참고로 여기선 Params가 아니라, Body에 raw로 가셔서 값을 직접 넣어주면 됩니다.
자 이렇게하면 201이 뜨면서 성공합니다. 아 참고로 여기선 그냥 따옴표가 아닌, 쌍따옴표를 써야합니다.
자 여기에 raise_exception = True가 있습니다. 이것이 뭐냐면, 아까 코드에서
elif request.method == 'POST':
serializer = ArticleSerializer(data = request.data)
if serializer.is_valid(raise_exception = True):
serializer.save()
return Response(serializer.data, status =201)
return Response(serializer.errors, status = 400)
여기서 return하고 또 return이 있으니 보기 싫어집니다. 그래서 쓰이는것이 바로 저 raise_exception = True입니다. 마지막 저 return(즉, else)를 대신 해준다고 생각하시면 됩니다. 즉, 만약 is_valid하지 않다면, raise_exception 즉, 예외를 생성한다라는 뜻입니다
자 그러면 이제, 솔직히 만약에 status를 잘못 기입하면 어떻게 될까요? 그런 상황을 막기위해 status란것이 존재합니다.
from rest_framework import status
여기서 가져와서.
이렇게 쓰면 됩니다.
4. Article 삭제 (Delete) 그럼 delete도 urls를 적어줘야겠네? ㄴㄴ 아닙니다 똑같이 article_detail로 가셔서 하시면 됩니다. 하지만 이번에는 GET이 아닌 DELETE이겠죠?
특정 pk로 접근했을 경우, 그 pk가 삭제되도록 하는 로직입니다.
이제 실행하면 204가 정상적으로 뜨는것을 볼 수 있습니다.
5. Article 수정 (Update) 이것도 똑같이 detail에서 해주시면 됩니다.(여기선 instance를 써야합니다 -> 만약 값을 바꾸는 상황이 오면 instance를 써야하는데, 만약 instance = ???을 쓰고 .save()를 하면 이 instance는 새로운 값을 생성하는게 아니라, 값을 update하는 방식으로 전환이 됩니다.)
여기서 data에 request.data를 넣어서 save()를 했지만 수정도 비슷하게 넣는데,(data = request.data) 그런데 그냥 하는것이 아니라, 첫번째 인자로, 처음 조회한 값을 넣어줍니다. 즉,
이렇게 쓴다는 겁니다. POST에서 ArticleSerializer에 처음 인자로 클라이언트가 보낸 값을 저장하게 했습니다. 하지만 여기서는 처음 받는 값으로 조회한 값이 들어가게 됩니다. 약간 감이 오시나요?
즉, 받는 값이 우리가 준 값이 아니라, 조회한 값이 들어가게 된다는 뜻입니다. 그 뒤로 data를 넣어주게 되는 것이죠. 추가적으로 data=request.data의 의미는 클라이언트가 보낸 JSON 데이터를 받아서 ArticleSerializer의 인스턴스를 생성합니다. 즉, 이 데이터가 모델(Article)의 필드 요구사항과 유효성 조건을 만족하는지 검증하려는 목적입니다.
조금 더 자세히 설명하겠습니다. 1. ArticleSerializer(data=request.data) 여기서 클래스는 데이터를 역직렬화(deserialization): 클라이언트가 보낸 JSON 데이터를 받아 Django 모델 인스턴스를 생성할 수 있도록 준비. 데이터를 직렬화(serialization): Django 모델 인스턴스를 JSON으로 변환해 클라이언트에게 전달. 의 두가지 주요 역할을 합니다. 첫 번째 인자: data data는 클라이언트가 보낸 데이터를 받는 매개변수입니다. 이 경우, data=request.data를 전달했으니 request.data에 담긴 클라이언트의 JSON 데이터를 받아 새로운 객체를 생성하려는 상황입니다. 즉, data를 받아야 하는데, 뭘 받아야할지 =request.data를 통해 데이터를 전해주는 겁니다(쉽게 생각해서 pk = pk라 생각하시면 됩니다). 이 코드는 주로 POST 요청에서 사용되며, 새롭게 데이터를 생성(insert)할 때 쓰입니다. 내부 동작: data=request.data는 입력 데이터의 유효성을 검증(validation)합니다. 데이터가 유효하면, serializer.save()를 호출해 새로운 Article 모델 인스턴스를 데이터베이스에 저장합니다.
예시: 클라이언트가 아래 데이터를 보냈다고 가정해 봅시다:
{
"title": "New Article",
"content": "This is a new article."
}
data=request.data는 이 데이터를 받아 ArticleSerializer가 이를 Article 모델의 필드로 변환합니다. 그 후, 데이터 검증을 통과하면 새로운 Article 객체가 생성됩니다.
2. ArticleSerializer(article, data=request.data) 첫 번째 인자: article 첫 번째 인자는 이미 존재하는 모델 인스턴스를 받는다. 여기서 article은 데이터베이스에 저장된 Article 객체를 의미해. 이 코드는 데이터 수정(update) 작업을 처리하기 위한 것이며, 주로 PUT 또는 PATCH 요청에서 사용된다. 즉, 첫번째 인스턴스를 data=request.data(즉, 새롭게 받아온 정보가 아닌 이미 존재하는 데이터)가 아닌, 이미 존재하는 정보를 인스턴스로 만들어 받는다는 소리입니다.
그리고 두 번째 인자는 여전히 클라이언트가 보낸 데이터를 나타냅니다. 즉, request.data가 수정에 사용될, 즉, 클라이언트가 보낸 새로운 데이터를 받는다는 소리입니다.
완전히 폼 같지 않나요?
그리고 postman도 잘 작동하는 것을 볼 수 있습니다.
그런데 만약 내가 content만 바꾸고 싶다면? 그럴때는 옵션 하나를 주면 됩니다. 그것은바로 partial=True입니다.