全棧“食”代:用 Django + Nuxt 實現美食分享網站(上)
Django 作為 Python 社群最受歡迎的 Web 框架之一,憑藉其高度抽象的元件和強大方便的腳手架,將快速且流暢的開發體驗演繹到了極致。而 Nuxt 作為從 Vue.js 進化而來的前端框架,能夠輕鬆勝任複雜的 SPA(單頁應用)開發。兩者相遇,能夠擦出怎樣的火花?這篇教程將用 Django + Nuxt 實現帶有完整的增刪改查(CRUD)功能的全棧應用。最後鄭重警告:不要在深夜閱讀此教程!!!
本文所涉及的原始碼都放在了 上,如果您覺得我們寫得還不錯,希望您能給❤️這篇文章點贊+Github倉庫加星❤️哦~ 本文程式碼改編自 。
專案初始化
在這一系列教程中,我們將會實現一個全棧美食分享網站,後端用 Django 實現,前端則是 Nuxt 框架,下面是最終完成後的專案效果:
預備知識
本教程假定你已經知道了
- 基本的 Python 3 語言知識,包括使用 pip 安裝包
- Django 框架的基礎概念(MTV 架構),可參考這篇進行學習
- Vue 的基礎概念,以及用 npm 工具鏈的使用,可參考
- 前後端分離的基本概念,包括前端如何透過發起 HTTP(S) 請求從後端獲取資料
學習目標
學完這篇教程後,你將:
- 瞭解用 pipenv 工具管理 Python 依賴
- 學會用 Django REST Framework 快速開發 REST API
- 學會用 Nuxt 框架快速開發 SPA(單頁應用),能夠從後端獲取資料並渲染
用 pipenv 初始化 Python 環境
首先建立專案目錄,並進入:
$ mkdir recipes_app && cd recipes_app
在這個專案中,我們用 來管理 Python 專案的環境依賴。Pipenv 是 Python 社群偶像級大師 Kenneth Reitz 牽頭開發的開發流程最佳化工具,立志集所有專案管理工具(Node 的 npm、Ruby 的 bundler、PHP 的 composer 等等)的優勢為一體。我們透過下面的命令安裝 pipenv,並建立專案的依賴環境:
$ pip install pipenv
$ pipenv shell
如果看到命令提示符前面出現 (recipes_app-nV3wuGJ1)
的提示(後面那串隨機字串可能不一樣),就表明我們已經成功地建立了專案獨有的虛擬環境!我們接著安裝 Django “三件套”:
- Django: Django 框架本身,提供了豐富且強大的伺服器開發元件;
- DRF (Django Rest Framework):Django 框架的超級搭檔,大大方便了 REST API 的開發;
- Django CORS Headers:用於實現跨域資源請求(CORS)的 Django 中介軟體(如果你不瞭解 CORS,可以參考阮一峰的)。
安裝命令如下:
(recipes_app-nV3wuGJ1) $ pipenv install django django-rest-framework django-cors-headers
這時 pipenv 便產生了 Pipfile 檔案,它的作用就類似 Node 專案中的 package.json 檔案:
[[source]]
url = ""
verify_ssl = true
name = "pypi"
[packages]
django = "*"
django-rest-framework = "*"
django-cors-headers = "*"
[dev-packages]
[requires]
python_version = "3.6"
然後用 Django 腳手架建立伺服器專案 api
的基本結構,並進入到 api
建立一個子應用 core
:
(recipes_app-nV3wuGJ1) $ django-admin startproject api
(recipes_app-nV3wuGJ1) $ cd api
(recipes_app-nV3wuGJ1) $ python manage.py startapp core
接著進行資料庫遷移,並建立用於登入後臺管理的超級使用者:
(recipes_app-nV3wuGJ1) $ python manage.py migrate
(recipes_app-nV3wuGJ1) $ python manage.py createsuperuser
按照問題輸入資訊即可。要記住使用者名稱和密碼哦!然後執行開發伺服器:
(recipes_app-nV3wuGJ1) $ python manage.py runserver
訪問 ,可以看到後臺管理的登入頁面。輸入剛才建立的超級使用者的使用者名稱和密碼,就進入了後臺管理系統,如下所示:
熟悉的介面,但是——沒什麼東西,而且全是英文!別擔心,後面我們會一個個搞定。
用 Django 實現 REST API
接下來我們將實現本專案所需要用的所有 API。對,你沒有聽錯,我們會在這一步實現所有後端介面,大概只 10 分鐘左右可以敲完!這就是 Django 的宣言:
The web framework for perfectionists with deadlines.
“為趕時間的完美主義者而生!”
全域性配置
首先,在全域性配置檔案 settings.py 中做如下改動:
- 在
INSTALLED_APPS
中新增rest_framework
、corsheaders
和core
,前兩個分別是 Django Rest Framework 和 Django CORS Headers 的應用,最後一個是我們網站的應用; - 在
MIDDLEWARE
中新增corsheaders.middleware.CorsMiddleware
,註冊跨域請求中介軟體(注意一定要放在最前面!); - 設定
CORS_ORIGIN_WHITELIST
,新增跨域請求白名單,這裡我們先寫上,後面開發前端時將用到;
- 設定
LANGUAGE_CODE
為zh-hans
,可以將後臺管理設定為中文,非常方便; - 設定
MEDIA_URL
和MEDIA_ROOT
,用於在開發中提供圖片資原始檔的訪問。
具體程式碼如下:
# ...
INSTALLED_APPS = [
# 預設的 App ...
'rest_framework',
'corsheaders',
'core',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
# 預設的中介軟體 ...
]
CORS_ORIGIN_WHITELIST = (
'',
)
# ...
LANGUAGE_CODE = 'zh-hans'
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
實現 core 應用
接下來就是實現 core
這個 Django 應用。實現一個 Django 應用大致都是按照這樣的流程:
- 定義資料模型(models.py),用於實現和資料庫之間的繫結;
- 定義後臺管理配置(admin.py),用於在後臺管理系統中進行操作;
- 定義序列化器(serializers.py),僅當實現 REST API 時需要,用於提供資料模型的 JSON 序列化(或其他資料交換格式);
- 定義檢視(views.py),用於實現具體的業務邏輯;
- 定義路由(urls.py),用於定義路由規則,將其對映到相應的檢視;
- 將應用路由接入全域性路由檔案(api/urls.py)中。
我們從第一步開始,完成菜譜 Recipe
資料模型如下:
from django.db import models
class Recipe(models.Model):
DIFFICULTY_LEVELS = (
('Easy', '容易'),
('Medium', '中等'),
('Hard', '困難'),
)
name = models.CharField(max_length=120, verbose_name='名稱')
ingredients = models.CharField(max_length=400, verbose_name='食材')
picture = models.FileField(verbose_name='圖片')
difficulty = models.CharField(choices=DIFFICULTY_LEVELS, max_length=10,
verbose_name='製作難度')
prep_time = models.PositiveIntegerField(verbose_name='準備時間')
prep_guide = models.TextField(verbose_name='製作指南')
class Meta:
verbose_name = '食譜'
verbose_name_plural = '食譜'
def __str__(self):
return '{} 的食譜'.format(self.name)
其中,class Meta
定義了 Recipe
的後設資料;__str__
方法定義了一個菜譜物件轉換為字串時應該怎樣顯示。這些設定的作用在開啟後臺管理系統之後就會很清晰了。想要了解更多關於 Django 資料模型的知識,請參考相關。
第二步,為 core
子應用配置相應的後臺管理功能。非常簡單,只需註冊定義好的 Recipe
模型:
from django.contrib import admin
from .models import Recipe
# Register your models here.
admin.site.register(Recipe)
第三步,定義序列化器 serializers.py(腳手架並不會自動建立,需要手動建立)。序列化器是 Django Rest Framework 提供的功能,能夠非常方便地將 Django 資料模型序列化成相應的 JSON 資料格式。在這裡,我們定義一個 RecipeSerializer
,並在 class Meta
中指定對應的資料模型為剛才建立的 Recipe
,並選擇相應的欄位展示:
from rest_framework import serializers
from .models import Recipe
class RecipeSerializer(serializers.ModelSerializer):
class Meta:
model = Recipe
fields = (
'id', 'name', 'ingredients', 'picture',
'difficulty', 'prep_time', 'prep_guide'
)
第四步,實現檢視。這裡我們採用開掛模式,直接呼叫 Django Rest Framework 提供的模型檢視集(ModelViewset
)直接搞定資料模型的增刪改查邏輯:
from rest_framework import viewsets
from .serializers import RecipeSerializer
from .models import Recipe
class RecipeViewSet(viewsets.ModelViewSet):
serializer_class = RecipeSerializer
queryset = Recipe.objects.all()
只需指定 serializer_class
(序列器類)和 queryset
(模型查詢集),就自動定義好了模型的新增、刪除、查詢和修改!雖然檢視集非常強大,但是如果要實現更加靈活的業務邏輯,那麼還是要為每個介面定義單獨的檢視類才行。
第五步,實現路由。由於我們上一步使用了檢視集,因此只需先呼叫 DefaultRouter
自動生成相關的路由,然後加入記錄路由對映的列表 urlpatterns
中:
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import RecipeViewSet
router = DefaultRouter()
router.register(r'recipes', RecipeViewSet)
urlpatterns = [
path('', include(router.urls)),
]
router
為我們自動生成以下路由:
-
/recipes/
:建立食譜(POST 方法)或讀取食譜列表(GET方法); -
/recipes/{id}
:獲取單個食譜(GET)、更新單個食譜(PUT)或刪除食譜(DELETE)。
注意
在 Django 路由定義中不包括 HTTP 方法,具體的 HTTP 方法可以在檢視中讀取並判斷。
最後一步,我們將 core
子應用中的路由接入全域性路由:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('core.urls')),
]
沒錯,關於食譜的增刪改查的 API 我們全都實現了!不信?先執行開發伺服器:
(recipes_app-nV3wuGJ1) $ python manage.py runserver
由於 Django REST Framework 為我們提供了測試 API 的 Web 介面,因此這裡就不用 Postman 等工具進行測試了。用瀏覽器訪問 ,就進入瞭如下所示的 API 測試頁面:
這個頁面的下方還有新增資料(發起 POST 請求)的表單,我們填一些資料,然後點選 POST 按鈕:
然後再次訪問食譜列表頁面,就有我們剛剛新增的食譜了!此外,你還可以嘗試訪問單個食譜的詳情頁面(例如 ),並且可以透過 Web 頁面直接修改或刪除哦!
用 Nuxt.js 實現網站首頁
Django 的 MTV 架構固然優秀,但是隨著現在的業務邏輯越來越多地向前端傾斜(也就是現在流行的富前端應用),其中的 T(Template)需要更強大的武器來解決,這裡就是我們的第二位主角 Nuxt。
用腳手架初始化 Nuxt 專案
我們將把所有的前端程式碼放到 client 目錄中,不過無需自己建立,我們呼叫 nuxt 的腳手架來建立前端應用:
$ npx create-nuxt-app client
之後腳手架應用會詢問一系列問題,按下面的截圖進行選擇(當然作者名填自己):
我們對 Nuxt 腳手架生成的目錄結構稍作講解。可以看到 client 目錄下有以下子目錄:
- assets:存放圖片、CSS、JS 等原始資原始檔
- components:存放 Vue 元件
- layouts:存放應用佈局檔案,佈局可在多個頁面中使用
- middleware:存放應用的中介軟體。Nuxt 中的中介軟體指頁面渲染前執行的自定義函式(本教程中不需要)
- pages:應用的檢視和路由。Nuxt 會根據此目錄中的
.vue
檔案自動建立應用的路由 - plugins: 存放 JavaScript 外掛,用於在應用啟動前載入(本教程中不需要)
- static:存放通常不會改變的靜態檔案,並且將直接對映到路由(即可透過
/static/picture.png
訪問) - store:存放 Vuex Store 檔案(本教程中不需要)
本專案所用到的圖片資源請訪問我們的 ,並下載到對應的目錄中。
編寫前端首頁
我們在 client/pages 中建立 index.vue 檔案,並在其中實現我們的前端首頁:
<template>
<header>
<div class="text-box">
<h1>吃貨天堂 </h1>
<p class="mt-3">製作我們喜愛的美食 ️</p>
<nuxt-link class="btn btn-outline btn-large btn-info" to="/recipes">
檢視食譜
<span class="ml-2">→</span>
</nuxt-link>
</div>
</header>
</template>
<script>
export default {
head() {
return {
title: "首頁"
};
}
};
</script>
<style>
header {
min-height: 100vh;
background-image: linear-gradient(
to right,
rgba(0, 0, 0, 0.9),
rgba(12, 5, 5, 0.4)
),
url("/images/banner.jpg");
background-position: center;
background-size: cover;
position: relative;
}
.text-box {
position: absolute;
top: 50%;
left: 10%;
transform: translateY(-50%);
color: #fff;
}
.text-box h1 {
font-family: cursive;
font-size: 5rem;
}
.text-box p {
font-size: 2rem;
font-weight: lighter;
}
</style>
模板(Template)+ 指令碼(Script)+ 樣式(Style),經典的 Vue.js 元件。
我們剛剛建立了 pages 目錄下的 index.vue 檔案,這意味著當訪問根路由 /
時,這個檔案將被訪問到。透過 npm run dev
執行我們的前端頁面(記得在 client 子目錄下執行!),可以看到:
真是讓人食慾大開!
資料展示:實現食譜列表
接下來我們將演示如何展示資料,並實現食譜列表頁面。
實現 RecipeCard 元件
首先,實現將會在多個頁面中反覆使用的食譜卡片元件 RecipeCard
如下:
<template>
<div class="card recipe-card">
<img :src="recipe.picture" class="card-img-top" />
<div class="card-body">
<h5 class="card-title">{{ recipe.name }}</h5>
<p class="card-text">
<strong>成分:</strong>
{{ recipe.ingredients }}
</p>
<div class="action-buttons">
<nuxt-link :to="`/recipes/${recipe.id}/`" class="btn btn-sm btn-success">檢視</nuxt-link>
<nuxt-link :to="`/recipes/${recipe.id}/edit/`" class="btn btn-sm btn-primary">編輯</nuxt-link>
<button @click="onDelete(recipe.id)" class="btn btn-sm btn-danger">刪除</button>
</div>
</div>
</div>
</template>
<script>
export default {
props: ["recipe", "onDelete"]
};
</script>
<style>
.card-img-top {
height: 12rem;
width: 100%;
}
.recipe-card {
border: none;
box-shadow: 0 1rem 1.5rem rgba(0, 0, 0, 0.6);
}
</style>
在這個元件中,我們定義了兩個 props
,分別是 recipe
(代表食譜物件)和 onDelete
(刪除時的回撥函式),並在模板中使用這兩個成員。
瞭解 Nuxt 的路由功能
在實現第二個頁面之前,我們有必要先了解一下 Nuxt 的路由功能——透過 pages 目錄下的文件結構,就可以自動生成 vue-router 的路由器配置!
例如我們這樣安排 pages 下面的目錄結構????:
pages
├── README.md
├── index.vue
└── recipes
├── _id
│ ├── edit.vue
│ └── index.vue
├── add.vue
└── index.vue
_id
目錄(或者其他以單下劃線開頭的目錄或 .vue 檔案)被稱作是動態路由(Dynamic Routing),可以接受引數作為 URL 的一部分。上面的 pages 目錄自動生成下面的 router
:
router: {
routes: [
{
name: 'index',
path: '/',
component: 'pages/index.vue'
},
{
name: 'recipes',
path: '/recipes',
component: 'pages/recipes/index.vue'
},
{
name: 'recipes-add',
path: '/recipes/add',
component: 'pages/recipes/add.vue'
},
{
name: 'recipes-id',
path: '/recipes/:id?',
component: 'pages/recipes/_id/index.vue'
},
{
name: 'recipes-id-edit',
path: '/recipes/:id?/edit',
component: 'pages/recipes/_id/edit.vue'
}
]
}
提示
如果想要更深入地瞭解 Nuxt 的路由功能,請參考。
實現食譜列表頁面
建立食譜列表頁面 pages/recipes/index.vue(先使用假資料填充),程式碼如下:
<template>
<main class="container mt-5">
<div class="row">
<div class="col-12 text-right mb-4">
<div class="d-flex justify-content-between">
<h3>吃貨天堂</h3>
<nuxt-link to="/recipes/add" class="btn btn-info">新增食譜</nuxt-link>
</div>
</div>
<template v-for="recipe in recipes">
<div :key="recipe.id" class="col-lg-3 col-md-4 col-sm-6 mb-4">
<recipe-card :onDelete="deleteRecipe" :recipe="recipe"></recipe-card>
</div>
</template>
</div>
</main>
</template>
<script>
import RecipeCard from "~/components/RecipeCard.vue";
const sampleData = [
{
id: 1,
name: "通心粉",
picture: "/images/food-1.jpeg",
ingredients: "牛肉, 豬肉, 羊肉",
difficulty: "easy",
prep_time: 15,
prep_guide:
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Omnis, porro. Dignissimos ducimus ratione totam fugit officiis blanditiis exercitationem, nisi vero architecto quibusdam impedit, earum "
},
{
id: 2,
name: "羊肉串",
picture: "/images/food-2.jpeg",
ingredients: "牛肉, 豬肉, 羊肉",
difficulty: "easy",
prep_time: 15,
prep_guide:
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Omnis, porro. Dignissimos ducimus ratione totam fugit officiis blanditiis exercitationem, nisi vero architecto quibusdam impedit, earum "
},
{
id: 3,
name: "炒飯",
picture: "/images/banner.jpg",
ingredients: "牛肉, 豬肉, 羊肉",
difficulty: "easy",
prep_time: 15,
prep_guide:
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Omnis, porro. Dignissimos ducimus ratione totam fugit officiis blanditiis exercitationem, nisi vero architecto quibusdam impedit, earum "
}
];
export default {
head() {
return {
title: "食譜列表"
};
},
components: {
RecipeCard
},
asyncData(context) {
let data = sampleData;
return {
recipes: data
};
},
data() {
return {
recipes: []
};
},
methods: {
deleteRecipe(recipe_id) {
console.log(deleted`${recipe.id}`);
}
}
};
</script>
<style scoped>
</style>
開啟前端網站,可以看到我們剛才實現的食譜列表頁面:
到這兒,我們分別實現了這個全棧食譜網站的前端和後端應用,這篇教程的第一部分也就結束了。在接下來的教程中,我們將實現前後端之間的通訊,並進一步實現食譜的詳情及新增頁面,不見不散!
想要學習更多精彩的實戰技術教程?來逛逛吧。
本文所涉及的原始碼都放在了 上,如果您覺得我們寫得還不錯,希望您能給❤️這篇文章點個推薦+Github倉庫加星❤️哦~ 本文程式碼改編自 。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1343/viewspace-2824880/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 美食食譜
- django 網站實現簡單分頁Django網站
- 基於JavaScript的現代Web應用全棧開發:MEANJavaScriptWeb全棧
- 美食食材社群電商產品分析
- 基於TP5開發的菜譜美食分享網站網站
- 關於美食網站的想法!網站
- Python全棧Web(Django框架、模板)Python全棧WebDjango框架
- 全棧測試實戰:用Jest測試Vue+Koa全棧應用全棧Vue
- Django + DebugToolbar構建全棧WEB開發Django全棧Web
- 怎麼用python django做網站?PythonDjango網站
- 2019全網最實用的ppt素材網站!網站
- 用佇列實現棧佇列
- 用棧實現佇列佇列
- 從零到部署:用 Vue 和 Express 實現迷你全棧電商應用(七)VueExpress全棧
- 從零到部署:用 Vue 和 Express 實現迷你全棧電商應用(五)VueExpress全棧
- 從零到部署:用 Vue 和 Express 實現迷你全棧電商應用(一)VueExpress全棧
- 從零到部署:用 Vue 和 Express 實現迷你全棧電商應用(三)VueExpress全棧
- 從零到部署:用 Vue 和 Express 實現迷你全棧電商應用(四)VueExpress全棧
- 夠味美食網-基於菜譜的社交網站網站
- HelloDjango 啟動!免費帶你學Django全棧!Django全棧
- Python全棧Web(Django框架、模型中的CRUD)Python全棧WebDjango框架模型
- 分享一些免費實用的線上網站網站
- 現代化網站品牌和風格網站
- Python全棧工程師之從網頁搭建入門到Flask全棧專案實戰(6) - Flask表單的實現Python全棧工程師網頁Flask
- 棧的應用和實現
- 解構現代資料棧
- 走在JS上的全棧之路(一)JS全棧
- 9. 題目:對佇列實現棧&用棧實現佇列佇列
- 現代資料棧是如何走向實時化的?
- 建wap網站。實現遙控應用網站
- 用rsync實現網站映象和備份網站
- 全網開發網站搭建教程篇之Python 用函式實現模組化程式設計網站Python函式程式設計
- django 開發網站-建立 django 專案Django網站
- 現代化SharePoint經典網站網站
- Django與前端框架協作開發實戰:高效構建現代Web應用Django前端框架Web
- Day 10| 232.用棧實現佇列 、 225. 用佇列實現棧佇列
- 實現基於Nuxt.js的SSR應用UXJS
- 使用棧實現表示式求值,運用棧計算