云计算百科
云计算领域专业知识百科平台

从零开始搭建Django博客②--Django的服务器内容搭建

本文主要在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)

# 文章正文。保存大量文本使用 TextField
body = 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/id
def 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)
# 浏览量 +1
article.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_title
article.body = new_article_body
article.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.user
models.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 = User
fields = ('username', 'email')

# 对两次输入的密码是否一致进行检查
def clean_password2(self):
data = self.cleaned_data
if 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(应用内)

项目中定义路由(根目录的urls.py)

# 引入用户管理后台
from django.contrib import admin
# 记得引入include
from django.urls import path, include
# 需要使用文章视图显示文章列表作为首页
from article import views

urlpatterns = [
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 views

app_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/,得到如下页面:

在这里插入图片描述

到此,我们已经完成了所有应用模型、视图的搭建,并初步构建了一个文件创建的页面进行验证,下一步我们将针对每一个视图建设模板,完成视图与模板之间的连接。

赞(0)
未经允许不得转载:网硕互联帮助中心 » 从零开始搭建Django博客②--Django的服务器内容搭建
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!