Django REST Framework คือ toolkit หรือไลบรารีของ Python ในการสร้าง RESTful APIs ได้อย่างรวดเร็วและสมบูรณ์แบบ ในบทความนี้จะมาอธิบายการสร้าง RESTful APIs และพื้นฐานต่าง ๆ ที่จำเป็นต้องรู้ พร้อมทั้งได้ Web Service/API เป็นของตัวเองด้วยการ deploy ไปที่ PaaS (Platform as a Service) อย่าง Heroku และใน Ep ถัดไปซึ่งเป็นบทความที่ 2 เป็น (Final EP) จะเป็นการใช้งาน Vue.js ร่วมกับ Django REST Framework
Prerequisite
- พื้นฐาน Django framework
ปล.ถ้ายังไม่มีพื้นฐาน Django แนะนำบทความ - พัฒนาเว็บไซต์ด้วย Django เฟรมเวิร์คฉบับสมบูรณ์ ปี 2023
Table of Contents
- REST คืออะไร
- Django vs Django REST Framework
- ทำไมต้อง Django REST Framework
- HTTP Method & Status Code
- Virtual Environment
- ติดตั้ง Django & Django REST Framework
- Django project & app
- Database Model (SQLite)
- Serializers
- Views
- URLs
- Permission & Authentication (pending)
- CORS (Cross Origin Resource Sharing) (pending)
- Deploying to Heroku (pending)
REST คืออะไร ?
REST : REpresentational State Transfer คือสถาปัตยกรรมอย่างหนึ่งของซอฟต์แวร์ที่อยู่บนพื้นฐานของ HTTP Protocol ถึงแม้เราจะมองไม่เห็นแต่ว่ามันมีอยู่จริง (แน่นอนละ เพราะมันเกี่ยวข้องกับซอฟต์แวร์ จะมองเห็นได้ไงเนอะ) โดยจะมี 2 ตัวละครหลักคือ Client และ Server
- Client –> ทำการร้องขอข้อมูล (request) ไปที่เซิร์ฟเวอร์พร้อมด้วย HTTP requests ที่บอกไปที่เซิร์ฟเวอร์ปลายทางว่า จะร้องขออะไร และ action ที่จะเกิดขึ้นเราจะสามารถรู้หรือคาดเดาได้ผ่าน HTTP requests (GET, POST, PUT, DELETE, etc) เช่นถ้า GET ก็แค่ดึงข้อมูลจากเซิร์ฟเวอร์มาแสดงผลเท่านั้นหรอถ้าเป็น POST ก็แสดงว่ามีการสร้าง resource หรือข้อมูลเพิ่มขึ้นมาใหม่เป็นต้น ซึ่งจะมีอธิบายเพิ่มเติมในส่วนของ HTTP Methods ในหัวข้อด้านล่างเกือบท้าย ๆ บทความ
- Server –> ทำการตอบกลับ (Response) กลับมาที่ client โดยที่ข้อมูล (payload) ที่แนบมากับ body นั้นก็จะเป็นรูปแบบต่าง ๆ ไม่ว่าจะเป็น JSON, XML, HTML, etc ซึ่งรูปแบบที่นิยมมากที่สุดคือ JSON
6 คุณลักษณะที่บ่งบอกตัวตนของ RESTful
- Uniform Interface
- Stateless
- Client- Server
- Cacheable
- Layered System
- Code on Demand
ตรงส่วนนี้ขอไม่อธิบายรายละเอียดมาก อาจจะต้องไปหาข้อมูลเพิ่มเติมครับ ถือว่าให้เป็นคีย์เวิร์ดไปสืบค้นต่อไป
บทความแนะนำ/อ่านเพิ่มเติม What is a REST API
Django vs Django REST Framework
เชื่อว่าหลายคนอาจจะยังมีข้อสงสัยที่ว่า Django กับ Django REST Framework นั้นแตกต่างกันอย่างไร ซึ่งขอยกเอา discussion ที่น่าสนใจในเว็บ stackoverflow มาเป็น reference ในครั้งนี้ครับ ดังนั้นจึงขอสรุปให้แบบเนื้อ ๆ
เลือก
- Django –> ถ้าต้องการเว็บไซต์ที่ไม่ต้องมีการสร้าง API ให้แอพอื่นใช้ ต้องการทำเว็บไซต์แบบ standalone ตัวอย่างเว็บก็ stackpython.co นี่ก็คืออีกหนึ่งเว็บที่ไม่ต้องสร้าง REST APIs ให้ยุ่งยาก หรือไม่มีการเปิด API ให้เว็บภายนอกใช้งาน ทำแบบ server side rendered โดยใช้ Bootstrap, HTML, CSS, JQuery ได้เลย
- Django REST Framework –> ถ้าต้องการสร้าง Web API หรือ Web Service ให้เว็บหรือแอพอื่นใช้งาน เช่น Web App ที่ต้องสร้างเป็นเว็บแบบ SPA (Single Page Application) หรือไม่ว่าจะเป็น Mobile App ที่ต้องการสร้าง ios หรือ Android app ก็สามารถใช้ DRF เป็น Back-end สำหรับสร้าง Web API เพื่อให้ Front-end หรือ Client side ดึงข้อมูล (consume/call) ผ่าน API โดยที่ฝั่ง client นั้นไม่จำเป็นที่จะต้องรู้ว่าฝั่ง backend หรือ server นั้นพัฒนาด้วยเทคโนโลยีอะไรบ้าง เช่นใช้ Django, Laravel, Spring, etc ขอแค่ส่ง JSON มาให้ฝั่ง client และมาให้ถูกฟอร์แมตก็ถือว่าเพียงพอแล้ว เพราะว่าถือเป็นมาตรฐานเดียวกันหมด
Note: จริง ๆ แล้วเราสามารถที่จะเขียนและสร้าง API ด้วย Django แบบปกติคือเรียกว่า build from scratch ได้เลย แต่เมื่อมี tool อย่าง REST Framework แล้ว ก็ไม่มีเหตุผลที่จะไป re-invent the wheels หรือสร้างอะไรสักอย่างที่มันดีและมีอยู่แล้วขึ้นมาใหม่นั่นเอง
ทำไมต้อง Django REST Framework
นี่คือเหตุผลบางส่วนว่าทำไมต้องใช้ Django REST Framework
- ง่ายในการเรียนรู้เพราะว่า base on Python ซึ่งก็ขึ้นชื่อในเรื่องของไวยากรณ์ที่อ่านเข้าใจง่าย สละสลวยสวยงามเหมือนภาษาอังกฤษ
- Class Based Views ที่พร้อมด้วยฟังก์ชันสำเร็จรูปต่าง ๆ พร้อมใช้งาน
- สร้าง RESTful APIs ได้อย่างรวดเร็ว
- มี serializers ที่ช่วยในการแปลง Django, Python object ไปเป็น JSON ได้อย่างง่ายดาย
- ฺBrowsable API คือมีหน้า Interface ในการเรียกดู ทดสอบ API ในตัว ทำให้ง่ายและสะดวกขึ้นมาก
- ฺBuilt-in Authentication System ซึ่งแน่นอนว่าได้รับมรดกตกทอดมาจาก Django
- ได้รับความนิยมสูงและมีคอมมิวนิตี้ขนาดใหญ่ มีแหล่งเรียนรู้ รีซอร์สต่าง ๆ ไม่ว่าจะเป็นบทความ คลิป tutorials และแหล่งเรียนรู้อื่น ๆ เพิ่มเติมเยอะ
- มี Documentation ที่ครบครัน เขียนไว้ได้อย่างครอบคลุและละเอียด ทำให้เป็น reference ได้เป็นอย่างดี
- มีบริษัทชื่อดังระดับโลกมากมายใช้งาน เช่น Mozilla, Red Hat, Heroku, etc ดังนั้นมีความน่าเชื่อถือสูง ไม่หยุดพัฒนาง่าย ๆ อย่างแน่นอน
- ฯลฯ อื่น ๆ ถ้ามีเพิ่มเติม รบกวนคอมเมนต์มาที่โพสต์ต้นทางของบทความนี้ในเฟสบุ๊คได้เลยครับ มีแนบไว้ท้ายบทความ
HTTP Method & Status Code
ทั้งสองส่วนนี้เรียกได้ว่ามีบทบาทสำคัญสำหรับการสร้าง RESTful APIs ไปทำความเข้าใจทั้งสองส่วนนี้กันได้เลย
HTTP Methods (GET, POST, PUT, DELETE)
HTTP methods นั้นก็แทบจะเรียกไว้ว่าสื่อความหมายและอธิบายได้ในตัวมันเอง (Self-explanatory) และตรงไปตรงมา ซึ่งทั้ง 4 เมธอดดังต่อไปนี้มีความนิยมและพบเจอได้บ่อยที่สุด
- GET –> เมธอดที่ใช้สำหรับดึงข้อมูลเท่านั้น ไม่ได้ทำให้ resource เกิดการเปลี่ยนแปลง
- POST –> เมธอดที่ใช้สำหรับสร้างข้อมูลขึ้นมาใหม่ เรียกได้ว่าเป็นเมธอดที่ทำให้เกิดเอฟเฟคต่อเซิร์ฟเวอร์
- PUT –> เมธอดที่ใช้สำหรับอัปเดตข้อมูลที่มีอยู่
- DELETE –> เมธอดที่ใช้สำหรับลบข้อมูลออกจาก resource
ซึ่งจริง ๆ แล้ว HTTP Methods ไม่ได้มีเพียงแค่ 4 ตัวนี้ ยังมีตัวอื่น ๆ เช่น HEAD, PATCH, CONNECT, TRACE, OPTIONS ดังนั้นมีลิ้งค์อ้างอิงในท้ายบทความ สามารถไปศึกษาเพิ่มเติมได้เลยครับ
Note: Safe method: GET เพราะว่าไม่ได้ทำให้ข้อมูล (data/resource) ใน server มีการเปลี่ยนแปลง ส่วน POST , PUT , DELETE เป็น unsafe methods เพราะว่าทำให้ข้อมูลใน server เกิดการเปสี่ยนแปลง ถ้าเคยเขียน Django มาก่อน เช่นในหน้า HTML form นั้นถ้ามีการใช้งานเมธอด POST จะต้องใส่แท็กของ CSRF token เข้าไปช่วยป้องกันทุกครั้ง
Status Codes
- 200 - 299 –> status ที่บ่งบอกว่า response สำเร็จ (Successful Responses)
- 300 - 399 –> status ที่บ่งบอกว่ามีการ redirect (Redirect Responses)
- 400 - 499 –> status ที่บ่งบอกว่า response ผิดพลาด (Client Error Responses)
- 500 - 599 –> status ที่บ่งบอกว่า server ผิดพลาด (Server Error Responses)
ซึ่งโดยปกติแล้ว status codes ที่เรามักจะพบเจอบ่อย ๆ มีดังนี้
- 200 OK คือ response สำเร็จเป็น status code ที่พบเจอบ่อยที่สุด เช่นเมื่อมีการเสิร์ชหาหน้าเว็บต่าง ๆ แล้วเราคลิกเข้าดูได้ server ก็จะตอบกลับด้วย status code นี้
- 201 Created คือ response สำเร็จเมื่อ resource ใหม่ถูกสร้างขึ้นมา พบเจอได้บ่อย ๆ เช่นมีการบันทึกข้อมูลผ่านหน้าฟอร์ม และถูก submit ไปที่ server แล้ว server ทำการตอบกลับด้วย status code นี้
- 301 Moved Permanently คือ redirect หรือหน้านี้มีการเปลี่ยน URL ใหม่
- 404 Not Found คือ Page not found (ไม่พบหน้าที่ต้องการ/ร้องขอ) หน้านี้ไม่มีอยู่อีกต่อไปหรือถูกลบไปแล้ว
- 500 คือ Internal Server Error หรือเกิดความผิดพลาดขึ้นที่เครื่อง server
Virtual Environment
ทำการสร้างโฟลเดอร์เพื่อเก็บโปรเจคท์พร้อมทั้งสร้าง virtual environment ให้กับโปรเจคท์ (quick start)
# Create a folder to store our project
mkdir restframework
# ชี้ไปที่โฟลเดอร์ที่สร้างขึ้นมา
cd restframework
# สร้าง virtual environment ชื่อว่า env
python -m venv env
# Activate (เรียกใช้งาน) virtual environment (Windows)
env/Scripts/activate
# Virtual env ถูก activate และพร้อมใช้งาน
(env)
คลิปวิดีโอแนะนำ (Youtube) - Virtual Environment คืออะไร
ติดตั้ง Django & Django REST Framework
ทำการติดตั้ง Django และ Django REST Framework ผ่าน pip
(env) pip install django
(env) pip install djangorestframework
Django project & app
เมื่อทำการติดตั้ง Django และ Django REST Framework เสร็จแล้ว ก็จะเป็นการเริ่มต้นสร้างโปรเจคท์และแอพ
(env) django-admin startproject mysite .
(env) cd mysite
(env) python manage.py startapp api
requirements.txt
(env) pip freeze > requirements.txt
จะมีแพ็คเกจและไลบรารีที่ติดตั้งดังนี้
asgiref==3.3.4
Django==3.2.2
djangorestframework==3.12.4
pytz==2021.1
sqlparse==0.4.1
typing-extensions==3.10.0.0
Project Structure
├── .gitignore
└── restframework
├── db.sqlite3
├── mysite
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── manage.py
├── api
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
└── requirements.txt
ทำการรีจิสเตอร์แอพใน settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'api', # Oup app
'rest_framework' # DRF
]
Database Model - SQLite
เมื่อสร้างโปรเจคท์และแอพเสร็จเรียบร้อย ถัดมาก็จะเป็นการสร้าง model ซึ่งในที่นี้จะหมายถึงการสร้าง class/object เพื่อติดต่อกับกับฐานข้อมูล เป็นอันว่ามักจะเข้าใจกันดีถ้าพูดถึง model นั้น แน่นอนว่าจะมีความเกี่ยวข้องกับ database นั่นเอง โดย database ที่ใช้ในโปรเจคท์นี้ก็จะใช้ SQLite ซึ่งเก็บข้อมูลในรูปแบบของไฟล์ ไม่ต้องทำการคอนฟิกเซิร์ฟเวอร์หรือพอร์ตอะไรต่าง ๆ ให้ยุ่งยาก เรียกได้ว่ามีมาให้กับ Django และพร้อมใช้กันได้เลย
Note: SQLite จะนิยมใช้กับโปรเจคท์เล็ก ๆ หรือไม่ว่าจะเป็นในช่วงของการ development ใน localhost เท่านั้น แต่ถ้าโปรเจคท์ใหญ่ ๆ มีผู้ใชเ้ยอะ ๆ มี concurrency สูง ๆ จะไม่เหมาะ แต่สำหรับคำแนะนำของผู้เขียน ถ้าจะ build โปรเจคท์จริง ๆ ควรใช้ PostgreSQL ทั้งในส่วนของ development และ production นั่นก็คือ keep the same environment as possible
**
ทำการออกแบบและสร้างตารางใน models.py
from django.db import models
class Task(models.Model):
title = models.CharField(max_length=80)
description = models.TextField()
date_created = models.DateTimeField(auto_now_add=True)
complete = models.BooleanField()
class Meta:
ordering = ['-date_created']
db_table = 'task'
def __str__(self):
return self.title
จากนั้นทำการรันคำสั่งเพื่อ migrate database โดยต้อง makemigrations ก่อน
(env) python manage.py makemigrations
จะได้
Migrations for 'task':
api\migrations\0001_initial.py
- Create model Task
จากนั้นทำการ migrate
(env) python manage.py migrate
ตารางทุกตารางจะถูก migrate ไปที่ฐานข้อมูล SQLite เป็นอันเสร็จสิ้นขั้นตอน migrat่ion
Operations to perform:
Apply all migrations: admin, api, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying api.0001_initial... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying auth.0012_alter_user_first_name_max_length... OK
Applying sessions.0001_initial... OK
Serializers
serializer คือกระบวนการแปลง model object ไปเป็น JSON ฟอร์แมตเพื่อส่งไปที่ client หรือจะมีส่วนกลับอีกตัวที่มีชื่อว่า deserializer ที่ทำหน้าที่ตรงกันข้ามคือแปลง JSON ไปเป็น Django object เพื่อให้ Django สามารถอ่านเข้าใจได้
ทำการสร้างไฟล์ใหม่ขึ้นมาชื่อ serializers.py และทำการอิมพอร์ตโมดูล serializers เข้ามาเพื่อเรียกใช้งานคลาส ModelSerializer ซึ่งจะเป็นพระเอกในการ serialize ออปเจคท์นั่นเอง
serializers.py
from rest_framework import serializers
from .models import Task
class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = ('id', 'title', 'description', 'date_created', 'complete')
- TaskSerializer : คลาสที่ถูกสร้างขึ้นมา
- model : โมเดลที่ต้องการ serialize ซึ่งแน่นอนมีแค่โมเดลเดียวในตอนนี้คือ Task ที่ได้สร้างไว้ใน models.py
- fields : ฟีลด์หรือคอลัมน์ในโมเดลที่ต้องการ serialize ซึ่งในที่นี้คือนำมาใช้ทุกฟีลด์
Views
Views คือส่วนของการเขึยน business logic ของ Django เช่นการจัดการกับ request/response ต่าง ๆ ซึ่งปกติแล้วจะมีรูปแบบการเขียนได้ 2 แบบ คือ Function Based (FBV) View และ Class Based View (CBV) ซึ่งในโปรเจคท์นี้จะเขียนแบบที่สอง เพราะว่าจะสามารถเรียกใช้ฟีเจอร์ต่าง ๆ ของ REST Framework ได้เต็มประสิทธิภาพ ซึ่งพระเอกของเราต่อไปนี้ก็คือโมดูล generics ที่ประกอบไปด้วย APIView ต่าง ๆ ให้เราสามารถเลือกใช้งานได้อย่างสะดวกและรวดเร็วมาก ๆ
ไปที่ไฟล์ views.py
from django.shortcuts import render
from .serializers import TaskSerializer
from .models import Task
from rest_framework import generics
class TaskList(generics.ListAPIView):
queryset = Task.objects.all()
serializer_class = TaskSerializer
# return Response(queryset.data)
**
- TaskList : คลาสที่ถูกสร้างขึ้นมา
- ListAPIView : คลาสที่สืบทอดมาจาก generics โมดูลเพื่อให้สามารถเข้าถึง list ของ object ซึ่งในที่นี้ก็คือลิสต์ของ task ต่าง ๆ ที่จะแสดงผลนั่นเอง
- queryset : ตัวแปรที่เก็บข้อมูลที่ query มาจากฐานข้อมูลในตาราง Task นั่นเอง
- serializer_class : กำหนดคลาสที่ได้ serialize ไว้ก่อนหน้านี้ใน serializers.py ซึ่งต้องอิมพอร์ตเข้ามาใช้งานด้วยเช่นกัน นั่นก็คือ TaskSerializer
Note: การเขียนแบบ Class Based View จะขึ้นต้นด้วยคีย์เวิร์ด class และตัวที่เป็น Function Based View จะขึ้นต้นด้วย def
บทความแนะนำ/อ่านเพิ่มเติม CBV vs FBV
URLs / Route / End-points
ต่อมาจะเป็นการกำหนด URLs ให้กับโปรเจคท์ โดยทำอิมพอร์ตโมดูล views จากแอพ api เข้ามาเพื่อเรียกใช้งานคลาสและออปเจคท์ต่าง ๆ ในโมดูล
ไปที่ไฟล์ mysite/urls.py
from django.contrib import admin
from django.urls import path, include
from api import views
urlpatterns = [
path('admin/', admin.site.urls),
path('api/tasks/', views.TaskList.as_view()),
]
- ‘api/tasks/’ URL end-point ของ API ที่กำลังสร้าง
- ทำการเพิ่ม TaskList ที่เข้าถึงได้ผ่าน views. เข้าไปใน path เพื่อแม็ปเข้าไปเป็นอากิวเมนต์ลำดับถัดไปต่อจาก URL end-point เพื่อที่เวลา request มาจาก client จะถ้าเข้ามาเจอฟังก์ชันหรือแอคชั่นใน end-point นี้
RESTful Structure & URL endpoints
Endpoint –> Method –> Action
- /api/tasks/ –> GET –> ดึงข้อมูล tasks มาแสดงผลทุก task
- /api/tasks/
/ –> GET –> ดึงข้อมูลมาแสดงเฉพาะ task นั้น ๆ - /api/tasks/ –> POST –> สร้าง task ขึ้นมาใหม่
- /api/tasks/
/ –> PUT –> อัปเดตข้อมูลใน task นั้น ๆ - /api/tasks/
/ –> DELETE –> ลบ task นั้น ๆ
Browsable API
อีกหนึ่งจุดเด่นของ Django REST Framework คือมีหน้า Interface ที่ใช้สำหรับเรียกดู ทดสอบ API ได้ ซึ่งทำให้สะดวกมาก ๆ ในการพัฒนา ซึ่งเราจะเรียกหน้านี้ว่า Browsable API แปลเป็นไทยก็คือหน้า API ที่สามารถเรียกดูได้นั่นเอง
http://127.0.0.1:8080/api/tasks/

สังเกตเห็นว่าตอนนี้ยังไม่มีข้อมูลหรือ record ใด ๆ ใน URI นี้ ก็เพราะว่ายังไม่ได้มีการเพิ่มหรือบันทึกข้อมูล ๆ เข้ามานั่นเองครับ เลยยังแสดงเป็นลิสต์เปล่า ๆ []
สร้าง Superuser
ทำการสร้าง superuser เพื่อเข้าใช้งานหน้าแอดมินด้วยคำสั่ง createsuperuser
(env) python manage.py createsuperuser
จะขึ้น console ตามนี้ และจัดการกำหนด username และ password ให้เรียบร้อย
Username (leave blank to use 'user'): sonny
Email address: sonny_stackpython@gmail.com
Password:
Password (again):
The password is too similar to the username.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.
Note: password เป็น hidden field จะไม่แสดงให้เห็นในหน้าคอนโซล และ Email เป็น blank field สามารถปล่อยว่างได้
**
เข้าถึงหน้า admin site
ทำการสร้าง superuser เสร็จแล้วก็ทำการรีจิสเตอร์โมเดลใน admin โดยไปที่ไฟล์ admin.py จากนั้นทำการรีจิสเตอร์ตามปกติ
from django.contrib import admin
from .models import Task
# Register our model
admin.site.register(Task)
จากนั้นเข้าหน้าแอดมินและทำการเพิ่มข้อมูลเข้าไปสัก 1 record ดังภาพด้านล่าง

HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
[
{
"id": 1,
"title": "Copy code from Stackoverflow",
"description": "I have no idea, so I decided to copy code from Stackoverflow for my new project",
"date_created": "2021-05-10T10:46:37.932394Z",
"complete": false
}
]
Response Header
- HTTP : Status code ซึ่ง 200 ก็แสดงว่า request สำเร็จ
- Allow : HTTP methods ที่สามารถใช้ได้ใน end-point นี้
- Content-Type : รูปแบบข้อมูล ซึ่งแนบมาใน header เพื่อบอกชนิดของข้อมูล (Contype Type) ใน URL นี้ ซึ่งก็คือ JSON หรือถ้าเป็น MIME type (Media Type) อย่างเช่นเสียง (audio/oog) หรือภาพ (image/png) เป็นต้น ซึ่งจะอยู่ในรูปของ type/format
อ่านเพิ่มเติม Content Type (mozilla.org)
http://127.0.0.1:8080/api/tasks/
http://127.0.0.1:8080/api/tasks/
ซึ่งถ้าสังเกตในคอนโซลจะพบว่ามีการรีเทิร์น status code เป็น 201 ซึ่งหมายถึงมีการสร้าง task ขึ้นมาใหม่สำเร็จ ซึ่งจะอธิบายในส่วนของ HTTP Methods และ Status Code
[10/May/2021 18:01:00] "POST /api/tasks/ HTTP/1.1" 201 9379
**
HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
[
{
"id": 2,
"title": "Read articles on FreeCodeCamp",
"description": "I must read articles on FreeCodeCamp everyday to boost my programming knowledge",
"date_created": "2021-05-10T11:01:00.844176Z",
"complete": true
},
{
"id": 1,
"title": "Copy code from Stackoverflow",
"description": "I have no idea, so I decided to copy code from Stackoverflow for my new project",
"date_created": "2021-05-10T10:46:37.932394Z",
"complete": false
}
]
**
Task Detail / Update / Delete
ต่อมาจะเป็นในส่วนของการเข้าดูรายละเอียดของแต่ละ task ซึ่งนั่นก็คือ Task Detail นั่นเอง ปกติแล้วเราต้องทำการเขียนฟังก์ชันเพื่อกำหนดแอคชั่นของแต่ละเมธอด ว่าจะให้เรียกดู อัปเดต เพิ่มหรือลบ แต่ด้วยความพิเศษของ genericsView ช่วยให้เราสามารถเขียนอยู่บนคลาสเดียวและจัดการให้เสร็จสรรพ
สร้างคลาสใหม่ที่มีชื่อว่า TaskDetail ใน views.py
...
class TaskList(generics.ListCreateAPIView):
queryset = Task.objects.all()
serializer_class = TaskSerializer
# New
class TaskDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Task.objects.all()
serializer_class = TaskSerializer
- Retrieve –> ดึงข้อมูลหรือรายละเอียดของ task นั้น ๆ มาแสดง
- Update –> อัปเดตข้อมูลใน task ที่มีอยู่
- Destroy –> ลบ task
ทำการกำหนด end-point ใหม่ซึ่งจะอ้างอิงด้วย URL พารามิเตอร์ในรูปแบบของ pk คือ /int:pk ต่อท้าย tasks
อัปเดตโค้ดต่อไปนี้ใน mysite/urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('api/tasks/', views.TaskList.as_view()),
path('api/tasks/<int:pk>', views.TaskDetail.as_view()) # New
]
- ‘api/tasks/int:pk’ : สำหรับ end-point นี้เรียกได้ว่าครอบจักรวาลคือเป็นทั้งในส่วน detail, update, delete ใน end-point เดียว ไม่ต้องประกาศแยกออกเป็น 3 URL end-points ให้ซ้ำซ้อนและเปลืองแรง
```python
HTTP 200 OK Allow: GET, HEAD, OPTIONS Content-Type: application/json Vary: Accept
{ “id”: 1, “title”: “Copy code from Stackoverflow”, “description”: “I have no idea, so I decided to copy code from Stackoverflow for my new project”, “date_created”: “2021-05-10T10:46:37.932394Z”, “complete”: false }
##
###
## Permissions & Authentication
ตอนนี้สามารถสร้าง CRUD แอพได้เรียบร้อยแล้ว แต่ว่ายังเหลือระบบ permissions และ authentication เหตุผลก็เพราะว่า
- user ที่ได้ทำการยืนยันตัวตน (authenticated user) เท่านั้นถึงจะสามารถสร้าง task ได้
- user เจ้าของ task นั้น ๆ เพียงเท่านั้นที่สามารถ update หรือ delete ของตัวเองได้
- user ที่ไม่ได้ยืนยันตัวตนสามารถเข้าดู task ได้ แต่จะดูหรืออ่าน (read) ได้เท่านั้น จะไม่สามารถ create, update, delete ได้
### Permissions
เรียกใช้งาน permissions จาก
rest_framework เพื่อทำการกำหนด permission ให้กับ user
จากนั้นทำการกำหนดตัวแปร permisssion_classes โดยกำหนดเป็น permisssions.IsAuthenticated
คือต้องมีการ login เข้ามาเท่านั้นถึงจะสามารถเข้าถึงหน้านี้ได้
```python
from rest_framework import generics, permissions # New
...
class TaskList(generics.ListCreateAPIView):
queryset = Task.objects.all()
serializer_class = TaskSerializer
# Only an authenticated user can access this API endpoint
permission_classes = [permissions.IsAuthenticated]
...
user ที่ไม่ได้ทำการล็อกอิน หรือไม่มี permission จะไม่สามารถเข้าถึงหน้านี้ได้
http://127.0.0.1:8080/api/tasks/
HTTP 403 Forbidden
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
"detail": "Authentication credentials were not provided."
}
ล็อกอินผ่าน Browsable API
นอกจากนี้การล็อกอินผ่าน Browsable API นอกจากสะดวกแล้ว ยังทำให้รู้ได้ว่า user คนไหนที่กำลังใช้งานอยู่ โดยการตั้งค่า login/logout ผ่าน Browsable API ก็ทำได้ง่าย ๆ โดยทำการกำหนดเพิ่มใน URL ของโปรเจคท์
mysite/urls.py
urlpatterns = [
...
path('api-auth/', include('rest_framework.urls')) # New
]
Note: ชื่อ URL end-point จะต้ั้งเป็นชื่อไหนก็ได้ตามต้องการไม่จำเป็นต้องเป็น แต่ใน URL ใน Docs ที่กำหนดมาก็ถือว่า make sense ไม่มีเหตุผลต้องเปลี่ยน

ทำการรีเฟรช จะพบว่ามีข้อความแสดงสถานะการ login หรือไม่ ผ่าน Browsable API

Log in ผ่าน Browsable API
ซึ่งแน่นอนว่าตอนนี้มี user เพียงแค่ 1 นั่นก็คือ sonny และ user คนนี้มี task อยู่ทั้งสิ้น 2 tasks

Tasks ทั้งหมดของ sonny
สร้าง user ขึ้นมาอีก 1 user โดยสามารถสร้างผ่านด้วยวิธีการที่ง่ายที่สุดคือสร้างผ่านหน้าแอดมินได้เลย

เสร็จแล้ว SAVE
****
เพิ่ม user เข้ามาอีก 1 รวมเป็น 2 เพื่อทดสอบ
ตอนนี้มี 2 users คือ sonny และ Sam เรียบร้อยพร้อมทดสอบ จากนั้นให้ทำการล็อกอินผ่าน user ชื่อ Sam และสร้าง task ขึ้นมา 1 task
HTTP 201 Created
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
"id": 3,
"title": "This is a task from Sam",
"description": "A new task created by Sam",
"date_created": "2021-05-12T05:32:56.891877Z",
"complete": false
}
HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
[
{
"id": 3,
"title": "This is a task from Sam",
"description": "A new task created by Sam",
"date_created": "2021-05-12T05:32:56.891877Z",
"complete": false
},
{
"id": 2,
"title": "Read articles on FreeCodeCamp",
"description": "I must read articles on FreeCodeCamp everyday to boost my programming knowledge",
"date_created": "2021-05-10T11:01:00.844176Z",
"complete": true
},
{
"id": 1,
"title": "Copy code from Stackoverflow",
"description": "I have no idea, so I decided to copy code from Stackoverflow for my new project",
"date_created": "2021-05-10T10:46:37.932394Z",
"complete": false
}
]
ทดสอบเข้าดูที่ URL จะสังเกตว่าพอเข้ามาที่ URL end-point นี้แล้วมีสามารถทำได้ทุกอย่างไม่ว่าจะเป็นดึงข้อมูล (GET), เพิ่มข้อมูล (POST), อัปเดตข้อมูล (PUT), ลบข้อมูล (DELETE), etc
http://127.0.0.1:8080/api/tasks/1

Sam สามารถแก้ไข task ของ sonny ได้ ?
จะเห็นว่า Sam สามารถเข้าดูข้อมูลของ sonny และสามารถอัปเดตหรือลบข้อมูลในนี้ได้ ซึ่ง use case จริง ๆ ไม่ควรเป็นแบบนี้ ควรจะเป็น owner ของ task นั้น ๆ ที่มี permission ในการทำแบบนั้นได้เท่านั้น
ถือว่าจบไปแล้วเซสชั่นแรกกับ Django REST Framework - Tutorial ฉบับเต็ม ซึ่งในบทความนี้จริง ๆ แล้วยังไม่จบเพียงเท่านี้ครับ ยังมีอีก 2 - 3 หัวข้อที่จะมาทำการอัปเดตให้เพิ่มเติมคือ Authentication, Deployment (Heroku), etc ติดตามการอัปเดตในครั้งถัดไปได้เลยครับ และจะมีการเพิ่ม Vue.js สำหรับ Front-end ในการเรียกใช้งาน API ที่ได้สร้างไว้ผ่านไลบรารีที่มีชื่อว่า axios
สำหรับเพื่อน ๆ ที่อ่านจบแล้ว ถ้ารู้สึกชอบหรืออยากให้เขียนต่อให้จบไว ๆ หรือมีคำแนะนำตรงส่วนไหน ก็สามารถคอมเมนต์ไปที่โพสต์นี้ เพื่อส่งสารโดยตรงให้ผม ได้ปลุกไฟในตัว แล้วลุยเขียนให้จบสิ้นสนองความต้องการของผู้อ่านกันครับ ไปที่โพสต์บน Facebook
บทความแนะนำ/อ่านเพิ่มเติม Django REST Framework by Wasin
ปล.บทความด้านบนเป็นอีกบทความ Django REST Framework ฉบับภาษาไทยที่เขียนได้ค่อนข้างละเอียดมาก ๆ โดยพี่ Toey Wasin ที่จะสอนในคลาส Live ผ่าน Google Meet ในวันที่ 29 พฤษภาคมนี้ กับคอร์สสร้าง RESTful APIs ด้วย Django REST Framework with React.js สำหรับใครที่สนใจหรือยังไม่ได้ลงทะเบียนรีบด่วนครับ มีโปรโมชั่นอยู่
References
[ redhat.com ] - What is a REST API
[ djangostars.com ] - Why Use the Django REST Framework
[ developer.mozilla.org ] - HTTP Request Methods
[ developer.mozilla.org ] - HTTP Response Status Codes
[ stackoverflow.com ] - Django or Django REST Framework
[ django-rest-framework.org ] - Authentication & Permissions
[ django-rest-framework.org ] - Class-Based Views
