
Python
다음 프로젝트가 오랜만에 장고로 업무 관리 프로그램을 만들어야 하는데 혹시 사용할만한 기능이 있나 해서 백엔드를 위한 Django REST Framework with 파이썬 도서를 구매해서 봄
프론트 입장에서 쓴 책만 보다가 백엔드 기준에서 쓴 책은 무엇이 다를까?
웹 서비스: 프론트엔드(데이터 처리 제외한 기능 구현, 웹구조, 디자인) + 백엔드(데이터 처리)가 일정한 규칙을 가지고 데이터 주고 받음
현금이 필요해서 출금 요청 시 은행원이 알아서 꺼내줌. 시스템이 만들어 놓은 서비스 창구 = API
이름 없는 택배 상자 같은 시스템 데이터라는 자원을 이름 등으로 구분해서 주고받는 것 = REST
REST API: 자원을 이름으로 구분해서 주고 받는 시스템 창고. 프론트(응용프로그램 역활)와 백(시스템 역활)이 데이터 주고 받기 위한 방법 중 하나. 기능(API)를 잘(REST하게) 만들기~
JSON: 데이터 양식
데이터베이스: 데이터들이 모여 있는 큰 창고
테이블: 창고 안에서 데이터 구분해 놓고 정리하기 위한 단위. 표 형식(속성. 값)
레코드: 각 속성에 대한 값이 모여 있는 데이터의 한 단위. tuple. row.
키: 데이터들 간 중복되지 않는 고유한 값 ex> id, phone, id + phone…
쿼리: 데이터베이스에게 물어보는 문장. CRUD(조회, 수정, 생성, 삭제)
간단한 문법(변수 타입 미지정 가능 등)으로 난이도가 비교적 쉽고 활용 가능한 분야가 많음
파이썬 설치: https://www.python.org
테스트 사용: https://colab.research.google.com/
변수: 데이터 담을 수 있는 그릇. 변할 수 있는 데이터 값. 기본 단위.
조건: if / elif / else
user = input("가위/바위/보 중 하나를 내세요. : ") comp = "바위" if user == "가위": print("당신은 졌습니다.") elif user == "바위": print("비겼습니다.") else: print("당신이 이겼습니다!")
반복: for-in / while
for i in range(0, 5): print("Hello world!")
i = 0 while i < 5: print("Hollo world!") i += 1
while 1 == 1: print("Hello world!") break
자료구조: 자료 저장 방식. 여러 데이터를 배치해서 변수에 담을 때 효율성
a = [1, 2, 'a', 'bcd', 123] len(a) # 5 a.append('new data') a # [1, 2, 'a', 'bcd', 123, 'new data'] nums = [5, 6, 3, 2, 4] nums[1:3] # [6, 3] nums.sort() nums # [2, 3, 4, 5, 6]
my_dict = {'Alice':30, 'BoB':60, 35:'fail', 70:'success', 'class':'best'} my_dict['Alice'] # 30 my_dict['John'] = 50 my_dict.keys() # dict_keys(['Alice', 'BoB', 35, 70, 'class', 'John']) my_dict.values() # dict_values([30, 60, 'fail', 'success', 'best', 50]) for key, val in my_dict.items(): print(key, val) Alice 30 BoB 60 35 fail 70 success class best John 50
my_data = (30,50) my_data[0] # 30
함수: 코드를 묶어놓은 단위체. 어떤 값 넣었을 때 결과 값 도출 하는 코드 묶음.
def sum(a, b): result = a + b return result sum(20, 30) # 50
클래스: 객체/인스턴스 만들기 위한 템플릿. 생성자라는 방식으로 객체/인스턴스 생성. 상속 가능.
클래스 기능 = 메소드. 함수 형태로 작성 되어 있음.
class Human(): # 클래스 hp = 100 # 속성 name = '기본 이름' def walk(self): # 메소드 print('I can Walk!') class Wizard(Human): # 부모 클래스 상속 def __init__(self, name): # 생성자(초기값 설정) self.name = name def magic(self): print("Magic!") my_cahr = Wizard('난마법사') print(my_cahr.name) # 난마법사 print(my_cahr.hp) # 100 my_cahr.walk() # I can Walk! my_cahr.magic() # Magic!
Django: 파이썬 기반 웹 풀스택 프레임워크. 자유도가 낮음. MTV패턴(모델M. 템플릿T. 뷰V)
설치 방법: [python/setting] 파이썬 – Django 설치 + 셋팅
# 원하는 폴더에 가상 환경 설치 py -3.8 -m venv vBook2
# pip 최신 버전 python -m pip install --upgrade pip # 장고 pip install django # MySQL DB pip install mysqlclient # 장고 CORS pip install django-cors-headers # 기본 프로젝트 django-admin startproject back .
create database book2; #데이터베이스 생성 use book2; #book2 DB 사용
import os DEBUG = True # 디버깅 켜기 ALLOWED_HOSTS = ["*"] INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'corsheaders', # 장고 CORS 'back' # 만든 앱 추가 ] TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'static')], # 템플릿 폴더 세팅 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] DATABASES = { 'default': { #'ENGINE': 'django.db.backends.sqlite3', #'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 'ENGINE': 'django.db.backends.mysql', 'NAME': 'book2', #mysql 'USER': 'root', #root 'PASSWORD': '0000', #password 'HOST': 'localhost', 'PORT': '3306' } } LANGUAGE_CODE = 'ko' #추가 - 언어 TIME_ZONE = 'Asia/Seoul' #변경 - 서울 STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')] #추가 STATIC_URL = '/static/' #추가
여기까지 진행하면
이렇게 장고를 쓸 수 있게 활성화 됨
python manage.py makemigrations #모델 변경한 내용 파일로 생성 python manage.py migrate #모델을 DB 적용 python manage.py createsuperuser
여기까지 진행 하고 관리자 URL http://127.0.0.1:8000/admin/ 접속해보면 [사용자(들)] 부분에 내가 가입한 계정이 보인다.
python manage.py startapp photo #앱 생성
앱 생성 시 back/settings.py 에 앱 등록해 주어야 함
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'corsheaders', 'back', 'photo' ]
from django.db import models class Photo(models.Model): title = models.CharField(max_length=50) #CharFiled = 문자열(길이제한) author = models.CharField(max_length=50) image = models.CharField(max_length=200) description = models.TextField() #TextFiled = 문자열(길이 제한 없음) price = models.IntegerField() #InterFiled = 정수
필드 종류:
- CharFiled : 문자열 (길이 제한 필요)
- IntergerField : 정수
- TextField : 문자열 (길이 제한 필요 없음)
- DataField : 날짜
- DataTimeField : 날짜 + 시간
- FileField : 이미지 파일
- ForeignKey : 외래 키(관계)
- OneToOneField : 1대1 관계
- ManyToManyField : 다대다 관계
from django.contrib import admin from .models import Photo admin.site.register(Photo) #Admin에 Photo 등록
python manage.py migrate python manage.py createsuperuser
여기까지 진행하면 관리자 페이지에 등록 된 [Photos]를 볼 수 있다.
Django 템플릿 태그: HTML이 파이썬으로부터 데이터를 바로 넘겨받아서 처리할 수 있는 도구 / {} 형태
View: 모델을 통해 데이터 접근해서 템플릿에게 답변 보내줌. 코드의 가장 많은 비중.
URL: 라우팅 역활 + 서버로 해당 주소에 할당된 리소스 요청
from django.urls import path from . import views urlpatterns = [ path('', views.photo_list, name='photo_list'), path('photo/<int:pk>/', views.photo_detail, name='photo_detail'), path('photo/new/', views.photo_post, name='photo_post'), ]
from django.urls import path from . import views urlpatterns = [ path('', views.photo_list, name='photo_list'), path('photo/<int:pk>/', views.photo_detail, name='photo_detail'), path('photo/new/', views.photo_post, name='photo_post'), path('photo/<int:pk>/edit/', views.photo_edit, name='photo_edit'), ]
from django.shortcuts import render, get_object_or_404, redirect from .models import Photo from .forms import PhotoForm def photo_list(request): photos = Photo.objects.all() return render(request, 'photo_list.html', {'photos': photos}) def photo_detail(request, pk): photo = get_object_or_404(Photo, pk=pk) return render(request, 'photo_detail.html', {'photo': photo}) def photo_post(request): if request.method == "POST": form = PhotoForm(request.POST) if form.is_valid(): photo = form.save(commit=False) photo.save() return redirect('photo_detail', pk=photo.pk) else: form = PhotoForm() return render(request, 'photo_post.html', {'form': form}) def photo_edit(request, pk): photo = get_object_or_404(Photo, pk=pk) if request.method == "POST": form = PhotoForm(request.POST, instance=photo) if form.is_valid(): photo = form.save(commit=False) photo.save() return redirect('photo_detail', pk=photo.pk) else: form = PhotoForm(instance=photo) return render(request, 'photo_post.html', {'form' : form})
from django import forms from .models import Photo class PhotoForm(forms.ModelForm): class Meta: model = Photo fields = ( 'title', 'author', 'image', 'description', 'price', )
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no, user-scalable=no" /> <title>Photo App</title> </head> <body> <h1><a href="">사진 목록 페이지</a></h1> <h3><a href="{% url 'photo_post' %}">New Photo</a></h3> <section> {% for photo in photos %} <div> <h2><a href="{% url 'photo_detail' pk=photo.pk %}">{{ photo.title }}</a></h2> <!-- 링크 수정 --> <img src="{{ photo.image }}" alt="{{ photo.title }}" width="100" /> <p>{{ photo.author }}, {{ photo.price }}원</p> </div> {% endfor %} </section> </body> </html>
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no, user-scalable=no" /> <title>Photo App</title> </head> <body> <h1>{{ photo.title }}</h1> <h3><a href="{% url 'photo_edit' pk=photo.pk %}">Edit Photo</a></h3> <section> <div> <img src="{{ photo.image }}" alt="{{ photo.title }}" width="300" /> <p>{{ photo.description }}</p> <p>{{ photo.author }}, {{ photo.price }}원</p> </div> </section> </body> </html>
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no, user-scalable=no" /> <title>Photo App</title> </head> <body> <h1><a href="/">홈으로 돌아가기</a></h1> <section> <h2>New Photo</h2> <form method="POST" enctype="multipart/form-data"> {% csrf_token %} {{ form.as_p }} <button type="submit">완료!</button> </form> </section> </body> </html>
관리자 Photo에 값 넣으면 http://127.0.0.1:8000/ 에 해당 값이 자동 불러진다.
from django.db import models 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) def __str__(self): return self.title
from django.contrib import admin from django.urls import path from . import views urlpatterns = [ path('', views.todo_list, name='todo_list'), path('post/', views.todo_post, name='todo_post'), path('<int:pk>/', views.todo_detail, name='todo_detail'), path('<int:pk>/edit/', views.todo_edit, name='todo_edit'), path('done/', views.done_list, name='done_list'), path('done/<int:pk>/', views.todo_done, name='todo_done'), ]
from django.contrib import admin from .models import Todo admin.site.register(Todo)
from django import forms from .models import Todo class TodoForm(forms.ModelForm): class Meta: model = Todo fields = ('title', 'description', 'important')
from django.shortcuts import render, redirect from .models import Todo from .forms import TodoForm def todo_list(request): todos = Todo.objects.filter(complete=False) return render(request, 'todo_list.html', {'todos': todos}) def todo_detail(request, pk): todo = Todo.objects.get(id=pk) return render(request, 'todo_detail.html', {'todo': todo}) def todo_post(request): if request.method == "POST": form = TodoForm(request.POST) if form.is_valid(): todo = form.save(commit=False) todo.save() return redirect('todo_list') else: form = TodoForm() return render(request, 'todo_post.html', {'form': form}) def todo_edit(request, pk): todo = Todo.objects.get(id=pk) if request.method == "POST": form = TodoForm(request.POST, instance=todo) if form.is_valid(): todo = form.save(commit=False) todo.save() return redirect('todo_list') else: form = TodoForm(instance=todo) return render(request, 'todo_post.html', {'form': form}) def done_list(request): dones = Todo.objects.filter(complete=True) return render(request, 'done_list.html', {'dones': dones}) def todo_done(request, pk): todo = Todo.objects.get(id=pk) todo.complete = True todo.save() return redirect('todo_list')
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no, user-scalable=no" /> <title>Todo App</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.1/font/bootstrap-icons.css"> </head> <body> <div class="container"> <h1 style="padding:30px 0 15px">TODO 목록 앱</h1> <p> <a href="{% url 'todo_post' %}"><i class="bi-plus"></i>Add Todo</a> <a href="{% url 'done_list' %}" class="btn btn-primary" style="float:right">완료한 TODO 목록</a> </p> <ul class="list-group"> {% for todo in todos %} <li class="list-group-item"> <a href="{% url 'todo_detail' pk=todo.pk %}">{{ todo.title }}</a> {% if todo.important %} <span class="badge badge-danger">!</span> {% endif %} <div style="float:right"> <a href="{% url 'todo_done' pk=todo.pk %}" class="btn btn-danger">완료</a> <a href="{% url 'todo_edit' pk=todo.pk %}" class="btn btn-outline-primary">수정하기</a> </div> </li> {% endfor %} </ul> </div> </body> </html>
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no, user-scalable=no" /> <title>Todo App</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.1/font/bootstrap-icons.css"> </head> <body> <div class="container"> <h1 style="padding:30px 0 15px">TODO 상세보기</h1> <div class="container"> <div class="row"> <div class="col-md-12"> <div class="card"> <div class="card-body"> <h5 class="card-title">{{ todo.title }}</h5> <p class="card-text">{{ todo.description }}</p> <a href="{% url 'todo_list' %}" class="btn btn-primary">목록으로</a> </div> </div> </div> </div> </div> </div> </body> </html>
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no, user-scalable=no" /> <title>Todo App</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.1/font/bootstrap-icons.css"> </head> <body> <div class="container"> <h1 style="padding:30px 0 15px">TODO 추가하기</h1> <div class="container"> <div class="row"> <div class="col-md-12"> <div class="card"> <div class="card-body"> <form method="POST"> {% csrf_token %} {{ form.as_p }} <button class="submit" class="brn btn-primary">등록</button> </form> </div> </div> </div> </div> </div> </div> </body> </html>
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no, user-scalable=no" /> <title>Todo App</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.1/font/bootstrap-icons.css"> </head> <body> <div class="container"> <h1 style="padding:30px 0 15px">DONE 목록</h1> <p> <a href="{% url 'todo_list' %}" class="btn btn-primary">홈으로</a> </p> <ul class="list-group"> {% for done in dones %} <li class="list-group-item"> <a href="{% url 'todo_detail' pk=done.pk %}"> {{ done.title }}</a> {% if done.important %} <span class="badge badge-danger">!</span> {% endif %} </li> {% endfor %} </ul> </div> </body> </html>
책에는 views.py 부분에 done_list 함수 코드가 이상해서
https://github.com/TaeBbong/drf_for_backend/blob/master/03_%EC%9E%A5%EA%B3%A0Todo/todo/views.py
이 쪽 깃헙을 보니 코드가 달라서 깃헙 쪽 코드를 참고함
Django REST Framework: Django를 기반으로 REST API 서버를 만들기 위한 라이브러리로 JSON같은 양식으로 다양한 플랫폼의 클라이언트에게 데이터 제공 가능
pip install djangorestframework phthon manage.py startapp example python manage.py migrate python manage.py createsuperuser
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'corsheaders', 'rest_framework', 'example', ]
from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path("example/", include("example.urls")) ]
상태 코드
- HTTP 200 OK : 데이터 GET 요청 정상
- HTTP 201 CREATED: 데이터 POST 요청 정상
- HTTP 206 PARTIAL CONTENT: 데이터 일부 수정하는 PATCH 요청 정상
- HTTP 400 BAD REQUEST: 잘못된 요청으로 클라이언트가 응답 없음
- HTTP 401 UNAUTHORIZED: 인증 필요한데 관련 내용이 인증 요청이 없을 때 나타남
- HTTP 403 FORBIDDEN: 클라이언트가 접근 못하도록 막아 놓은 곳에 요청 왔을 때
- HTTP 404 NOT FOUND: 클라이언트가 요청 보낸 곳이 잘못된 URL일 때 (리소스가 없을 때)
- HTTP 500 INTERNAL SERVER ERROR: 서버 코드가 잘못되었을 때
특징 | Pure Django | Django Rest Framework |
---|---|---|
개발 목적 | 웹 풀스택 개발 | 백엔드 API 서버 개발 |
개발 결과 | 웹 페이지 포함한 웹 서비스 | 여러 클라이언트에서 사용할 수 있는 API 서버 |
응답 형태 | HTML | JSON |
다른 파일 | templates | serializers.py |
시리얼라이저(Serialzer): 직렬화. 모델로부터 뽑은 queryset, 즉 모델 인스턴스를 JSON 타입으로 바꾸는 것
DRF(Django REST Framework) 내에 데이터 저장 시 Django 모델을 통해 저장됨. 모델은 데이터 베이스 테이블을 추상화한 개념으로 ORM을 통해 파이썬 문법으로 데이터를 처리할 수 있음 = 파이썬 객체의 형태로 저장.
API는 이런 데이터를 클라이언트에게 보내주는 역할. 단, 그대로 주면 클라이언트는 모르는 파이썬 데이터로 받게 되어 읽지 못하기에 읽을 수 있는 문자열(JSON 등)으로 변환하여 보내주어야 함 = Serialzer
반대로 클라이언트가 DRF로 데이터 보낼 시 JSON 등 문자열 형태로 보내기에 바로 저장 불가. 파이썬 객체 형태여야 함 = 역직렬화 = 디시리얼라이즈(Deserialize)
from django.urls import path, include from .views import helloAPI, bookAPI, booksAPI, BookAPI, BooksAPI, BooksAPIMixins, BookAPIMixins urlpatterns = [ path("hello/", helloAPI), path("fbv/books/", booksAPI), path("fbv/book/<int:bid>/", bookAPI), path("cbv/books/", BooksAPI.as_view()), path("cbv/book/<int:bid>/", BookAPI.as_view()), path("mixin/books/", BooksAPIMixins.as_view()), path("mixin/book/<int:bid>/", BookAPIMixins.as_view()), ]
from django.apps import AppConfig class ExampleConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'example'
from django.db import models class Book(models.Model): bid = models.IntegerField(primary_key=True) # 책 id title = models.CharField(max_length=50) # 책 제목 author = models.CharField(max_length=50) # 저자 category = models.CharField(max_length=50) # 카테고리 pages = models.IntegerField() # 페이지 수 price = models.IntegerField() # 가격 published_date = models.DateField() # 출판일 description = models.TextField() # 도서설명
from rest_framework import serializers from .models import Book class BookSerializer(serializers.ModelSerializer): class Meta: model = Book fields = ['bid', 'title', 'author', 'category', 'pages', 'price', 'published_date', 'description', ]
시리얼라이저는 파이썬 모델 데이터를 JSON으로 바꿔주는 변환기이기에 모델의 어떤 속성을 JSON에 넣어줄지 선언해줘야 함. 즉 필드 선언해 줘야 함.
기존 BookSerializer(serializers.Serializer) 로 작업하면 def create / def update 전부 다 선언해줘야 하기에 BookSerializer(serializers.ModelSerializer)로 해주면 간단히 해줄 수 있음
ModelSerializer는 모델 내용 기반으로 동작하는 시리얼라이저임
from rest_framework import viewsets, permissions, status, generics, mixins from rest_framework.response import Response from rest_framework.views import APIView from rest_framework.decorators import api_view from rest_framework.generics import get_object_or_404 from .models import Book from .serializers import BookSerializer @api_view(['GET']) def helloAPI(request): return Response("hello world!") class HelloAPI(APIView): def get(self, request, format=None): return Response("hello world") @api_view(['GET', 'POST']) def booksAPI(request): if request.method == 'GET': books = Book.objects.all() serializer = BookSerializer(books, many=True) return Response(serializer.data, status=status.HTTP_200_OK) elif request.method == 'POST': serializer = BookSerializer(data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @api_view(['GET']) def bookAPI(request, bid): book = get_object_or_404(Book, bid=bid) serializer = BookSerializer(book) return Response(serializer.data, status=status.HTTP_200_OK) class BooksAPI(APIView): def get(self, request): books = Book.objects.all() serializer = BookSerializer(books, many=True) return Response(serializer.data, status=status.HTTP_200_OK) def post(self, request): serializer = BookSerializer(data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) class BookAPI(APIView): def get(self, request, bid): book = get_object_or_404(Book, bid=bid) serializer = BookSerializer(book) return Response(serializer.data, status=status.HTTP_200_OK) class BooksAPIMixins(mixins.ListModelMixin, mixins.CreateModelMixin,generics.GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializer def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs) class BookAPIMixins(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializer lookup_field = 'bid' def get(self, request, *args, **kwargs): return self.retrieve(request, *args, **kwargs) def put(self, request, *args, **kwargs): return self.update(request, *args, **kwargs) def delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs) class BooksAPIGenerics(generics.ListCreateAPIView): queryset = Book.objects.all() serializer_class = BookSerializer class BookAPIGenerics(generics.RetrieveUpdateDestroyAPIView): queryset = Book.objects.all() serializer_class = BookSerializer lookup_field = 'bid'
python manage.py migrate python manage.py createsuperuser
……..