MVC是一种使用MVC(Model View Controller 模型-视图-控制器)设计创建Web应用程序的模式:
对于Django,它有另一套差不多的模式:MTV模式
在命令提示符(cmd)中输入命令行django-admin
可查看常用命令。
startproject
startapp
runserver
shell
makemigrations
migrate
在命令提示符输入命令行:django-admin startproject xxx
,xxx
是你想要创建的项目名。例如:
目录与文件 | 说明 |
---|---|
init.py | 空文件,表示当前目录是一个包 |
setting.py | 整个项目的配置文件 |
urls.py | 项目的URL配置文件(路由配置),用于配置用户请求的URL与View模块中函数的对应关系 |
wsgi.py | 项目与支持WSGI协议的Web服务器对接的入口文件 |
manage.py | 项目的入口文件 |
进入项目文件夹,输入命令:python manage.py runserver
,然后在浏览器的地址栏输入http://127.0.0.1:8000/
,如果看到下面这个页面说明Django项目运行成功。
每一个项目可划分为若干个子模块,一个子模块可视为一个应用app。创建Django应用的命令:python manage.py startapp xxx
,其中xxx
就是你想要创建的应用名,例如:
创建blog应用
之后,项目目录变为:
其中,
db.sqlite3
是在项目测试运行(runserver
)时生成的一个默认的后台连接的数据库,目录下的blog
文件夹就是刚生成的新的应用。注意,用命令提示符cmd生成项目及应用的时候Template模板文件需要自己创建,用Pycharm中的终端进行创建时会自动生成。
下面对生成的应用blog
进行某些目录分析:
目录与文件 | 说明 |
---|---|
models.py | Model模型,用来构建和操作Web应用中的数据 |
Views.py | View视图,负责接受用户请求,进行业务处理,并返回响应 |
admin.py | Django自带的后台管理工具 |
创建应用完成之后要在settings.py
中添加应用,如下图位置:
需要注意的是在'bolg'
后面别忘了加,
,以便下次新增应用。
要想输出HelloWorld
需要在views.py
中自定义一个函数用来输出。
views.py
:
from django.shortcuts import render
from django.http import HttpResponse
def Hello(request):
return HttpResponse('Hello World')
接着需要配置URL(即打开浏览器在地址栏输入什么URL才会调用HelloWorld函数):
urls.py
:
from django.contrib import admin
from django.urls import path,include
from blog import views
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/hello/',views.hello),
]
配置好之后,在终端输入命令:python manage.py runserver
开启服务器,然后到浏览器地址输入http://127.0.0.1:8000/blog/hello/
,成功的页面如下所示:
这里需要考虑另一个很重要的问题就是:当URL出现过多或者app中出现同名函数造成路由混乱了怎么办?为了解决这个问题,可以将路由分解为项目路由,应用路由。改进如下:
blog_yyz
下的urls.py
(项目路由):
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', include('blog.urls')),
]
blog
下的urls.py
(需要自己新建,应用路由):
from django.urls import path,include
from blog import views
urlpatterns={
path('hello/',views.hello)
}
实际的地址http://127.0.0.1:8000/blog/hello/
由上面两部分路由共同组成。
在上面HelloWorld的例子中,返回的仅仅是一个字符串,但是在实际项目中返回的应该是一个页面,也就是模板T,里面可以对数据进行动态的渲染。Django模板语言:DTL,带有特殊语法的HTML文件,可以传递View视图中的参数,实现数据动态化。
渲染模板(传递参数):
from django.shortcuts import render
def index(request):
return render('request','xxx.html',{字典传递参数})
from django.shortcuts import render
from django.http import HttpResponse
def hello(request):
return HttpResponse('Hello World')
def index(request):
return render(request,'index.html')
blog
下的urls.py
:
from django.urls import path,include
from blog import views
urlpatterns={
path('hello/',views.hello),
path('index/',views.index)
}
在浏览器地址栏中输入地址http://127.0.0.1:8000/blog/index/
即可访问到以下页面:
Django中的一个模型类就对应着数据库中的一张数据表,对模型类的操作就是对数据库表的操作。引入模型层的原因就是:因为数据库存在多样性,因此如果同一个程序要连接管理不同的数据库,语法会有差异,引入模型类就能实现统一的管理,大大方便了我们对数据库的操作。
在settings.py
中的DATABASES
字段配置了项目所用到的数据库,如下图:
在本次的实验项目中,可以看到用的数据库是db.sqlite3
(本地的轻量级数据库),也就是默认的后台数据库,一开始运行项目时所生成的那个文件。
博客文章大概可分为四部分:文章标题,文章摘要,文章内容,文章ID
models.py
中的一个类就对应数据库中的一张表,类中的一个对象就对应数据库表中的一个字段。
from django.db import models
class Article(models.Model):
article_id=models.AutoField(primary_key=True)
title=models.TextField()
abstract=models.TextField()
content=models.TextField()
生成迁移文件
编写完models.py
之后,要想在数据库db.sqlite3
中生成对象的表和字段,需要生成迁移文件并执行,迁移的目的是通过Django的ORM系统将定义在模型类中的字段转换成对应的SQL语句。执行命令为:python manage.py makemigrations
,执行完之后会在migrations
目录下增加0001_initial.py
文件。
执行迁移文件
执行迁移文件数据库生成相应数据库表与字段,执行命令为:python manage.py migrate
。
执行完之后,接下去就是验证是否生成了对应的数据库的表及各字段,需要用到软件:Navicat Premium:
在软件内建立与sqlite
的连接,选择本地文件(就是Django博客项目下的db.sqlite3
文件),用户名为root
,不需要输入密码,打开数据库查找到blog_article
这张表即可看到对应的四个字段已经生成。如下图:
交互式的编程方式,类似与python shell。继承Django环境,可临时性操作和小范围的Debug。不需要运行整个项目来测试。
命令:python manage.py shell
实例:使用Django shell创建博客文章。
首先在命令行输入python manage.py shell
:
接着依次输入以下几条命令来创建一个实例化对象:
from blog.models import Article
a=Article()
a.title='随笔'
a.abstract='笔记'
a.content='......'
a.save()
如下图:
接着去Navicat Premium
中对blog_article
这张表进行刷新可以看到数据已经添加成功了:
articles=Article.objects.all()
,接着利用for循环
打印输出信息,如下图(这里我只存了一条信息,如果表中存了多条数据打印出来的就是全部文章的标题title):对象.delete()
能够将数据库sqlite中的数据打印输出了之后,现在就需要将数据渲染到一个动态的数据页面index.html
上去,达到最终目的。
静态页面index.html来自于Bootstrap,部分代码如下图:
Django模板语法:
{% for ... in ...%}
...
{% endfor %}
{% if %}
...
{% elif %}
...
{% else %}
{% 变量 %}
采用Django模板语言(DTL)改写之后:
index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>我的第一个Django Web项目</title>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>
<body>
<div class="container page-header">
<h1>第一个Django项目-极简博客
<small>--by yyz</small>
</h1>
</div>
<div class="container page-body">
<div class="col-md-9" role="main">
<div class="body-main">
<div>
{% for article in articles %}
<h2>{{article.title}}</h2>
<p>
{{article.content}}
</p>
{% endfor %}
</div>
</div>
</div>
<div class="col-md -3" role="complementary">
<div>
<h2>最新文章</h2>
{% for article in articles %}
<h4><a href="#">{{article.title}}</a></h4>
{% endfor %}
</div>
</div>
</div>
</body>
</html>
views.py
也需要改写(将index.html中的循环变量articles做为参数传过去):
from django.shortcuts import render
from django.http import HttpResponse
from blog.models import Article
def hello(request):
return HttpResponse('Hello World')
def index(request):
articles=Article.objects.all()
return render(request,'index.html',{'articles':articles})
用Django模板语法先对detail.html
进行改写:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>我的第一个Django Web项目</title>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>
<body>
<div class="container page-header">
<h1>{{ curr_article.title }}
</h1>
</div>
<div class="container body-main">
<div>
<p>
{{ curr_article.content }}
</p>
</div>
<div>
<nav aria-label="...">
<ul class="pager">
<li><a href="/blog/detail/{{ p_article.article_id}}">上一篇:{{ p_article.title }}</a></li>
<li><a href="/blog/detail/{{ n_article.article_id}}">下一篇:{{ n_article.title }}</a></li>
</ul>
</nav>
</div>
</div>
</body>
</html>
接着编写视图函数views.py
中的对应detail方法:
def get_detail_page(request):
curr_article=Article.objects.all()[0] #[0]表示拿到第一篇文章
return render(request,'detail.html',{
'curr_article':curr_article #curr_article就是你想要查看详细内容的那篇文章
})
当然还需要配置URL:
在应用路由blog下的urls.py
添加一条路由(项目路由不需要配置):
path('detail/',views.get_detail_page),
接着在浏览器地址栏中输入:http://127.0.0.1:8000/blog/detail
就能访问到第一篇文章的详情页面了。
当然,这种做法每次需要在代码输入指定的文章下标[]
才能去进行访问,显然是不科学的,最好的方法就是在浏览器地址栏中直接输入对应文章的id就能访问到指定文章的详情页面,就比如输入http://127.0.0.1:8000/blog/detail/1
,就能访问到第二篇文章。
所以就需要更改一下路由配置urls.py
:
path('detail/' ,views.get_detail_page),
对应的视图函数views.py
:
def get_detail_page(request,article_id):
all_article=Article.objects.all()
for article in all_article:
if article.article_id==article_id:
curr_article=article
break
return render(request,'detail.html',{
'curr_article':curr_article #curr_article就是你想要查看详细内容的那篇文章
})
这样就实现了在地址栏可以输入http://127.0.0.1:8000/blog/detail/下标
来访问对应下标的文章详情页面。
想要实现在文章列表中点击对应的文章超链接或标题可以跳转到指定文章的详情页面。
index.html
:
<div>
{% for article in articles %}
<h2><a href="/blog/detail/{{ article.article_id }}" >{{article.title}}</a></h2>
<p>
{{article.content}}
</p>
{% endfor %}
</div>
...
<div>
<h2>最新文章</h2>
{% for article in articles %}
<h4><a href="/blog/detail/{{ article.article_id }}">{{article.title}}</a></h4>
{% endfor %}
</div>
...
但是详情页面又显得非常单调,除了标题和内容就没其他元素了。可以在详情页面加上一组按钮,用来实现跳转上一篇或者下一篇的功能。
Bootstrap 翻页功能组件
detail.html
:
当然这里另外需要增加显示的是在上一篇下一篇按钮处显示对应上一篇或者下一篇文章的题目:
<div>
<p>
{{ curr_article.content }}
</p>
</div>
<div>
<nav aria-label="...">
<ul class="pager">
<li><a href="/blog/detail/{{ p_article.article_id}}">上一篇:{{ p_article.title }}</a></li>
<li><a href="/blog/detail/{{ n_article.article_id}}">下一篇:{{ n_article.title }}</a></li>
</ul>
</nav>
</div>
接着就需要在views.py
中找到上一篇和下一篇文章并传过来,具体思路就是先要找到当前这篇文章的下标索引值index
,那么它的上一篇文章的下标就是index-1
,下一篇文章的下标就是index+1
,当然当当前文章是第一篇时,它是没有上一篇的;当前文章是最后一篇时,它是没有下一篇的。
def get_detail_page(request,article_id):
all_article=Article.objects.all()
p_index=0 #前一篇文章的下标
n_index=0 #后一篇文章的下标
for index,article in enumerate(all_article): #enumerate函数的作用是为迭代对象添加索引,索引下标从0开始
if index==0: #当前文章是第一篇
p_index = 0
n_index = index+1
elif index==len(all_article)-1: #当前文章是最后一篇
p_index = index-1
n_index = index
else:
p_index = index-1
n_index = index + 1
if article.article_id==article_id:
curr_article=article
p_article=all_article[p_index]
n_article = all_article[n_index]
break
return render(request,'detail.html',{'curr_article':curr_article,'p_article':p_article,'n_article':n_article})
还是一样去刚才的Bootstrap复制分页的html代码,并把它放到index.html
中。
<div>
<nav aria-label="Page navigation">
<ul class="pagination">
<li>
<a href="/blog/index?page={{previous_page}}" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<% for num in page_num%>
<li><a href="/blog/index?page={{num}}">{{num}}</a></li>
{% endfor %}
<li>
<a href="/blog/index?page={{next_page}}" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</div>
目前在网页上常见的浏览不同页面的方法就是在地址栏中输入http://127.0.0.1:8000/blog/index?page=1
,通过获取到地址栏的中page
参数的值来显示对应的页面。语句为:
page=int(request.GET.get('page')) #获取到的page是字符串类型的,需要进行转换。
具体的分页需要用到Django的一个模块:Paginator
。具体代码及函数如下:
from django.core.paginator import Paginator as pr
def index(request):
page=request.GET.get('page')
if page: #这里代码的目的是为了防止第一次输入时地址出错就默认跳转到第一页
page=int(page)
else:
page=page
all_articles = Article.objects.all()
paginator=pr(all_articles,3) #每一页三篇文章
page_num=paginator.num_pages #总页数
page_article_list=paginator.page(page)
if page_article_list.has_next(): #has_next():是否有下一页
next_page = page + 1
else:
next_page = page + 1
if page_article_list.has_previous(): #has_previous():是否有上一页
previous_page = page - 1
else:
previous_page = page
return render(request,'index.html',{
'articles':page_article_list,
'curr_page':page, #当前页
'previous_page':previous_page, #上一页
'next_page':next_page, #下一页
'page_num':range(1,page_num+1), #各个页码的列表
})
Django Admin 模块是Django自带的的后台管理工具,可读取模型数据,提供强大的管理使用页面。
创建admin用户:python manage.py createsuperuser
,然后输入我们的用户名和密码:
后台管理的URL是http://127.0.0.1:8000/admin
,不需要我们手动配置,直接可以访问。
在访问时,如果出现
TypeError: 'set' object is not reversible
的报错,报错的原因是“设置”对象不可逆。只需要将urls.py
中的urlpatterns
改为[]
而不是{}
即可。
访问成功的页面如下:
输入自己刚才设置的用户名和密码进行登录:
第一次登录是是看不到BLOG
下的Articles
的,这时候需要在admin.py中注册模型:
from blog.models import Article
admin.site.register(Article)
如果想要设置语言为中文,可以在settings.py
中修改:
LANGUAGE_CODE
是设置语言环境,TIME_ZONE
设置时区。
LANGUAGE_CODE = 'zh-Hans' #修改语言为中文
TIME_ZONE = 'Asia/Shanghai' #修改时区
# Python和Django的版本问题,可能会导致退出服务
Django的MTV模式中的的模型model采用的是ORM模型的方式:对象关系映射,通过类的方法操作数据库,不再写原生的SQL语句,把表映射成类,把行作实例,把字段作属性。(Django在操作数据库时可以忽视数据库的类型,仅仅是操作某个类就可以实现对数据库中表,字段的操作)。这种模式的优点就是:性能损耗小,设计灵活,可移植性(轻松切换数据库)。
可以在MySql中直接创建,也可以用Navicat连接到MySql并创建。
在连接上右键选择新建数据库:
创建好了之后就需要在seetings.py
中配置数据库连接。
seetings.py
:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME':'blogdb',
'USER':'root',
'PASSWORD':'182584',
'HOST':'127.0.0.1',
'PORT':'3306'
}
}
即便是这样配置好了,项目还是无法执行,因为对应的迁移文件还没有创建。并且,Django操作Mysql数据库需要一个驱动程序—pymysql库,首先先下载pymysql第三方库。然后,在整个项目的
_init_.py
中添加如下代码:
import pymysql
pymysql.version_info = (1, 3, 13, "final", 0) //这句话是为了解决Django3与pymysql不兼容的问题
pymysql.install_as_MySQLdb()
创建迁移文件之前,需要将之前sqlite的迁移文件删除掉,即下图中的0001_initial.py
:
然后执行命令python manage.py makemigrations
来创建迁移文件:
执行python manage.py migrate
来执行迁移文件(看到一连串的OK就说明执行成功了):
可以看到Mysql中的blogdb数据库中已经多了一张表blog_article:
这个时候再去启动项目服务可以看到在地址http://127.0.0.1:8000/blog/index/?page=1
中已经出现页面,但是还没有数据显示,因为我们压根没有往表中添加数据:
可以选择执行sql文件或者手动添加数据,数据添加完了之后刷新页面就有文章可见了:
在视图函数views.py
中,每个函数都有一句返回语句:
from django.shortcuts import render
...
def index(request):
...
return render(request,'index.html',{字典传递参数})
像上图中的
index.html
,写的时候并没有指定具体的路径,仅仅是写了文件名,并且index.html
这些html
文件都放在blog/templates
(应用/模板)下。其中的原因就是在seetings.py
文件中:
'DIRS':[]
:(如果你想执行html
文件)到指定路径下查找。
APP_DIRS:True
:到app(视图函数对应的app)的templates目录下查找。
先按DIRS找,然后找已安装的app的templates目录,再找其他app(选择有先后次序)。当然在seetings.py中已经添加了应用blog
:
所以在render的参数中,index.html
不需要再加什么路径,它会自己找到所在app
下的templates
中去寻找。当然,如果index.html
并不是直接放在templates
目录下,而是放在templates/aa
文件夹下,那么在render中的路径就要改为aa/index.html
,否则就会报错了。
如果有多个应用同时使用到模板文件,比如index.html
,那么这些模板文件都可以放到整个项目的templates
文件夹(自己新建)下,在所有的应用中的视图函数views.py
都可以方便使用。如果templates
放到了整个项目文件夹下,需要进行配置才可以使用。配置方法如下:
在settings.py
中配置:
'DIRS': [os.path.join(BASE_DIR,'templates')]
BASE_DIR
在项目中原本就有配置,就是整个项目的路径:
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
一个网页中,除了html骨架外,还有css样式文件,js执行文件,以及图片等文件。
blog/static/blog
STATICFILES_DIRS=[os.path.join(BASE_DIR,'static')]
如下图在应用blog文件夹下新建一个文件夹static,将静态文件(图片等)放到里面。
为了避免与原index.html
混淆,新建一个index2.html
,然后写入以下代码:
{% load static %} #load标签加载静态文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<img src="{% static '20190309160339403.gif' %}" alt=""> #这里只需要写文件名,因为在settings.py中已经设置了应用静态文件的路径为blog/static
</body>
</html>
因为有了一个新的页面,因此还需要配置视图函数,在之前的hello视图函数的基础上改写:
def hello(request):
return render(request,'index2.html')
在地址栏中输入:http://127.0.0.1:8000/blog/hello/
就可以访问到添加了静态页面之后的页面:
当有多个应用在使用静态文件的时候,假如两张图片重名了,那么为了避免寻找出错,我们需要在每个应用的static文件夹下再新建一个和应用同名的文件夹
然后在页面index2.html中将文件名改成地址+文件名:,这样就可以避免应用静态文件重名的问题。
同样的,如果静态文件是多个应用会用到的,那么就在整个项目文件夹下新建一个static
文件夹,然后放入静态文件,在settings.py
中写入语句:STATICFILES_DIRS=[os.path.join(BASE_DIR,'static')]
,index2.html
中img的src只需要写一个文件名就可以了。
Django(博客系统实例)到此结束!!!