프로젝트 생성
$ django-admin startproject mypjt
app 생성
$ cd mypjt
$ python manage.py startapp movies
가상환경 설정
$ python -m venv venv
$ source venv/Scripts/activate
$ pip install -r requirements.txt
settings.py
INSTALLED_APPS = [
'rest_framework',
'movies',
LANGUAGE_CODE = 'ko-kr'
TIME_ZONE = 'Asia/Seoul'
movies- models.py
from django.db import models
class Actor(models.Model):
name = models.CharField(max_length=100)
class Movie(models.Model):
title = models.CharField(max_length=100)
overview = models.TextField()
release_date = models.DateTimeField()
poster_path = models.TextField()
actors = models.ManyToManyField(Actor)
class Review(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
movie = models.ForeignKey(Movie, on_delete=models.CASCADE)
- Actor
id, name 만 필드로 하기 위해 name만 가지는 모델 생성
- Movie
actors를 가지고 있다.
movie - actors 는 M:N 관계로 ManyToManyField이다.
- Review
movie를 참조하고 있다. movie와 review는 1:N 관계로 FK를 사용한다. on_delete 요소도 추가했다.
참조 필드는 참조 모델의 id 값을 가져오게 된다.
movie로 Movie모델을 참조하면서, 테이블에 movie_id필드를 가져오게 된다.
모델 필드 만들떄 movie_id로 해버리면 movie_id_id로 되기 때문에 주의하자.
movies - admin.py
from django.contrib import admin
from .models import Movie, Review, Actor
admin.site.register(Actor)
admin.site.register(Movie)
admin.site.register(Review)
load data
fixtures 파일 위치 : mypjt - movies - fixtures - movies - 파일(movies.json / reviews.json / actors.json )
$ python manage.py makemigrations
$ python manage.py migrate
$ python manage.py loaddata movies/actors.json movies/movies.json movies/reviews.json
mypjt - urls.py
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/', include('movies.urls'))
]
actor_list
movies - urls.py
from django.urls import path
from . import views
urlpatterns = [
path('actors/',views.actor_list)
]
movies - serializers.py
from rest_framework import serializers
from .models import Actor, Movie, Review
class ActorSerializer(serializers.ModelSerializer):
class Meta:
model = Actor
fields='__all__'
.models가 아니고, mypjt.movies.models 로 import 하면 오류 생긴다.
actor 조회를 위한 serializer를 만들었다.
movies- models.py
from rest_framework.response import Response
from rest_framework.decorators import api_view
from .models import Actor
from .serializers import MovieSerializer, ReviewSerializer, ActorSerializer
from django.shortcuts import get_list_or_404, get_object_or_404
@api_view(['GET'])
def actor_list(request):
actors = get_list_or_404(Actor)
serializer = ActorSerializer(actors, many = True)
return Response(serializer.data)
전체 actor목록 조회 끝!
actors를 가져올 때
actors = Actor.objects.all()
으로도 할 수 있지만, 요소가 없을 때 에러가 발생한다.
get_list_or_404로 가져오면, 요소가 없을 때 에러 대신에 404를 보여준다.
actor_detail
actor 정보 디테일
movies-urls.py
from django.urls import path
from . import views
urlpatterns = [
path('actors/',views.actor_list),
path('actors/<int:actor_id>', views.actor_detail),
url 추가했다.
class MovieTitleSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
fields=('title',)
actor 정보에 movie의 title이 나와야한다.
먼저 Movie의 title만 나오는 serializer를 만들었다.
class ActorDetailSerializer(serializers.ModelSerializer):
movie_set = MovieTitleSerializer(many=True)
class Meta:
model = Actor
fields='__all__'
위에서 만든 MovieTitleSerializer을 이용하는 ActorDetailSerializer 만들었다.
역참조 해야 하기 때문에 movie_set을 오버 라이딩 한다.
actor 기준에서 여러 movie가 나올 수 있기 때문에 (M:N) many = True를 지정한다.
movies - views.py
@api_view(['GET'])
def actor_detail(request, actor_id):
actor = get_object_or_404(Actor, pk=actor_id)
serializer = ActorDetailSerializer(actor)
return Response(serializer.data)
movie_list
movie 전체 조회
movies- serializers.py
class MovieSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
fields=('title','overview',)
movies-urls.py
path('movies/<int:movie_id>/', views.movie_detail),
movies - views.py
@api_view(['GET'])
def movie_detail(request, movie_id):
movie = get_object_or_404(Movie, pk=movie_id)
serializer = MovieSerializer(movie)
return Response(serializer.data)
movie detail
serializers.py
class ActorSerializer(serializers.ModelSerializer):
class Meta:
model = Actor
fields='__all__'
class ReviewSerializer(serializers.ModelSerializer):
class Meta:
model = Review
fields=('title','content')
class MovieDetailSerializer(serializers.ModelSerializer):
actors = ActorSerializer(many=True)
review_set = ReviewSerializer(many=True)
class Meta:
model = Movie
fields='__all__'
movie - review 에서도 review가 movie를 참조하기 때문에 역참조 매니저 review_set을 오버라이딩 했다.
views.py
@api_view(['GET'])
def movie_detail(request, movie_id):
movie = get_object_or_404(Movie, pk=movie_id)
serializer = MovieDetailSerializer(movie)
return Response(serializer.data)
class ReviewSerializer(serializers.ModelSerializer):
class Meta:
model = Review
fields=('title','content')
@api_view(['GET'])
def review_list(request):
rivews = get_list_or_404(Review)
serializer = ReviewSerializer(rivews, many = True)
return Response(serializer.data)
리뷰 목록 조회
review_detail
class MovieTitleSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
fields=('title',)
class ReviewDetailSerializer(serializers.ModelSerializer):
movie = MovieTitleSerializer(read_only=True)
class Meta:
model = Review
fields='__all__'
Review에는 movie 요소가 있고, 이것을 오버라이딩한다.
영화, 리뷰는 1:N으로 리뷰에는 영화가 하나로 정해져있기 때문에 movie에 many=True를 넣으면 안된다. (기본값 False)
이 serializer로 리뷰 생성도 할 것이기 때문에 read_only를 넣어주었다.
read_only는 movie가 읽기전용으로, 입력을 할 수 없게 하는 것이다. 리뷰를 작성할때 영화는 정해져있기때문이다.
@api_view(['GET'])
def review_detail(request, review_id):
review = get_object_or_404(Review, pk=review_id)
serializer = ReviewDetailSerializer(review)
return Response(serializer.data)
리뷰 디테일 조회
from rest_framework import status
@api_view(['GET','PUT','DELETE'])
def review_detail(request, review_id):
review = get_object_or_404(Review, pk=review_id)
if request.method =="GET":
serializer = ReviewDetailSerializer(review)
return Response(serializer.data)
if request.method=='DELETE':
review.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
elif request.method == 'PUT':
serializer = ReviewDetailSerializer(instance = review, data = request.data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data)
method를 GET, PUT, DELETE로 나누어 조회, 수정, 삭제를 한번에 관리한다.
review_id를 가지고 가져온 인스턴스 review는 동일하게 사용하도록 맨 위에 작성한다.
1. 조회
위에서 작성한 코드를 그대로 if request.method=="GET": 에 넣어준다.
2. 삭제
삭제한 뒤에 204 no content 상태를 리턴해준다.
3. 수정
인스턴스와 입력된 값을 serializer에 넣어주어야 한다.
modelform과 다르게 인스턴스, 데이터 순으로 매개변수를 작성한다.
is_valid()를 해준 뒤에 save()해주어야 한다. (아니면 오류!)
is_valid를 해줄때 raise_exception=True해주면, valid하지 않을때의 상태도 자동적으로 처리한다.
POSTMAN에서 확인가능하다.
create_review
urls.py
path('movies/<int:movie_id>/reviews/',views.create_review)
댓글 생성 url 추가
serializers.py
class MovieTitleSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
fields=('title',)
class ReviewDetailSerializer(serializers.ModelSerializer):
movie = MovieTitleSerializer(read_only=True)
class Meta:
model = Review
fields='__all__'
# read_only_fields = ('movie',)
review를 작성할 때 movie가 read_only 상태여야 한다. read_only 해야 리뷰를 create할 때 그 요소(movie)는 작성해주지 않는 것이기 때문이다.
read_only_fields = ('movies',) 혹은 read_only=True로 지정한다.
@api_view(['POST'])
def create_review(request,movie_id):
movie = get_object_or_404(Movie,pk=movie_id)
serializer = ReviewDetailSerializer(data = request.data)
if serializer.is_valid(raise_exception=True):
serializer.save(movie=movie)
return Response(serializer.data, status =status.HTTP_200_OK)
Movie요소를 가지고 온다.
request.data를 data로 serializer를 만든다.
data = 를 넣어주지 않으면, ()의 가장 앞 파라미터는 인스턴스로 인식하기때문에 오류가 생긴다.
인스턴스가 아니고 data라는 것을 명시해주어야 한다.
수정이라면, serializer = ReviewDetailSerializer(instance = review, data = request.data)으로 만들것이다.
저장하기 전에 is_valid(raise_exception=True)해준다.
'Back-end > Django' 카테고리의 다른 글
Django 배포 - EC2, gunicorn, nginx (0) | 2023.01.18 |
---|---|
REST API (1:N) (0) | 2022.11.15 |
REST API, 1:N, M:N - movie, actor, review (0) | 2022.10.21 |
Fixtures - dumpdata, loaddata (0) | 2022.10.12 |
Django M:N 관계 - User끼리의 follow 구현하기 (0) | 2022.10.12 |