本文主要在Ubuntu環境上搭建,為便于研究理解,采用SSH連接在虛擬機里的ubuntu-24.04.2-desktop
系統搭建,當涉及一些文件操作部分便于通過桌面化進行理解,通過Nginx
代理綁定域名,對外發布。
此為從零開始搭建Django博客
系列的第二篇,計劃用一周時間完成一個博客搭建并全程記錄,便于學習和跟著操作。
從零開始搭建Django博客①–正式開始前的準備工作
從零開始搭建Django博客②–Django的服務器內容搭建
框架理解
我們已經建立了一個基本的名為myblog
的Django項目,他的框架如下:
# 項目文件夾結構
├── manage.py # Django 項目的管理文件
└── myblog # 項目實際存放目錄├── asgi.py # 運行在 ASGI 兼容的 Web 服務器上的入口├── __init__.py # 證明該目錄此為Python 包。├── settings.py # Django 項目的配置文件├── urls.py # Django 項目的 URL 聲明,就像你網站的“目錄”。└── wsgi.py # 運行在 WSGI 兼容的Web服務器上的入口
網站構建
在Web應用中,通常有一些業務功能模塊是在不同的項目中都可以復用的,故在開發中通常將工程項目拆分為不同的子功能模塊,各功能模塊間可以保持相對的獨立,在其他工程項目中需要用到某個特定功能模塊時,可以將該模塊代碼整體復制過去,達到復用。因此我們每個WEB應用都是有很多不同的子應用組成,在Django
中,通過APP來創建子應用。
為了實現一個博客,我們需要以下三種功能:
![[Pasted image 20250422111023.png]]
創建APP
在根目錄下通過以下代碼創建三個app子應用
# 創建文章功能
python manage.py startapp article
# 創建用戶功能
python manage.py startapp user
# 創建評論功能
python manage.py startapp comment
此時項目文件夾結構如下,可以發現其中除了我們建立的三個文件夾,又生成了一個db.sqlit3
,這是因為在我們未設置數據庫信息時,Django
在settings.py
里默認連接的是項目目錄里的該數據庫。
![[Pasted image 20250422111427.png]]
連接數據庫
為了下一步的數據可視化,我們首先配置數據庫信息,打開myblog
文件夾目錄下的settings.py
配置數據庫信息:
![[Pasted image 20250422114305.png]]
安裝pymysql
包(用于連接數據庫)和cryptography
包(用于處理密碼)
在settings.py
同目錄下的__init__.py
中導入mysql
包相關功能
import pymysql
pymysql.install_as_MySQLdb()
![[Pasted image 20250422114611.png]]
重新啟動Djangond
服務器,無報錯說明配置無誤。
注冊APP
同樣在 myblog
文件夾目錄下的settings.py
中,找到INSTALLED_APPS配置項,將新創建的3個app添加到項目的app列表,如下
創建模型
在此之前,我們先理解Django
的MVT
模式
- M全拼為Model,負責和數據庫交互,進行數據處理。
- V全拼為View,接收請求,進行業務處理,返回應答。
- T全拼為Template,負責封裝構造要返回的html。
文章模型
數據需求分析
首先分析文章功能需要哪些數據
數據名 | 標題 | 作者 | 創建時間 | 更新時間 | 正文 | 瀏覽量 |
---|---|---|---|---|---|---|
id | title | author | created | updated | body | views |
類型 | 字符串 | 時間 | 時間 | 大量文本 | 數字 | |
備注 | 外鍵約束 |
構建代碼
# 文件:article/modle.py
from django.db import models
# 導入django內建的User模型(后面會提)
from django.contrib.auth.models import User
# timezone 用于處理時間相關事務。
from django.utils import timezone
# 用于反向解析,動態生成鏈接
from django.urls import reverse# 博客文章數據模型
class ArticlePost(models.Model):# 文章標題。models.CharField 為字符串字段,用于保存較短的字符串,比如標題title = models.CharField(max_length=100)# 文章作者。外鍵約束,參數 on_delete 用于指定數據刪除的方式,鏈接到(User),只要作者(User)被刪除,所有該作者的文章數據都會被刪除。author = models.ForeignKey(User, on_delete=models.CASCADE)# 文章創建時間。參數 default=timezone.now 指定其在創建數據時將默認寫入當前的時間created = models.DateTimeField(default=timezone.now)# 文章更新時間。參數 auto_now=True 指定每次數據更新時自動寫入當前時間updated = models.DateTimeField(auto_now=True)# 文章正文。保存大量文本使用 TextFieldbody = models.TextField()# 文章瀏覽量total_views = models.PositiveIntegerField(default=0)# 內部類 class Meta 用于給 model 定義元數據(Django內部類)class Meta:# ordering 指定模型返回的數據的排列順序# '-created' 表明數據應該以倒序排列ordering = ('-created',)# 魔術方法 __str__ 定義當調用對象的 str() 方法時的返回值內容。def __str__(self):# return self.title 將文章標題返回return self.title# 獲取文章地址,按照數據庫id解析為url:/article/iddef get_absolute_url(self):return reverse('article:article_detail', args=[self.id])
評論模型
數據需求分析
首先分析文章功能需要哪些數據
數據名 | 評論文章 | 評論作者 | 評論時間 | 正文 |
---|---|---|---|---|
id | title | author | created | body |
類型 | 字符串 | 時間 | 大量文本 | |
備注 | 外鍵約束 |
構建代碼
from django.db import models
from django.contrib.auth.models import User
from article.models import ArticlePost# 博文的評論
class Comment(models.Model):
# related_name表示可以反向查詢文章和用戶的評論article = models.ForeignKey(ArticlePost,on_delete=models.CASCADE,related_name='comments')user = models.ForeignKey(User,on_delete=models.CASCADE,related_name='comments')body = models.TextField()created = models.DateTimeField(auto_now_add=True)# 按照創建時間排序class Meta:ordering = ('created',)def __str__(self):return self.body[:20]
數據遷移
編寫好了Model后,接下來就需要進行數據遷移。遷移是Django對模型所做的更改傳遞到數據庫中的方式。
注意,每當對數據庫進行了更改(添加、修改、刪除等)操作,都需要進行數據遷移。
Django的遷移代碼是由模型文件自動生成的,它本質上只是個歷史記錄,Django可以用它來進行數據庫的滾動更新,通過這種方式使其能夠和當前的模型匹配。
在命令行中輸入命令讓 Django知道我們自定義模型有一些變更,并根據我們自定義app的模型生成創建數據表的腳本:
python manage.py makemigrations
python manage.py migrate
數據查看
可以通過查看數據庫檢查數據遷移效果
mysql -u 數據庫用戶名 -p
輸入密碼后進入數據庫
# 切換到數據庫
USE 數據庫名;
# 顯示數據表
SHOW TABLES;
可以看到數據庫中已經有了Django
自帶的用戶數據表和我們創建的文章、評論數據表
創建視圖
再看下MVT圖:
我們已經構建起Model與數據庫之間的聯系,下面需要構建模型(Model)與視圖(View)之間的關系。
Django 中視圖的概念是「一類具有相同功能和模板的網頁的集合」。
每一個視圖表現為一個簡單的Python函數,它需要要做的只有兩件事:返回一個包含被請求頁面內容的 HttpResponse
對象,或者拋出一個異常,比如 Http404
。
視圖函數中的request與網頁發來的請求有關,里面包含get或post的內容、用戶瀏覽器、系統等信息。簡單來說,視圖就是實現將用戶的想法通過WEB傳遞到模型。
根據系統設計過程中需要的功能,我們分別在不同的app 中創建對應的View視圖。
文章視圖
對于文章,我們需要有以下功能:
- 文章創建(article_create)
- 實現文章的創建和提交功能。
- 文章內容顯示(article_detail)
- 展示文章的標題和內容詳情,同時在此視圖中取出評論,并展示所有評論列表。
- 文章刪除(article_delete)
- 實現文章刪除功能。
- 文章修改(article_update)
- 實現文章的修改功能。
- 文章排序(article_list)
- 實現默認按發布日期排序。
- 實現可按瀏覽量(熱度)排序。
- 利用Django 的
paginator
組件實現分頁功能。
# 導入Django內部的登錄裝飾器,可以確保只有登錄才能訪問相應視圖。
from django.contrib.auth.decorators import login_required
# 導入http功能
from django.http import HttpResponse
# 導入render功能和redirect功能
from django.shortcuts import render,redirect
# 導入數據模型ArticlePost
from comment.models import Comment
# 從本目錄導入模型
from . import models
# 從本目錄的模型中導入文章功能
from .models import ArticlePost
# 引入內置User模型
from django.contrib.auth.models import User
# 引入內置的分頁模塊
from django.core.paginator import Paginator# 定義文章創建函數
def article_create(request):# 如果用戶提交數據,則把相應數據填入變量中if request.method == 'POST':new_article_title = request.POST.get('title')new_article_body = request.POST.get('body')# 默認第一個用戶作者new_article_author = User.objects.get(id=1)# 生成一個文章對象,即創建模型。models.ArticlePost.objects.create(title=new_article_title, body=new_article_body,author=new_article_author)return redirect("article:article_list")# 如果用戶請求獲取數據,則給用戶提供一個寫文章的界面else:return render(request, 'article/create.html')# 一般都是先請求獲取數據--生成寫文章界面--寫完后再提交數據# 文章詳情
def article_detail(request, id):# 取出相應的文章article = ArticlePost.objects.get(id=id)# 瀏覽量 +1article.total_views += 1# 把新的瀏覽量字段保存數據庫article.save(update_fields=['total_views'])# 取出文章評論comments = Comment.objects.filter(article=id)# 需要傳遞給模板的對象# context = { 'article': article }context = { 'article': article, 'comments': comments }# 載入模板,并返回context對象return render(request, 'article/detail.html', context)# 刪文章
def article_delete(request, id):# 根據 id 獲取需要刪除的文章article = ArticlePost.objects.get(id=id)# 調用.delete()方法刪除文章article.delete()# 完成刪除后返回文章列表return redirect("article:article_list")# 更新文章
# 提醒用戶登錄
@login_required(login_url='/userprofile/login/')
def article_update(request, id):# # 獲取需要修改的具體文章對象article = ArticlePost.objects.get(id=id)# 過濾非作者的用戶if request.user != article.author:return HttpResponse("抱歉,你無權修改這篇文章。")# 判斷用戶是否為 POST 提交表單數據if request.method == "POST":new_article_title = request.POST.get('title')new_article_body = request.POST.get('body')article.title = new_article_titlearticle.body = new_article_bodyarticle.save()# 完成后返回到修改后的文章中。需傳入文章的 id 值return redirect("article:article_detail", id=id)else:# 賦值上下文,將 article 文章對象也傳遞進去,以便提取舊的內容context = {'article': article}return render(request, 'article/update.html', context)def article_list(request):# 根據GET請求中查詢條件# 如果選擇按瀏覽量排序返回數組if request.GET.get('order') == 'total_views':article_list = ArticlePost.objects.all().order_by('-total_views')order = 'total_views'# 否則按照默認排序返回數組else:article_list = ArticlePost.objects.all()order = 'normal'# 分頁,每頁4項paginator = Paginator(article_list, 4)# 如果選擇了頁面page = request.GET.get('page')# 查看該頁的文章articles = paginator.get_page(page)# 返回這些文章模型,并按排序返回context = { 'articles': articles, 'order': order }return render(request, 'article/list.html', context)
評論視圖
在文件comment/views.py文件中創建如下視圖函數,評論比較簡單,暫時只創建一個添加評論的就可以了:
# 導入相關模塊
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
from article.models import ArticlePost
from . import models# 文章評論
@login_required(login_url='/user/login/')
def post_comment(request, article_id):article = get_object_or_404(ArticlePost, id=article_id)if request.method == 'POST':new_comment_body = request.POST.get('body')new_article_user = request.usermodels.Comment.objects.create(article=article, body=new_comment_body,user=new_article_user)return redirect(article)else:return HttpResponse("發表評論僅接受POST請求。")
用戶視圖
之所以沒創建用戶模型是因為
Django
內置了用戶模型,我們在開發一些用戶權限控制不復雜的網站或者系統時,可以直接采用Django自帶的用戶認證模塊功能,通過視圖調用即可。
用戶注冊登錄
用戶注冊、登錄一般都會用到表單,Django
中也內置了一個表單組件,首先我們利用該組件構建表單,在user的app中新建forms.py
# 引入表單類
from django import forms
# 引入 User 模型
from django.contrib.auth.models import User# 登錄表單,繼承了 forms.Form 類
class UserLoginForm(forms.Form):username = forms.CharField()password = forms.CharField()# 注冊用戶表單
class UserRegisterForm(forms.ModelForm):# 復寫 User 的密碼password = forms.CharField()password2 = forms.CharField()class Meta:model = Userfields = ('username', 'email')# 對兩次輸入的密碼是否一致進行檢查def clean_password2(self):data = self.cleaned_dataif data.get('password') == data.get('password2'):return data.get('password')else:raise forms.ValidationError("密碼輸入不一致,請重試。")
以上是一個簡單的登錄、注冊表單實現
視圖建設
用戶視圖需要三個功能,登錄(驗證賬號密碼)、退出、注冊(新建一條用戶數據),代碼如下:
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login,logout
from django.http import HttpResponse
from .forms import UserLoginForm,UserRegisterForm# Create your views here.
def user_login(request):if request.method == 'POST':user_login_form = UserLoginForm(data=request.POST)if user_login_form.is_valid():# .cleaned_data 清洗出合法數據data = user_login_form.cleaned_data# 檢驗賬號、密碼是否正確匹配數據庫中的某個用戶# 如果均匹配則返回這個 user 對象user = authenticate(username=data['username'], password=data['password'])if user:# 將用戶數據保存在 session 中,即實現了登錄動作login(request, user)return redirect("article:article_list")else:return HttpResponse("賬號或密碼輸入有誤。請重新輸入~")else:return HttpResponse("賬號或密碼輸入不合法")elif request.method == 'GET':user_login_form = UserLoginForm()context = { 'form': user_login_form }return render(request, 'userprofile/login.html', context)else:return HttpResponse("請使用GET或POST請求數據")def user_logout(request):logout(request)return redirect("article:article_list")# 用戶注冊
def user_register(request):if request.method == 'POST':user_register_form = UserRegisterForm(data=request.POST)if user_register_form.is_valid():new_user = user_register_form.save(commit=False)# 設置密碼new_user.set_password(user_register_form.cleaned_data['password'])new_user.save()# 保存好數據后立即登錄并返回博客列表頁面login(request, new_user)return redirect("article:article_list")else:return HttpResponse("注冊表單輸入有誤。請重新輸入~")elif request.method == 'GET':user_register_form = UserRegisterForm()context = { 'form': user_register_form }return render(request, 'userprofile/register.html', context)else:return HttpResponse("請使用GET或POST請求數據")
配置路由
定義完視圖后,我們需要通過路由訪問這些視圖:
查找視圖的過程 :
- 1.請求者在瀏覽器地址欄中輸入URL, 請求到網站.
- 2.網站獲取URL信息.
- 3.然后與編寫好的URLconf逐條匹配.
- 4.如果匹配成功則調用對應的視圖.
- 5.如果所有的URLconf都沒有匹配成功.則返回404錯誤.
我們有3個app,Django可以對URL進行分級管理,我們在每個app文件夾內的urls.py分別定義各自app相關的URL,然后在根目錄DjangoBlog的urls.py中通過include指令來包含各個app中的URL。
- 需要兩步完成
URLconf
配置- 1.在
項目
中定義URLconf
(到應用) - 2.在
應用
中定義URLconf
(應用內)
- 1.在
項目中定義路由(根目錄的urls.py)
# 引入用戶管理后臺
from django.contrib import admin
# 記得引入include
from django.urls import path, include
# 需要使用文章視圖顯示文章列表作為首頁
from article import viewsurlpatterns = [path('admin/', admin.site.urls),path('', views.article_list, name='home'),path('article/', include('article.urls', namespace='article')),path('user/', include('user.urls', namespace='user')),path('comment/', include('comment.urls', namespace='comment')),
]
應用中定義路由
文章APP(urls.py)
# 引入path
from django.urls import path
# 引入views.py
from . import views# 正在部署的應用的名稱
app_name = 'article'urlpatterns = [path('', views.article_list),# path函數將url映射到視圖path('article-list/', views.article_list, name='article_list'),# 文章詳情path('article-detail/<int:id>/', views.article_detail, name='article_detail'),# 寫文章path('article-create/', views.article_create, name='article_create'),# 刪除文章path('article-delete/<int:id>/', views.article_delete, name='article_delete'),# 更新文章path('article-update/<int:id>/', views.article_update, name='article_update'),
]
評論APP(urls.py)
# 引入path
from django.urls import path
# 引入views.py
from . import views# 正在部署的應用的名稱
app_name = 'comment'urlpatterns = [# # path函數將url映射到視圖# 發表評論path('post-comment/<int:article_id>/', views.post_comment, name='post_comment'),
]
用戶APP(urls.py)
from django.urls import path
from . import viewsapp_name = 'user'urlpatterns = [# 用戶登錄path('login/', views.user_login, name='login'),# 用戶退出path('logout/', views.user_logout, name='logout'),# 用戶注冊path('register/', views.user_register, name='register'),]
至此,所有后端工作準備完畢,下面我們建一個前端模板用于顯示相關信息:
創建模板
在根目錄新建一個templates
文件夾,用于存放模板文件。
配置主要目錄下的setting.py
中的TEMPLATES,綁定當前創建的templates
文件夾地址。
前期我們已經在視圖中設置了視圖傳入數據的目標地址,以文章視圖為例
我們有如下路由和創建文章功能
當得到GET請求頁面127.0.0.1:8000/article/article-create/
時,會調取article_create
函數返回 article/create.html
頁面.
我們直接用html語言簡單建立該頁面測試一下:
<!DOCTYPE html>
<html>
<head>
<title>只是測試</title>
</head>
<body>
<div class="container"><div class="row"><div class="col-12"><br><!-- 提交文章的表單 --><form method="post" action="."><!-- Django中需要POST數據的地方都必須有csrf_token -->{% csrf_token %}<!-- 文章標題 --><div class="form-group"><!-- 標簽 --><label for="title">文章標題</label><!-- 文本框 --><input type="text" class="form-control" id="title" name="title"></div><!-- 文章正文 --><div class="form-group"><label for="body">文章正文</label><!-- 文本區域 --><textarea type="text" class="form-control" id="body" name="body" rows="12"></textarea></div><!-- 提交按鈕,表單提交會直接POST --><button type="submit" class="btn btn-primary">完成</button></form></div></div>
</div>
</body>
</html>
訪問127.0.0.1:8000/article/article-create/
,得到如下頁面:
到此,我們已經完成了所有應用模型、視圖的搭建,并初步構建了一個文件創建的頁面進行驗證,下一步我們將針對每一個視圖建設模板,完成視圖與模板之間的連接。