iT邦幫忙

2

[不做怎麼知道系列之Android開發者的30天後端養成故事 Day7] - 資料飛來飛去 #怎麼傳資料? #DjangoForm #JsonRequest

https://ithelp.ithome.com.tw/upload/images/20200210/20124548TlT0wznW8G.png

大家好,我們又見面了,今天來看看怎麼在網頁上,直接對資料庫新增資料吧!

透過 Form 的方式來新增資料

Django 有個很方便的功能是,定義好 form 的格式後,它會幫你把欄位給補到 html 去,來看看怎麼做吧~

models.py,是之前 Day4Django Girls Tutorial 借過來的 Post model

from 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_tokentemplate 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 幫我們做的,如果不符合你想要的,也可以參考 官方文件 來變成你想要的樣式。

https://ithelp.ithome.com.tw/upload/images/20200210/20124548rpctQWyCLv.png

點下 Submit 後

點下 Submit 後,可以在 開發者工具 的 網路,看到我們從前端傳出去的 request,收到 200 OK 的回應,表示應該是沒問題才對。

https://ithelp.ithome.com.tw/upload/images/20200210/2012454824BbZvqzte.png

來到 admin 看看是不是真的有存進去 DB

有耶,hen 棒~

https://ithelp.ithome.com.tw/upload/images/20200210/20124548y3W3NFaln5.png

再進去到裡面,看看是不是真的剛剛新增的那一筆

https://ithelp.ithome.com.tw/upload/images/20200210/201245489m1Rn6nj3v.png

好的,那麼還有沒有別的方式來傳資料呢 ?

既然你誠心誠意的發問了,那我就大發慈悲的告訴你,有的 ! 那麼就是今天的主題:開 API,因為有些情況是希望除了透過網頁的方式傳資料,有可能是 mobile app 要傳資料到我們後端資料庫,這時候就需要一支 API 來溝通。

假設我想要讓前端,透過一個 POST 塞入 JSON 資料,來新增一筆發文呢?

我的最初想法是,在 Android 端,可以用 Retrofit 輕鬆丟出一個含有 JSON 資料的 POST request,我就想試試看後端要怎麼做,才能吃下含 JSON 的 POST request,我們來看看怎麼做。

上範例 !

根據 TDD(Test Driven Development),我們先定義出最後要測試的 request 和 response 長什麼樣子。

利用 Postman 來模擬傳、收 request 的情境

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."
}

先據透,最後在 Postman 的結果長這樣:

https://ithelp.ithome.com.tw/upload/images/20200210/201245486sycQBTC72.png

那要怎麼做呢?

整體概念是,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)
]

Django 跑起來

$ python manage.py runserver

再到 Postman 送出 request ! 所以結果就是前面所看到的這張

給沒用過 postman 的人們的簡易導覽,思考順序是從紅色(request method and url) → 橘色(put json data in body) → 黃色(get response)

https://ithelp.ithome.com.tw/upload/images/20200210/20124548iZG3U75eP8.png

如果是 GET 的話,也可以用瀏覽器訪問 127.0.0.1:8000/test/,就回一個含有 JSON 的 response,這樣就達到一支 API 可以同時符合 GET 和 POST 兩種。

https://ithelp.ithome.com.tw/upload/images/20200210/20124548GSTsEFXNRJ.png

再到 admin 確認是不是有真的存進去 DB

https://ithelp.ithome.com.tw/upload/images/20200210/20124548NGy1CJQeiS.png

單日心得總結

恩... 其實我犯了一個錯誤,我在 Day5Day6 提到說要開 API,原本以為開個 API 應該可以用很簡單的方式做,才知道我把這個餅畫得太大,就不知道該怎麼寫這一篇,所以你可以觀察到,恩,我漏了一天,這件事對你們來說雖然沒什麼,但是對我來講是沒有達到對自己的承諾,昨天隨著時間越拖越晚,就漸漸進入到一個低潮的狀態,謝謝我的女朋友給我打氣,今天才能再復活。

老實說,學會了之後再回去看都會覺得不難,但是我在走這段路的時候,真的是覺得超級難懂,為什麼還有 csrf token 問題、在 postman 明明加上 csrf token 卻還是沒辦法 POST 成功,我沒想到會這麼早碰到 authentication 的問題 QQ,還傻傻地想要在這篇一起把它給解決了,年輕人終究是年輕人。

感謝看到文章最後的你們,還有感謝我的女朋友。

我是 RS,這是我的 不做怎麼知道系列 文章,我們 明天見。



尚未有邦友留言

立即登入留言