【学习笔记】Django网页开发
Datawhale组队学习第26期:Django网站开发
本次学习的指导老师Tango的个人博客和教学视频
本贴为学习记录帖,有任何问题欢迎随时交流~
部分内容可能还不完整,后期随着知识积累逐步完善。
开始时间:2021年6月13日
最新更新:2021年7月11日(更新至Task3编写view)
文章目录
一、基础配置
(一)安装好基础软件。
1.Typora
结合Markdown写文章用的,所见即所得,体验感不错。
- Markdown同样也可以在VS Code上写,同样支持实时预览,需要安装插件Markdown All In One和Markdown Preview Enhanced。
- 个人习惯在VS Code上,上面也配置了LaTex,方便需要多种公式的写作和排版。
2.VMware和Ubuntu
虚拟机与系统,用于搭建服务器。
个人本机是Windows 10系统,服务器是云ECS服务器,配置的是Ubuntu20.04系统。
3.VSCode
代码编辑器,后面用于远程连接服务器和代码编辑。已安装,但需要配置插件和远程连接。
- 关于远程连接,使用的是Remote-SSH,会有遇到坑的可能,具体看第三节远程控制那一块内容。
- 经过组里小伙伴的实践验证,这里不一定需要使用VSCode,可以选用**Pycharm Professionals(专业版)**进行远程控制。其中具有学校邮箱等条件下,可以有一年***的机会,具体到JetBrains官网进行查阅。
4.宝塔
- 服务器后台管理
- 既可以用于搭建数据库和设置代理,也可以用于远程服务器的文件管理。
(二)云服务器设置
1.安全组设置
- 开放22端口,用于远程连接。
- 开放8888端口,宝塔用的面板端口。
- 开发3306端口,MySQL服务器端口。
2.用户设置
- 新增用户并增加管理权限
$ sudo adduser enguanei $ sudo adduser enguanei sudo
- 登录新用户,修改主机名
sudo hostname ubuntu
(三)远程连接
1.VS Code插件安装
打开Visual Studio Code,安装插件:Renite-SSH。
2.修改配置
- VS Code进行远程连接的时候出现“Bad owner or permissions on XXX”的错误
查资料的时候,发现VSCode和OpenSSH貌似是有冲突。
经过好几番尝试,最后选用了Git SSH的方案。 - 把OpenSSH删掉,安装Git。
- 将Git安装目录下
~ Git\bin
和~ Git\usr\bin
加入到系统变量中。 - 修改VS Code中“Remote-SSH”的设置文件
settings.json
,在该文件中写入以下内容:{ "remote.SSH.path": "Git安装目录\\Git\\usr\\bin\\ssh.exe", "remote.SSH.showLoginTerminal": true }
(四)环境配置
1.定位虚拟环境的位置
- 在
Desktop
下新建文件夹DjangoDev
,再进入DjangoDev
中新建demo
和env
文件夹。 env
用于存储虚拟环境所需要的包,demo
用于小文件测试。
2.建立虚拟环境
- 进入env文件夹,使用
python3 -m venv
建立虚拟环境 - 注意:提前使用
sudo apt-get install python3-venv
进行虚拟环境包安装
3.激活虚拟环境
- 使用
source bin/activate
激活虚拟环境 - 注意:这里使用的是相对路径
4.安装Django
- 使用
pip3 install django --trusted-host mirrors.aliyun.com
安装Django。
(五)Python相关的报错注意
报错一:安装ipykernel出现returned non-zero exit status 1
报错原因不清楚,需要进一步查证。
解决方法:参考Tango老师和其他博主的思路。
sudo rm /usr/bin/lsb_release
报错二:Not a trusted or secure host and is being ignored.
修改完后,需要增加原先设置的阿里源存在问题,利用pip安装时需要加上--trusted-host mirrors.aliyun.com
才能正常下载,原因阿里源设置时需要用http而不是https。
/usr/bin/python3 /home/enguanei/.vscode-server/extensions/ms-python.python-2021.5.926500501/pythonFiles/pyvsc-run-isolated.py pip install -U ipykernel --user --trusted-host mirrors.aliyun.com
-
安装完毕后如下所示,左边为脚本环境,右边为交互环境。
-
在脚本环境下输入
#%%
可以进入分段交互(挺有趣的知识点)。
(六)HTML的知识点补充
-
安装插件:HTML Snippets可以用于增强提示。Live Server可以用于本地连接远程的html。
-
在html界面中更换语言模式,原本是Django html,可以改成html。
-
head
是定义美观动作,body
是组织内容。 -
<tr>
表示表行,<td>
表示表列。 -
小实验代码如下:
-
展示结果:
二、站点设置
(一)添加站点
- 打开宝塔,这里需要先安装好Nginx和MySQL。
(二)修改保存路径
为了能使用前面设定好的虚拟环境看,修改网页保存的路径
(三)修改权限
- 不修改权限可能会报错。
三、初始化Django
(一)创建django 项目
django-admin startproject myDemo
生成的文件如下所示:
(二)修改文件名
为了方便开发,需要对文件名进行调整。
-
注意不能随意修改文件夹config所依赖的文件名,会报错。
-
删除无关文件
sudo rm -r -f myDemo/
(三)开启服务
- 进入服务
python manage.py runserver
- 创建app
python manage.py startapp blog
- 修改settings,与新建的blog关联
四、初始化后台管理
(一)创建superuser
- 创建超级管理员
设置用户名和密码,用于登陆后台管理。
python manage.py createsuperuser
- 同步数据库(这一步十分重要!)
python manage.py migrate
(二)登录后台
- 输入
python manage.py runserver
可以登进网页(第四部分的“开启服务”) - 在
127.0.0.1:8000
后面输入admin
可以连上后台管理(url配置文件设定)
(三)后台语言和时区修改
- 语言修改
找到config
文件夹下的settings.py
文件,修改LANGUAGE_CODE
LANGUAGE_CODE = 'zh-hans' # en-us 为 英文
- 时区修改
找到config
文件夹下的settings.py
文件,修改TIME_ZONE
TIME_ZONE = 'Asia/Shanghai'
- 修改完毕后,后台管理页面如下所示
(四)博客内容管理
此时的blog模块并未出现相应的内容,需要对blog
文件夹下的models
和admin
进行修改。
-
对
models.py
进行修改
-
对
admin.py
进行修改
将刚写好的blog
类导入到admin
中
-
同步数据库,否则会报错(No such table)
此时使用的数据库是SQLite# 终端上输入 python manage.py makemigrations python manage.py migrate
-
修改完毕后,可以增加blog内容(此时只能修改FILEDNAME)
-
字段中FILEDNAME和左边列表的Blogs修改为中文
修改models.py
的内容,注意这里的__str__
函数的作用在于编辑blog内容的时候,返回的是你创建标题的名字而非object(n)。class Blog(models.Model): FILEDNAME = models.CharField(max_length=255, verbose_name="标题") class Meta: verbose_name = "博客管理" verbose_name_plural = verbose_name def __str__(self): return self.title
同步数据库(记得每次修改内容时都需要同步数据库)
# 终端上输入 python manage.py makemigrations python manage.py migrate
-
进一步增加其他字段,如文章内容、阅读次数、创建时间等
- 继续修改
models.py
的内容,新建字段一般都会设置默认值,注意IntegerField
设置默认值应为数值型的,如0。 - CharField、TextField、IntegerField和DateTimeField的区别具体可以去查阅官方文档,在运行后也能直观看到各自的区别。
class Blog(models.Model): title = models.CharField(max_length=50, verbose_name="标题", default="") content = models.TextField(verbose_name="文章内容", default="") count = models.IntegerField(verbose_name="阅读次数", default=0) creat_time = models.DateTimeField( auto_now=True, verbose_name="创建时间") class Meta: verbose_name = "博客管理" verbose_name_plural = verbose_name
- 完成后,同步数据库
- 继续修改
-
显示blog管理中所有文章的信息
修改admin.py
文件# 将原来写的内容注释掉,使用装饰器 # admin.site.register(Blog) @admin.register(Blog) class BlogAdmin(admin.ModelAdmin): list_display = ("title", "creat_time", "count") # 这里的title等是来自于models中的字段
结果如下图所示:
(五)主页面设置
-
修改
blog
文件夹下的views.py
文件from .models import Blog
是从同级目录下的models.py
导入Blog
(上面写好的)Blog.objects.all()
返回的是列表render
用于向页面(“index.html”)传递内容(“blogs”)
# 增加以下内容 from .models import Blog # 用于展示博客信息 def index(request): # blogs = Blog.objects.all() 相当于SQL中的select * from Blog blogs = Blog.objects.all().order_by("-creat_time") # 按时间排序 return render(request, "index.html", { "blogs": blogs}) # 用于点击获取具体的博客信息 def read_blog(request, id): print(f"{ id}:我是通过前端点击链接来的") blog = Blog.objects.get(id=id) return render(request, "content.html", { "blog": blog})
-
在
MyBlog
文件夹下新建文件夹templates
,同时新建网页index.html
和content.html
该文件夹用于保存网页
-
修改
settings.py
文件
对TEMPLATES
内容(链接路径)进行修改# 修改前先导入os库 import os # 修改DIRS,链接到上面创建的文件夹templates TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, "templates")], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
-
修改
config
文件夹下的urls.py
该文件是用于请求映射,信息传输到浏览器页面上。# 额外导入上面写好的 index 函数 from blog.views import index, read_blog # 增加 index 和 read_blog 链接路径,其中在后台返回点击的 id 信息 urlpatterns = [ path('admin/', admin.site.urls), path("", index, name="index"), path("read/<int:id>", read_blog, name="read_blog"), ]
-
修改主页
index.html
的页面内容<html> <head> <title>我的首页</title> </head> <body> <h1>欢迎来到我的网站!</h1> <ul> {% for blog in blogs %} <li> <a href="{% url 'read_blog' blog.id %}">{ {blog.title}}</a> </li> <p>创建于 { {blog.creat_time}}</p> {% endfor %} </ul> </body> </html>
-
修改子页面
content.html
的页面内容<html> <head> <title>我的首页</title> </head> <body> <a href="/" style="float:right">回到首页</a> <h1>{ {blog.title}}</h1> <div> { {blog.content}} </div> </body> </html>
五、建立MySQL数据库
(一)初始化MySQL数据库
- 宝塔上安装MySQL,版本要求是5.7以上(Django官方文档给出的最低要求)
- 在软件商店上下载
- 设置好MySQL的账号密码和权限(可以设置为所有人)
- 在软件商店上下载
- 虚拟环境中安装
pymysql
pip install pymysql --trusted-host mirrors.aliyun.com
- 修改
config
文件夹中的settings.py
,具体可以参考官方文档# 将原有的的DATABASES注释掉 # DATABASES = { # 'default': { # 'ENGINE': 'django.db.backends.sqlite3', # 'NAME': BASE_DIR / 'db.sqlite3', # } # } DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'db_name', 'USER': 'user_name', 'PASSWORD': 'pwd', 'HOST': 'ip', 'PORT': '3306', } }
- 修改
config
文件夹下的__init__.py
文件import pymysql pymysql.install_as_MySQLdb() # 加载MySQL数据库
- 同步数据库
# 终端上执行 python manage.py migrate
-
报错类型中为Can’t connect to MySQL server
一般是因为防火墙限制,无法远程连接。
在终端上开放对应端口即可。sudo ufw status # 查看端口号开放情况 sudo allow 3306 # 开放对应的端口号
-
报错类型中为Access denied for user XXX to database 'blog’
一般是因为对应用户的db中没有blog
。
可以尝试远程连接MySQL看看该用户有哪些数据库有哪些(一般是跟用户名一致),NAME
是用户下的数据库中的一个。 -
更新成功后,登进数据库进行查看
# 终端执行 mysql -u user_name -p
show databases; # 查看有哪些数据库 use db_name; # 进入更新的数据库 show tables; # 查看该数据库的所有表
-
(二)后台代理设置
1.安装插件
- 安装supervisor和gunicorn
# 退出虚拟环境安装supervisor sudo apt install supervisor -y # 进入虚拟环境安装gunicorn pip install gunicorn --trusted-host mirrors.aliyun.com
2.配置gunicorn
-
在
MyBlog
的文件夹下新建gunicorn_start.sh
,写入以下内容DJANGODIR
填入的是django项目路径USER
和GROUP
填入的是用户名source
是激活虚拟环境,填入对应虚拟环境的路径exec
也是填入虚拟环境的路径
#!/bin/bash NAME="MyBlog" DJANGODIR=******/MyBlog #Django project directory USER=user_name # the user to run as GROUP=group_name # the group to run as NUM_WORKERS=1 # how many worker processes should Gunicorn spawn DJANGO_SETTINGS_MODULE=config.settings # which settings file should Django use DJANGO_WSGI_MODULE=config.wsgi # WSGI module name echo "Starting $NAME as `whoami`" # Activate the virtual environment cd $DJANGODIR source ******/env/bin/activate export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE export PYTHONPATH=$DJANGODIR:$PYTHONPATH # Create the run directory if it doesn't exist RUNDIR=$(dirname $SOCKFILE) test -d $RUNDIR || mkdir -p $RUNDIR # Start your Django Unicorn # Programs meant to be run under supervisor should not daemonize themselves (do not use --daemon) exec ******/env/bin/gunicorn ${DJANGO_WSGI_MODULE}:application \ --name $NAME \ --workers $NUM_WORKERS \ --user=$USER --group=$GROUP \ --log-level=debug \ --log-file=-
-
在宝塔的文件管理上修改
gunicorn_start.sh
的权限(777) -
终端上执行
./gunicorn_start.sh
,可以通过网页访问设置好的网站
3.宝塔代理设置
- 修改
config
文件夹下的settings.py
文件DEBUG = False ALLOWED_HOSTS = ["*"]
- 修改宝塔的网站配置文件
增加如下内容location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-NginX-Proxy true; proxy_redirect off; if (!-f $request_filename) { proxy_pass http://127.0.0.1:8000; break; } }
- 修改完后,在宝塔上对nginx进行重载配置和重启
4.nginx代理设置
- 在
MyBlog
文件夹下新建log
文件夹,用于存放日志内容。 - 终端上执行
sudo vim /etc/supervisor/conf.d/myblog.conf
,进入vim操作。
按<kbd>i</kbd>进入编辑模式,输入以下内容(******
填入对应的路径):[program:myblog] command = ******/gunicorn_start.sh user = server autostart=true autorestart=true redirect_stderr = true stdout_logfile = ******/log/myblog.log stderr_logfile = ******/log/myblog.err
- 终端上更新配置
sudo supervisorctl update sudo supervisorctl reload sudo /etc/init.d/nginx restart # 也可以在宝塔上进行重启Nginx
- 配置完毕
- 当ngnix启动后,可以直接利用宝塔网站配置文件中监听ip地址进行访问。
- 通常需要维护网站时,可以将ngnix关闭,外网无法访问。
- 更新网站内容后,需要执行上面的更新配置。
(三)页面管理
1.后台静态页面丢失问题
上述操作完毕后,登进管理页面,发现显示不正常(CSS和JS丢失):
2.后台静态页面修复
- 修改
settings.py
,在STATIC_URL
下面增加以下内容:STATICFILES_DIRS = [ os.path.join(BASE_DIR, "static") ]
- 在
MyBlog
文件夹下新建static
文件夹,用于存放静态页面内容 - 执行如下命令,自动在
static
文件夹下自动生成admin
文件夹(包含了CSS和JS)python manage.py collectstatic
- 修改宝塔中网站的配置文件
新增如下内容location /static/ { alias ******/MyBlog/static/; expires max; access_log off; log_not_found off; }
六、模块划分
(一)模块分类设计
-
文章(Post)
- ID
- 标题:title
- 作者:owner
- 分类(多对一):category多篇文章对应一个分类
- 标签(多对多):tags
- 摘要:description
- 正文:content
- 状态(草稿、发布、删除):status
- 发布时间:create_time
-
分类(Category)
- ID(Django自动分配)
- 名称:name
- 状态:status
- 作者:owner
- 创建时间:create_time
- 是否置顶导航:is_nav
-
标签(Tag)
- ID
- 名称:name
- 状态:status
- 作者:owner
- 创建时间:created_time
-
友情链接(Link)
- ID
- 网站名称:title
- 链接:href
- 作者:owner
- 状态:status
- 创建时间:created_time
- 权重:weight
-
评论(Comment)
- ID
- 文章(多对一):post
- 用户名:nickname
- 邮箱:email
- 网站地址:website
- 内容:content
- 创建时间:created_time
- 作者:owner
-
侧边栏(SideBar)
- ID
- 标题:title
- 类型:type
- 最新文章
- 最热文章
- 最近评论
- 状态:status
- 内容:content
- 创建时间:created_time
- 作者:owner
-
用户(User)
- ID
- 成就:achievement
- 头像:
(二)个人博客实体关系图
(三)模块划分脑图
(四)APP模块构建
1.APP模块创建
# python manage.py startapp blog # 该部分先前已经配置过,不用执行
python manage.py startapp blog_config
python manage.py startapp comments
- 将
blog_config
和comments
添加到config
文件夹下的settings
中。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog',
'blog_config',
'comments',
]
2.blog模块设置
- 清理原有blog的内容
- 对
models.py
文件进行修改from django.db import models from django.db.models.fields import DateTimeField from django.contrib.auth.models import User # Create your models here. class Category(models.Model): """分类 - ID(Django自动分配) - 名称:name - 状态:status - 作者:owner - 创建时间:create_time - 是否置顶导航:is_nav """ STATUS_ITEMS = ( (1, "正常"), (0, "删除"), ) name = models.CharField(max_length=50, verbose_name="名称") status = models.IntegerField(choices=STATUS_ITEMS, default=1, verbose_name="状态") owner = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="作者") is_nav = models.BooleanField(default=False, verbose_name="是否为导航") created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") def __str__(self): return self.name class Meta: verbose_name = '分类' verbose_name_plural = verbose_name class Tag(models.Model): """标签 - 名称:name - 状态:status - 作者:owner - 创建时间:created_time """ STATUS_ITEMS = ( (1, "正常"), (0, "删除"), ) name = models.CharField(max_length=50, verbose_name="名称") owner = models.ForeignKey( User, on_delete=models.CASCADE, verbose_name="作者") status = models.IntegerField( choices=STATUS_ITEMS, default=1, verbose_name="状态") created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") def __str__(self): return self.name class Meta: verbose_name = '标签' verbose_name_plural = verbose_name class Post(models.Model): """文章 - 标题:title - 作者:owner - 分类(多对一):category多篇文章对应一个分类 - 标签(多对多):tags - 摘要:desc - 正文:content - 状态(草稿、发布、删除):status - 发布时间:create_time """ STATUS_ITEMS = ( (1, "正常"), (0, "删除"), (2, "草稿") ) title = models.CharField(max_length=255, verbose_name="标题") owner = models.ForeignKey( User, on_delete=models.CASCADE, verbose_name="作者") category = models.ForeignKey(Category, verbose_name="分类", on_delete=models.CASCADE) tags = models.ManyToManyField(Tag, verbose_name="标签") desc = models.CharField(max_length=1024, verbose_name="摘要") content = models.TextField(verbose_name="正文") status = models.IntegerField( choices=STATUS_ITEMS, default=2, verbose_name="状态") created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") def __str__(self): return self.title class Meta: verbose_name = '文章' verbose_name_plural = verbose_name
- 对
admin.py
进行修改from django.contrib import admin from .models import Category, Tag, Post @admin.register(Category) class CategoryAdmin(admin.ModelAdmin): pass @admin.register(Tag) class TagAdmin(admin.ModelAdmin): pass @admin.register(Post) class PostAdmin(admin.ModelAdmin): pass
3.blog_config模块设置
- 修改
models.py
from django.contrib.admin.decorators import display from django.db import models from django.contrib.auth.models import User # Create your models here. class Link(models.Model): """友情链接 - 网站名称:title - 链接:href - 作者:owner - 状态:status - 创建时间:created_time - 权重:weight """ STATUS_ITEMS = ( (1, "正常"), (0, "删除"), ) title = models.CharField(max_length=50, verbose_name="网站名称") href = models.URLField(verbose_name="链接") status = models.IntegerField( choices=STATUS_ITEMS, default=1, verbose_name="状态") owner = models.ForeignKey( User, on_delete=models.CASCADE, verbose_name="作者") created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") weight = models.IntegerField(choices=zip( range(1, 11), range(1, 11)), default=1, verbose_name="权重") def __str__(self): return self.name class Meta: verbose_name = '友情链接' verbose_name_plural = verbose_name class SideBar(models.Model): """侧边栏 - 标题:title - 类型:type - 状态:status - 内容:content - 创建时间:created_time - 作者:owner """ TYPE_ITEMS = ( (1, "HTML"), (2, "最新文章"), (3, "最热评论"), (4, "最近评论"), ) STATUS_ITEMS = ( (1, "展示"), (0, "隐藏"), ) title = models.CharField(max_length=50, verbose_name="标题") display_type = models.IntegerField( choices=TYPE_ITEMS, default=1, verbose_name="展示类型") status = models.IntegerField( choices=STATUS_ITEMS, default=1, verbose_name="状态") content = models.CharField(max_length=500, verbose_name="内容") owner = models.ForeignKey( User, on_delete=models.CASCADE, verbose_name="作者") created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") def __str__(self): return self.name class Meta: verbose_name = '侧边栏' verbose_name_plural = verbose_name
- 修改
admin.py
from django.contrib import admin from .models import Link, SideBar @admin.register(Link) class LinkAdmin(admin.ModelAdmin): pass @admin.register(SideBar) class SideBarAdmin(admin.ModelAdmin): pass
4.comments模块设置
- 修改
models.py
from django.db import models from blog.models import Post # Create your models here. class Comment(models.Model): """评论 - 文章(多对一):post - 用户名:nickname - 邮箱:email - 网站地址:website - 内容:content - 创建时间:created_time - 作者:owner """ target = models.ForeignKey( Post, verbose_name="评论目标", on_delete=models.CASCADE) nickname = models.CharField(max_length=50, verbose_name="昵称") email = models.EmailField(verbose_name="邮箱") website = models.URLField(verbose_name="网站地址") content = models.TextField(verbose_name="评论内容") created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") def __str__(self): return f"{ self.target} By { self.nickname}" class Meta: verbose_name = '评论' verbose_name_plural = verbose_name
- 修改
admin.py
from django.contrib import admin from .models import Comment # Register your models here. @admin.register(Comment) class CommentAdmin(admin.ModelAdmin): pass
- 修改
5.更新数据库
- 上面的所有设定方便SQL进行数据写入,交给框架处理SQL
# 终端上执行 python manage.py makemigrations python manage.py migrate
6.进入后台管理
- 开启Debug
- 修改
settings.py
文件
DEBUG = True STATICFILES_DIRS = [ os.path.join(BASE_DIR, "static") ] # STATIC_ROOT = os.path.join(BASE_DIR, "static") # 这个需要注释掉
- 修改
- 开启服务
# 终端上执行 python manage.py runserver 127.0.0.1:8899
(五)Admin自定义设置
1.浏览页面展示设置
- 修改
blog
下的admin.py
owner是外键,不允许为空,而fields不包含owner,导致报错@admin.register(Category) class CategoryAdmin(admin.ModelAdmin): list_display = ('name', 'status', 'is_nav', 'created_time') # 首页展示 # fields = ('name', 'status', 'is_nav') # 这种写法错误 fields = ('name', 'status', 'owner', 'is_nav') # 详细页展示
2.设置标签名与登录用户保持一致
- 修改
blog
文件夹下的admin.py
@admin.register(Category) class CategoryAdmin(admin.ModelAdmin): list_display = ('name', 'status', 'owner', 'is_nav', 'created_time') fields = ('name', 'status', 'is_nav') def save_model(self, request, obj, form, change): obj.owner = request.user return super(CategoryAdmin, self).save_model(request, obj, form, change)
3.设置文章的搜索框、保存/删除的位置
- 修改
blog
下的admin.py
- exclude与fields相反
- actions_on_top和actions_on_bottom是控制执行动作的位置
- save_on_top是控制页面编辑的位置
- search_fields是增加搜索功能
- 注意tuple内,若需要查询的是外键,需要加上
__
符号。
class PostAdmin(admin.ModelAdmin): list_display = ("category","title", "owner", "status") exclude = ("owner",) actions_on_top = True save_on_top = True search_fields = ("title", "category__name") def save_model(self, request, obj, form, change): obj.owner = request.user return super(PostAdmin, self).save_model(request, obj, form, change)
4.显示对应标签文章的数量
- 修改
blog
下的admin.py
@admin.register(Category) class CategoryAdmin(admin.ModelAdmin): list_display = ('name', 'status', 'owner', 'is_nav', 'post_count', 'created_time') fields = ('name', 'status', 'is_nav') # select * from Post where p.category = 'name' def post_count(self, obj): return obj.post_set.count() post_count.short_description = "文章数" def save_model(self, request, obj, form, change): obj.owner = request.user return super(CategoryAdmin, self).save_model(request, obj, form, change)
(六)Views自定义设置
1.预备工作
- 通过宝塔上传模板文件,模板文件可以去Datawhale中获取。
2.创建base.html
-
这里的base.html是整个网页的框架,基本上是不怎么变动的,因此可以独立出来修改,再与其他页面互相链接。
-
复制
index.html
并更名为base.html
-
通过对模板网页结构的分析,可以将
main list
部分去除并保留在index.html
中,其他内容为base.html
的核心。
-
在
blog
文件夹下的views.py
写入函数,用于识别base.html
,向前端传递信息from django.shortcuts import render def index(request): return render(request, "base.html")
-
在
config
文件夹下的urls.py
导入上面写好的index
函数,添加识别路径from os import name from django.contrib import admin from django.urls import path from blog.views import index urlpatterns = [ path('admin/', admin.site.urls), path("", index, name="index"), ]
-
该
base.html
页面结果如下,大致形成了网站的基本框架。
3.导航界面修改
- 在这一模块下,简单来说是,
views.py
获取到查询的结果,以列表的形式传递给了urls.py
,urls.py
将其做成网站地址传递给了前端,base.html
从urls.py
接收到变量的信息,反映到网页上。后续的大部分内容都是依照这个思路不断改进和完善。 - 先对导航的内容进行查询,修改
blog
文件夹下的views.py
此时刷新页面时,终端会返回查询的结果(是列表形式),如:from django.shortcuts import render from .models import Category def index(request): categories = Category.objects.filter(is_nav=True , status=1) print(categories) return render(request, "base.html", { 'categories': categories} ) # 将categories结果返回给前端
<QuerySet [<Category: test1>, <Category: Python>, <Category: C++>]>
- 导航界面是有
base.html
控制的,修改base.html
上面返回给前端的结果是一个列表,可以在前端采用for循环取值。<ul class="layui-nav layui-bg-cyan"> <li class="layui-nav-item layui-this"> <a href="">首页</a> </li> {% for category in categories %} <li class="layui-nav-item"> <a href="">{ {category.name}}<span class="layui-badge-dot"></span></a> </li> {% endfor %} </ul>
- 结果如下,导航栏显示了后台设定的分类。
4.关联网页模块
- 增加
base.html
与index.html
的链接- 修改
base.html
,在刚才删除主体内容的位置增加block
模块
<div class="layui-col-md8"> {% block content %} {% endblock %} </div>
- 修改
index.html
,同样增加block
模块,同时通过extend
与base.html
链接
{% extends 'base.html' %} {% block content %} base.html删除的内容 {% endblock %}
- 修改
views.py
,将原来render
返回的base.html
修改为index.html
,完成链接。
- 修改
5.修改主体内容
-
原理同上,先查blog内容,然后通过
render
返回信息到前端(修改views.py
)from django.shortcuts import render from .models import Category, Post def index(request): categories = Category.objects.filter(is_nav=True , status=1) blogs = Post.objects.filter(status=1) return render(request, "index.html", { 'categories': categories, 'blogs': blogs })
-
分析
index.html
的框架
可以发现,每个blog内容都是由以下框架构成:<div class="layui-col-md12 margin20"></div> <div class="layui-col-md12"> ... </div>
- 删除上述的框架,仅保留一个
后续可以使用for循环
遍历blog的内容
- 删除上述的框架,仅保留一个
-
在后台添加新的文章
这里需要注意的是,上面views.py
文件中执行的查询是有条件的,即是导航栏且非草稿。因此添加文章时需要按条件来添加,以便观察效果。
-
修改框架的主体内容
- 上面的操作完成了页面的基本展示,但标题、标签、时间等都没有改变(仍然是模板的内容),这部分内容是在
index.html
上。 - 只需找到对应的文本位置进行修改即可,如:
title="{ {blog.title}}"
- 其中展示文章的标签,可以使用for循环
<div class="layui-col-xs3 layui-col-md3 Label"> <i class="layui-icon layui-icon-note"></i> {% for tag in blog.tags.all %} <a href="javascript:;">{ {tag.name}}</a> {% endfor %} </div>
- 上面的操作完成了页面的基本展示,但标题、标签、时间等都没有改变(仍然是模板的内容),这部分内容是在
6.通过detail.html建立链接指向
- 实现“阅读原文”指向对应的blog文章
- 修改
blog
文件夹下的views.py
,增加detail
def detail(request, id): blog = Post.objects.get(id=id) return render(request, "detail.html", { 'blog': blog })
- 修改
detail.html
内容,仅保留文章的主体内容<div class="layui-row">
{% extends 'base.html' %} {% block content %} <div class="layui-row"> xxx </div> {% endblock %}
- 修改
base.html
内容,在href
上增加链接
{% extends 'base.html' %} {% block content %} <div class="layui-row"> xxx </div> {% endblock %}
- 修改
7.博客页面导航栏设置
- 目的是同步导航栏,与首页一致
- 在
views.py
上将categories
设置为全局变量,在detail
中增加categories
from django.shortcuts import render from .models import Category, Post categories = None def index(request): global categories categories = Category.objects.filter(is_nav=True , status=1) blogs = Post.objects.filter(status=1) for blog in blogs: print(blog.tags.all()) return render(request, "index.html", { 'categories': categories, 'blogs': blogs }) def detail(request, id): blog = Post.objects.get(id=id) return render(request, "detail.html", { 'categories': categories, 'blog': blog })
8.首页链接设置
- 修改
base.html
,首页上的链接修改成<a href="/">首页</a>
9.设置导航栏的链接
-
在
blog
文件夹下的views.py
中新增一个函数,用于获取分类下的文章,即建立查询def get_posts_by_category(request, id): blogs = Post.objects.filter(status=1, category_id=id) print(blogs) return render(request, "index.html", { 'categories': categories, 'blogs': blogs })
-
在
config
文件夹下的urls.py
中导入上面的函数,添加路径from os import name from django.contrib import admin from django.urls import path # from blog.views import index, read_blog from blog.views import index, detail, get_posts_by_category urlpatterns = [ path('admin/', admin.site.urls), path("", index, name="index"), path("detail/<int:id>", detail, name="detail"), path("category/detail/<int:id>", get_posts_by_category, name="category_posts"), ]
-
修改
base.html
中的categories
循环中修改href
href="{% url 'category_posts' category.id %}"
参考资料
- https://www.bilibili.com/video/BV1w5411u7kc?t=4913
- https://github.com/datawhalechina/team-learning-program/tree/master/Django
- https://zhuanlan.zhihu.com/p/380235160
- https://blog.csdn.net/TongxinV/article/details/94883101
- https://www.bilibili.com/video/BV1Ab4y1o7JV?t=643
- https://www.cnblogs.com/longbigbeard/p/10028746.html
- https://www.infoq.cn/u/tango/publish
- https://blog.csdn.net/tdcqfyl/article/details/52104476