大家好,我們又見面了,今天來看看怎麼在網頁上,直接對資料庫新增資料吧!
Django 有個很方便的功能是,定義好 form 的格式後,它會幫你把欄位給補到 html 去,來看看怎麼做吧~
models.py
,是之前 Day4 從 Django Girls Tutorial 借過來的 Post
modelfrom django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Post(models.Model):
# 這邊我把 author 加了 null=True
author = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
title = models.CharField(max_length=200)
content = models.TextField()
created_date = models.DateTimeField(
default=timezone.now()
)
published_date = models.DateTimeField(
blank=True, null=True
)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
forms.py
定義好 Form 的欄位之後,透過 views.py
把 form 包在 context 傳給 html,就可以在 html 上,呈現出我們想要讓使用者填的欄位,在這邊我只顯示 title 和 content,因為其他的欄位是作者、建立的時間、發布的時間,不是讓使用者自己填的。
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields= {
'title',
'content'
}
views.py
from django.shortcuts import render
from django.http import JsonResponse
from django.http import HttpRequest
from .forms import PostForm
from .models import Post
# 這次我們試著接受 Form 的 POST
# 收到的 form 如果合法,我們就存到 DB 去
def post_create_view(request):
form = PostForm(request.POST or None)
if form.is_valid():
form.save()
context = {
'form': form
}
return render(request, 'blog/post_create.html', context)
# 上次新增的 view
def hello(request):
return render(request, 'blog/index.html', {
"first_var": "Hello Man",
"second_var": 87.8787,
"third_list": ["歡迎", "你好", "我是 RS", "來比對我阿"]
})
/templates/blog/post_create.html
{% extends 'blog/base.html' %}
{% block content %}
<form method="POST"> {% csrf_token %}
<!-- 從 context 拿出 form -->
{{ form.as_p }}
<input type="submit" value="Submit">
</form>
{% endblock %}
這邊有看到 csrf_token 的 template tag,先不要理它,那是 django 內建預設的 authentication 方法,我們先用用看。
urls.py
from django.contrib import admin
from django.urls import path
from blog.views import hello, post_create_view,
test_json_response_view
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', hello),
# 新增一個 讓使用者發布文章的頁面
path('blog/create/', post_create_view)
]
來到 127.0.0.1/blog/create
,可以看到畫面上已經有醜醜的簡易輸入格 XD,這是 django 的 form 幫我們做的,如果不符合你想要的,也可以參考 官方文件 來變成你想要的樣式。
點下 Submit 後,可以在 開發者工具 的 網路,看到我們從前端傳出去的 request,收到 200 OK
的回應,表示應該是沒問題才對。
有耶,hen 棒~
再進去到裡面,看看是不是真的剛剛新增的那一筆
既然你誠心誠意的發問了,那我就大發慈悲的告訴你,有的 ! 那麼就是今天的主題:開 API,因為有些情況是希望除了透過網頁的方式傳資料,有可能是 mobile app 要傳資料到我們後端資料庫,這時候就需要一支 API 來溝通。
POST
塞入 JSON 資料,來新增一筆發文呢?我的最初想法是,在 Android 端,可以用 Retrofit
輕鬆丟出一個含有 JSON 資料的 POST request,我就想試試看後端要怎麼做,才能吃下含 JSON 的 POST request,我們來看看怎麼做。
根據 TDD(Test Driven Development),我們先定義出最後要測試的 request 和 response 長什麼樣子。
request 長這樣:
method = POST
url = 127.0.0.1:8000/test
body = {
"title": "Add article by postman POST with json body",
"content": "yohohoho."
}
希望收到的 response 長這樣:
{
"status": "ok, I got you."
}
整體概念是,urls.py
會收到 request,再根據我們自定義的 url 規則,將 request 轉發到 views.py
裡的 test_json_response_view(request)
,然後再將 request 裡的 body 解析為 JSON 格式,並轉成 Post
物件再透過 django model 存到 DB 裡,再回個 response 回去,that's it !
views.py
from django.shortcuts import render
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.core.handlers.wsgi import WSGIRequest
import json
from .forms import PostForm
from .models import Post
# 讓這支 API 免 csrf authentication
# (之後我們再來深入!)
@csrf_exempt
def test_json_response_view(request: WSGIRequest):
print('-----------------------------------')
if request.method == 'GET':
print("get GET request: ", request)
return JsonResponse({'first': 'content', 'second': 'test'})
elif request.method == 'POST':
# get json data
data = json.loads(request.body)
print("get POST request data: ", data)
# save to db
Post.objects.create(title=data['title'], content=data['content'])
# check posts in db
print('all posts in db: ', Post.objects.all())
# send response to client
return JsonResponse({'status': 'ok, I got you.'})
else:
print("get unknown request: ", request)
return JsonResponse({'status': 'no, I don\'t know it.'})
# ----------- 以下在這個範例用不到 -----------
# Form 的 request
def post_create_view(request):
form = PostForm(request.POST or None)
print("form valid: ", form.is_valid, ", request: ", request)
if form.is_valid():
form.save()
context = {
'form': form
}
return render(request, 'blog/post_create.html', context)
# template tag testing
def hello(request):
return render(request, 'blog/index.html', {
"first_var": "Hello Man",
"second_var": 87.8787,
"third_list": ["歡迎", "你好", "我是 RS", "來比對我阿"]
})
urls.py
from django.contrib import admin
from django.urls import path
from blog.views import hello, post_create_view, test_json_response_view
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', hello),
path('blog/create/', post_create_view),
# 轉發 request 到 views.py 的 JSON API
path('test/', test_json_response_view)
]
$ python manage.py runserver
給沒用過 postman 的人們的簡易導覽,思考順序是從紅色(request method and url) → 橘色(put json data in body) → 黃色(get response)
如果是 GET 的話,也可以用瀏覽器訪問 127.0.0.1:8000/test/
,就回一個含有 JSON 的 response,這樣就達到一支 API 可以同時符合 GET 和 POST 兩種。
恩... 其實我犯了一個錯誤,我在 Day5 和 Day6 提到說要開 API,原本以為開個 API 應該可以用很簡單的方式做,才知道我把這個餅畫得太大,就不知道該怎麼寫這一篇,所以你可以觀察到,恩,我漏了一天,這件事對你們來說雖然沒什麼,但是對我來講是沒有達到對自己的承諾,昨天隨著時間越拖越晚,就漸漸進入到一個低潮的狀態,謝謝我的女朋友給我打氣,今天才能再復活。
老實說,學會了之後再回去看都會覺得不難,但是我在走這段路的時候,真的是覺得超級難懂,為什麼還有 csrf token 問題、在 postman 明明加上 csrf token 卻還是沒辦法 POST 成功,我沒想到會這麼早碰到 authentication 的問題 QQ,還傻傻地想要在這篇一起把它給解決了,年輕人終究是年輕人。
感謝看到文章最後的你們,還有感謝我的女朋友。
我是 RS,這是我的 不做怎麼知道系列 文章,我們 明天見。
喜歡我的文章嗎? 趕快來看看我都發了什麼文章吧:我的文章目錄
歡迎閱讀我的上一篇: [不做怎麼知道系列之Android開發者的30天後端養成故事 Day6] - 運用框架的槓桿 #前後端怎麼結合 #框架的威力 #Django Template Engine
歡迎閱讀我的下一篇: [不做怎麼知道系列之Android開發者的30天後端養成故事 Day8] - 換個皮 #怎麼套用前端樣版? #DjangoFileManagment #StaticFiles