完整代码在:https://gitee.com/dadadaliuliuliiu/BlogProject
随着互联网的飞速发展,让Internet应用在全球范围内日趋普及,当今社会也正快速向信息化社会发展,开发个人博客有着非常重要的意义。
本系统采用 Django
技术,后台开发工具为Pycharm
,前台开发工具为Pycharm
,数据库采用 MySQL
来进行开发。
(base) F:\ziliao\python_kaifa\my_code\12_django项目\BlogProject>python manage.py createsuperuser #创建后台管理的用户
用户名 (leave blank to use 'daliu'): admin
电子邮件地址: admin@qq.com
Password:westos123
Password (again):
(base) F:\ziliao\python_kaifa\my_code\12_django项\BlogProject>python manage.py runserver
浏览器端测试:
登陆刚才创建的用户,进入后台管理:
admin
westos123
blog 是整个BlogProject项目的一个功能
(base) F:12_django项目\BlogProject>python manage.py startapp blog
目录中出现blog应用
博客最主要的功能就是展示我们写的文章,它需要从某个地方获取博客文章数据才能把文章展示出来,通常来说这个地方就是数据库。我们把写好的文章永久地保存在数据库里,当用户访问我们的博客时,Django 就去数据库里把这些数据取出来展现给用户。
数据库存储的数据其实就是表格的形式。
存储博客文章的数据库表如下所示:
分析一下我们就会发现一个问题,这 3 篇文章的分类和标签都是相同的,这会产生很多重复数据,当数据量很大时就浪费了存储空间。
我们把分类和标签提取出来,做成单独的数据库表,再把文章和分类、标签关联起来。
#blog/models.py
from datetime import datetime
from django.db import models
# Create your models here.
class Category(models.Model):
"""分类表"""
#一个类就是一个数据库表
# Django的数据库模型默认会添加id作为主键
name=models.CharField(max_length=100)
def __str__(self):
return self.name
class Tag(models.Model):
"""标签表"""
name=models.CharField(max_length=100)
def __str__(self):
return self.name
class Post(models.Model):
""" 博客表 """
# 文章标题
title = models.CharField(max_length=70)
# 文章正文,我们使用了 TextField。
body = models.TextField()
# 这两个列分别表示文章的创建时间和最后一次修改时间,存储时间的字段用 DateTimeField 类型。
# auto_now=True,每次对像更新时更新时间。auto_now_add=True对象第一次创建时设置的时间
created_time = models.DateTimeField(auto_now_add=True)
modified_time = models.DateTimeField(auto_now=True)
# 文章摘要,可以没有文章摘要,但默认情况下 CharField 要求我们必须存入数据,否则就会报错。
# 指定 CharField 的 blank=True 参数值后就可以允许空值了。
excerpt = models.CharField(max_length=200, blank=True)
# on_delete=False在Django1.x版本默认是不级联删除的,可以不做设置. Django2.x一定要自 行指定.
category = models.ForeignKey(Category, on_delete=False)
# 多对多关系。 blank=True标签可以不做设置
tags = models.ManyToManyField(Tag, blank=True)
# 文章作者,这里 User 是从 django.contrib.auth.models 导入的。
# 因为我们规定一篇文章只能有一个作者,而一个作者可能会写多篇文章,因此这是一对多的关联关 系,和 Category 类似。
# author = models.ForeignKey(User)
def __str__(self):
return self.title
数据库做了改变,就要写入数据库,所以要产生一个迁移文件
(base) F:12_django项目\BlogProject>python manage.py makemigrations #生成迁移脚本
Migrations for 'blog':
blog\migrations\0001_initial.py
- Create model Category
- Create model Tag
- Create model Post
(base) F:\12_django项目\BlogProject>python manage.py migrate # 将迁移信息写入数据库
Operations to perform:
Apply all migrations: admin, auth, blog, contenttypes, sessions
Running migrations:
Applying blog.0001_initial... OK
要在后台注册我们自己创建的几个模型,这样 Django Admin 才能知道它们的存在,注册非常简单,只需要在 blog\admin.py 中加入下面的代码:
blog 是整个BlogProject项目的一个功能 ,Post, Category, Tag是一个类,他们分别对应一个数据库表,表示博文的正文、分类以及标签信息。因此,要在后台注册我们自己创建的几个模型,这样 Django Admin 才能知道它们的存在
# blog/admin.py
from django.contrib import admin
from blog.models import Post, Category, Tag
admin.site.register(Post)
admin.site.register(Category)
admin.site.register(Tag)
运行程序
访问网址 http://127.0.0.1:8000/admin/ 测试是否设置成功?
在 admin post 列表页面,我们只看到了文章的标题,但是我们希望它显示更加详细的信息,这需要我们来定制 Admin 了,在 admin.py 添加如下代码:
from django.contrib import admin
from blog.models import Post, Category, Tag
class PostAdmin(admin.ModelAdmin):
list_display = ['title', 'created_time', 'modified_time', 'category']
list_display_links = ['title', 'created_time']
search_fields=['title'] # 以title搜索
list_per_page = 5 #分页
# 把新增的 PostAdmin 也注册进来
admin.site.register(Post,PostAdmin)
admin.site.register(Category)
admin.site.register(Tag)
当用户在浏览器端输入一个url时,首先访问这里的代码
<网站域名>/post/1/
时,显示的是第一篇文章的内容,<网站域名>/post/2/
时,显示的是第二篇文章的内容。这里数字代表了第几篇文章,也就是数据库中 Post 记录的 pk(id) 值。下面依照这个规则来绑定 URL 和视图:首先在 blog 应用的目录下创建一个 urls.py 文件, 在 blog\urls.py 中写入这些代码:
# blogs/urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
#当用户在浏览器端输入一个url,没有开头也没有结尾时,比如:http://127.0.0.1:5000,去寻找index首页的视图函数,
url(r'^$', views.index, name='index'),
# 正则表达式
# ^: 以什么开头, $是以什么结尾。 [0-9]指单个数字, +代表前面的字符出现1次或者多次。
# [0-9]+: 1, 2, 2344, 78888,
# (?P[0-9]+) 关键字匹配
# /post/1/ ====> 1满足正则规则的, 将pk=1
url(r'^post/(?P[0-9]+)/$' , views.detail, name='detail'),
]
为了方便地生成上述的 URL,我们在 Post 类里定义一个 get_absolute_url 方法,注意 Post 本身是一个 Python 类,在类中我们是可以定义任何方法的。
# blog/models.py
class Post(models.Model):
...
def __str__(self):
return self.title
# 自定义 get_absolute_url 方法
# 记得从 django.urls 中导入 reverse 函数
def get_absolute_url(self):
# reverse相当于Flask里面的url_for, 根据视图函数名称反向获取对应的路由地址.
# /post/1/
return reverse('detail', kwargs={'id': self.id})
第二步编写我们的 视图函数,按照惯例视图函数定义在 views.py 文件里:
# blog/views.py
from django.http import HttpResponse
from django.shortcuts import render, get_object_or_404
from blog.models import Post
def index(request):
posts = Post.objects.all()
#在这里视图函数Django和flask不一样的地方是形式和传参,
# context :传的参数以字典的形式
return render(request, 'blog/index.html', context={
'posts': posts
})
def detail(request, id):
#Post:是models中的Post类对象
post = get_object_or_404(Post, id=id)
return render(request, 'blog/detail.html', context={'post': post})
"""
flask 中的views与django views视图函数对比
@app.route('/')
def index():
return 'xxxx'
return render_template('xxxx.html', name=name, welcome=welcome)
@app.route('/post/'/)
def detail(id):
return id
"""
最后,我们前面建立了一个 urls.py 文件,并且绑定了 URL 和视图函数 index ,但是 Django 并不知道。Django 匹配 URL 模式是在BlogProject\ 目录(即 settings.py 文件所在的目录)的 urls.py 下的,所以我们要把 blog 应用下的 urls.py 文件包含到 BlogProject\urls.py 里去,打开这个文件看到如下内容:
# BlogProject\urls.py
from django.conf.urls import url
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
url(r'', include('blog.urls')),
]
在项目根目录(即 manage.py 文件所在目录)下建立一个名为 templates 的文件夹,用来存放我们的模板。
在 templates\ 目录下建立一个名为 blog 的文件夹,用来存放和 blog 应用相关的模板。
在 templates\blog 目录下建立一个名为 index.html 的文件
# templates/blog/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>欢迎来到博客首页</h1>
</body>
</html>
# templates/blog/detail.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{ posts.title }}
{{ posts.created_time }}
{{ posts.modified_time }}
{{ posts.body }}
</body>
</html>
在BlogProject/ settings.py 找到 TEMPLATES 和 STATIC_URL 选项,编写:
# blogproject/settings.py
TEMPLATES = [
{
...
'DIRS': [os.path.join(BASE_DIR, 'templates')],
...
},
]
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/
STATIC_URL = '/static/'
# 静态文件存放路径
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]
项目使用了从网上下载的一套博客模板。
当我们点击首页文章的标题
或者继续阅读
按钮后就会跳转到该篇文章对应的详情页面了。然而如果你尝试跳转到详情页后,你会发现样式是乱的。由于我们是直接复制的模板,还没有正确地处理静态文件。
你会发现在任何页面都是需要引入这些静态文件,如果每个页面都要修改会很麻烦,而且代码都是重复的。下面就介绍 Django 模板继承的方法来帮我们消除这些重复操作。
项目使用了从网上下载的一套博客模板。这里面除了 HTML 文档外,还包含了一些 CSS 文件和JavaScript 文件以让网页呈现出我们现在看到的样式。同样我们需要对 Django 做一些必要的配置,才能让 Django 知道如何在开发服务器中引入这些 CSS 和 JavaScript 文件,这样才能让博客页面的 CSS样式生效。
在 BlogProject应用下建立一个 static 文件夹。将 CSS 和 JavaScript 等静态文件复制到static目录中。
并将首页index.html和详情页detail.html复制到templates/blog中
修改静态文件的链接目录
{% load staticfiles %}
# 两种方式任选一种
<link rel="stylesheet" href="{% static 'css/pace.css' %}">
<link rel="stylesheet" href="/static/css/pace.css">
这里选择第二种
这里是static不是stasic
index.html 文件和 detail.html 文件除了 main 标签包裹的部分不同外,其它地方都是相同的,我们可以把相同的部分抽取出来,放到 base.html 里。首先在 templates\ 目录下新建一个base.html 文件。把 index.html 的内容全部拷贝到 base.html 文件里,然后删掉 main 标签包裹的内容。
base.html
文件修改如下:
顶行加载静态文件
首页和详情页不同之处:title处,标题不一样,所以空出来
body处,导航栏一样,不用修改
body处,导航栏下面左边部分不同,空出来;右边部分,首页最上方多了一块内容最新文章,空出来
index.html文件修改如下:
我们在文件最顶部使用 {% extends ‘base.html’ %} 继承 base.html ,这样就把 base.html 里的代码继承了过来,
在{% block title %}{% endblock %}包裹的地方填上 index 页面的标题
在 {% block main %}{% endblock %} 包裹的地方填上 index 页面应该显示的内容
在{% for post in posts %}{% endfor %}包裹的地方填上 index 页面应该显示的博客正文信息,用for循环来访问,代码更简洁.
用如下(类似jinja2)的语法,在前端获取后端的数据。
{% for post in posts %}{% endfor %} # posts:首页的视图函数传进来的posts对象
detail.html文件修改如下:
在{% block title %}{% endblock %}中填充标题内容
在 {% block main %}{% endblock main %} 里填充 detail.html 页面应该显示的内容,
在main中加入获取后端数据的代码,让其显示文章的实际数据:
在 {% block aside%}{% endblock aside%} 中填写 base.html 中没有的目录部分的内容。不过目前的目录只
是占位数据,我们在以后会实现如何从文章中自动摘取目录。
https://gitee.com/dadadaliuliuliiu/BlogProject/tree/master/templates
(base) F:\ziliao\python_kaifa\my_code\11_Django项目\BlogProject>git add *
(base) F:\ziliao\python_kaifa\my_code\11_Django项目\BlogProject>git commit
(base) F:\ziliao\python_kaifa\my_code\11_Django项目\BlogProject>git push origin master
Username for 'https://gitee.com': dadadaliuliuliiu
Password for 'https://[email protected]':
每次push或者pull的时候都要求输入账号和密码
git config --global credential.helper store
git本地仓库用户名和密码设置
git config --global user.name "Your Name"
git config --global user.email “email@example.com”
git提交到远程地址,
在 .git/config
目录里是配置文件,如果要修改上传地址,在这个文件中修改
为了让博客文章具有良好的排版,显示更加丰富的格式,我们使用 Markdown 语法来书写我们的博文。
Markdown 是一种 HTML 文本标记语言,只要遵循它约定的语法格式,Markdown 的渲染器就能够把我们写的文章转换为标准的 HTML 文档,从而让我们的文章呈现更加丰富的格式,例如标题、列表、代码块等等 HTML 元素。
将 Markdown 格式的文本渲染成标准的 HTML 文档是一个复杂的工作,已经被封装成了一个 Python 第三方库。安装 Markdown即可使用
pip install -i https://pypi.douban.com/simple markdown
将 Markdown 格式的文本渲染成 HTML 文本非常简单,只需调用这个库的 markdown 方法即可。我们书写的博客文章内容存在 Post 的 body 属性里,回到我们的详情页视图函数,对 post 的 body 的值做一下渲染,把 Markdown 文本转为 HTML 文本再传递给前端模板:
# blog/views.py
import markdown
from django.shortcuts import render, get_object_or_404
from .models import Post
def detail(request, id):
post = get_object_or_404(Post, id=id)
# 记得在顶部引入 markdown 模块
# extensions,它是对 Markdown 语法的拓展,这里我们使用了三个拓展
# 1). extra 本身包含很多拓展,
# 2). codehilite 是语法高亮拓展
# 3). toc 则允许我们自动生成目录
post.body = markdown.markdown(post.body,
extensions=[
'markdown.extensions.extra',
'markdown.extensions.codehilite',
'markdown.extensions.toc',
])
return render(request, 'blog/detail.html', context={'post': post})
发布的文章详情页没有看到预期的效果,而是类似于一堆乱码一样的 HTML 标签,这些标签本应该在浏览器显示它本身的格式,但是 Django 出于安全方面的考虑,任何的 HTML 代码在 Django 的模板中都会被转义(即显示原始的 HTML 代码,而不是经浏览器渲染后的格式)。为了解除转义,只需在模板标签使用 safe 过滤器即可,告诉 Django ,这段文本是安全的,你什么也不用做。
在模板中找到展示博客文章主体的 {{ post.body }} 部分,为其加上 safe 过滤器, {{ post.body|safe }} ,即可。
我们在渲染时使用了 codehilite 拓展,但这只是实现代码高亮的第一步,还需要简单的几步才能达到我们的最终目的。
安装了一下 Pygments 什么也没做,但 Markdown 使用 Pygments 在后台为我们做了很多事。如果你打开博客详情页,找到一段代码段,在浏览器查看这段代码段的 HTML 源代码,可以发现 Pygments
的工作原理是把代码切分成一个个单词,然后为这些单词添加 css 样式,不同的词应用不同的样式,这样就实现了代码颜色的区分,即高亮了语法。为此,还差最后一步,引入一个样式文件来给这些被添加了样式的单词定义颜色。
pip install -i https://pypi.douban.com/simple Pygments
在项目的 static\css\highlights\ 目录下应该能看到很多 .css 样式文件,这些文件是用来提供代码高亮样式的。选择一个你喜欢的样式文件,在 base.html 引入即可(别忘了使用 static 模板标签)。这里引入github.css 和 vim.css的样式,那么引入这个文件:
确保在渲染文本时添加了 markdown.extensions.codehilite 拓展。
确保安装了 Pygments。
确保代码块的 Markdown 语法正确。
确保用于代码高亮的样式文件被正确地引入。
博客侧边栏有四项内容:最新文章、归档、分类和标签云。这些内容相对比较固定,且在各个页面都会显示,如果像文章列表或者文章详情一样,从视图函数中获取然后传递给模板,则每个页面对应的视图
函数里都要写一段获取这些内容的代码,这会导致很多重复代码。更好的解决方案是直接在模板中获
取,为此,我们使用 Django 的一个新技术:自定义模板标签来完成任务。
在我们的 blog 应用下创建一个 templatetags 包。包含 init.py 文件,之后在 templatetags\ 目录下创建一个 blog_tags.py 文件,这个文件存放自定义的模板标签代码。
自定义模板标签代码写在 blog_tags.py 文件中。其实模板标签本质上就是一个 Python 函数,因此按照Python 函数的思路来编写模板标签的代码就可以了.
# blog/templatetags/blog_tags.py
from django import template
from ..models import Post
# 创建模板库对象
register = template.Library()
# 将函数 get_recent_posts添加到模板中
#这里我的理解: 之前是用户输入url,通过url.py文件寻找一个路由,在视图函数里,会调取数据库中Post类的内容。这里也相当于调取数据库中Post类的内容。
@register.simple_tag
def get_recent_posts(num=5):
#这里的时间计算: 从元年到寻找创建的时间,从小到大,加上负号,就是从大到小,那么取前num个
posts=Post.objects.all().order_by('-created_time')[:num]
return posts
和最新文章模板标签一样,先写好函数,然后将函数注册为模板标签即可。
# 归档的模板标签
@register.simple_tag
def archives():
#
dates = Post.objects.dates('created_time', 'month', order='DESC')
return dates
这里 dates 方法会返回一个列表,列表中的元素为每一篇文章(Post)的创建时间,且是 Python 的 date对象,精确到月份,降序排列。接受的三个参数值表明了这些含义,一个是 created_time , 即 Post 的创建时间, month 是精度, order=‘DESC’ 表明降序排列(即离当前越近的时间越排在前面)。例如我们写了 3 篇文章,分别发布于 2017 年 2 月 21 日、2017 年 3 月 25 日、2017 年 3 月 28日,那么 dates 函数将返回 2017 年 3 月 和 2017 年 2 月这样一个时间列表,且降序排列,从而帮助我们实现按月归档的目的。
@register.simple_tag
def get_categories():
# 别忘了在顶部引入 Category 类
return Category.objects.all()
@register.simple_tag
def get_tags():
return Tag.objects.all()
打开 base.html,为了使用模板标签,我们首先需要在模板中导入存放这些模板标签的模块,这里是blog_tags.py 模块。当时我们为了使用 static 模板标签时曾经导入过 {% load staticfiles %},这次在 {% load staticfiles %} 下再导入 blog_tags:
当时在model.py中定义了这个函数
然后找到最新文章、归档、分类、标签云列表处,把里面的列表修改一下:
这里必须写 {% get_recent_posts as recent_posts %} # 将get_recent_posts 重命名未recent_posts ,并把获取到的值付给recent_posts
加了新的文件一定要重新runserver
成功显示了最新文章列表、归档、分类、标签云等信息。
点击第一篇博客
侧边栏已经正确地显示了最新文章列表、归档、分类等信息。现在来完善归档和分类功能,当用户点击归档下的某个日期或者分类下的某个分类时,跳转到文章列表页面,显示该日期或者分类下的全部文章。点击右侧栏最新文章,跳转相关文章
#blog/urls.py
# /post/1/
urlpatterns = [
url(r'^$', views.index, name='index'),
# ^: 以什么开头, $是以什么结尾。 [0-9]指单个数字, +代表前面的字符出现1次或者多次。
# [0-9]+: 1, 2, 2344, 78888,
# (?P[0-9]+) 关键字匹配
# /post/1/ ====> 1满足正则规则的, 将id=1
url(r'^post/(?P[0-9]+)/$' , views.detail, name='detail'),
# 获取指定分类信息的路由
url(r'^category/(?P[0-9]+)/$' , views.category, name='category'),
url(r'^tag/(?P[0-9]+)/$' , views.tag, name='tag'),
url(r'^archives/(?P[0-9]{4})/(?P[0-9]{1,2})/$' , views.archives, name='archives')
]
使用了模型管理器(objects)的 filter 函数来过滤文章。由于是按照日期归档,因此这里根据文章发表的年和月来过滤。
# blog/views.py
def category(request, id):
"""根据分类的id显示该分类的所有博客信息"""
# 记得在开始部分导入 Category 类
cate = get_object_or_404(Category, id=id)
post_list = Post.objects.filter(category=cate).order_by('-created_time')
return render(request, 'blog/index.html', context={'posts': post_list})
def tag(request, id):
"""根据分类的id显示该分类的所有博客信息"""
# 记得在开始部分导入 Category 类
tag = get_object_or_404(Tag, id=id)
post_list = Post.objects.filter(tags=tag).order_by('-created_time')
return render(request, 'blog/index.html', context={'posts': post_list})
def archives(request, year, month):
# created_time__year, 创建日期是一个date对象, 获取日期的年份yesr属性, 用双下划线
post_list = Post.objects.filter(created_time__year=year,
created_time__month=month
).order_by('-created_time')
return render(request, 'blog/index.html', context={'posts': post_list})
在模板找到各列表部分的代码,修改超链接的 href 属性,让用户点击超链接后跳转到文章归档页面:
为什么要使用 {% url %} 模板标签呢?
我们把超链接的 href 属性设置为 /archives/{{ date.year }}/{{ date.month }}/ 同样可以达到目的跳转页面,但是这种写法是硬编码的。
虽然现在 archives 视图函数对应的 URL 模式是这种形式,但是如果哪天这个模式改变了呢?如果使用了硬编码的写法,那你需要把每一处 /archives/{{ date.year }}/{{ date.month }}/ 修改为新的模式。但如果使用了 {% url %} 模板标签,则不用做任何修改。
简单来说, 就是Web项目的路由地址变化, 不需要去前端页面进行修改链接地址。便于代码的维护。
annotate
方法在我们的博客侧边栏有分类列表,显示博客已有的全部文章分类。现在想在分类名后显示该分类下有多少篇文章,该怎么做呢?最优雅的方式就是使用 Django 模型管理器的 annotate
方法。
#blog/templatetags/blog_tags.py
from django.db.models.aggregates import Count
from blog.models import Category
@register.simple_tag
def get_categories():
# 记得在顶部引入 count 函数
# Count 计算分类下的文章数,其接受的参数为需要计数的模型的名称
return Category.objects.annotate(num_posts=Count('post')).filter(num_posts__gt=0)
现在在 Category 列表中每一项都新增了一个 num_posts 属性记录该 Category 下的文章数量,我们就可以在模板中引用这个属性来显示分类下的文章数量了。
如何精确地记录一篇文章的阅读量是一个比较复杂的问题,不过对于我们的博客来说,没有必要记录的那么精确。因此我们使用一种简单但有效的方式来记录博客文章的阅读量:文章每被浏览一次,则其阅读量 +1,即所谓的文章页面 PV(Page View)数。虽然简单粗暴,但却高效实用。
# blog/models.py
class Post(models.Model):
# 新增 views 字段记录阅读量
views = models.PositiveIntegerField(default=0)
一旦用户访问了某篇文章,这时就应该将 views 的值 +1,这个过程最好由 Post 模型自己来完成,因此再给模型添加一个自定义的方法:
# blog/models.py
class Post(models.Model):
# 新增 views 字段记录阅读量
views = models.PositiveIntegerField(default=0)
def increase_views(self):
self.views += 1
self.save(update_fields=['views'])
一旦更改了模型,就需要迁移数据库,以便让 Django 将更改反应到数据库中。
python manage.py makemigrations
python manage.py migrate
# blog/views.py
def detail(request, pk):
post = get_object_or_404(Post, pk=pk)
# 阅读量 +1
post.increase_views()
# ...........
在模板中显示阅读量和显示其它字段一样,只需要使用模板变量即可。即模板适当的地方使用 {{post.views }} 模板变量。这里我们分别修改两个地方,分别是 index.html 和 detail.html。
关于可以写自己的简历
点击首页进入index
点击联系后进入我的github
(base) F:\ziliao\python_kaifa\my_code\11_Django项目\BlogProject>git add *
(base) F:\ziliao\python_kaifa\my_code\11_Django项目\BlogProject>git commit -m "add category archives tag .."
(base) F:\ziliao\python_kaifa\my_code\11_Django项目\BlogProject>git push origin master
Username for 'https://gitee.com': dadadaliuliuliiu
Password for 'https://[email protected]':
在部署项目前,编写README.md文件
并将项目环境所需的包导入requirements.txt文件
F:\ziliao\python_kaifa\my_code\11_Django项目\BlogProject>pip freeze > requirements.txt