18.1 建立项目
1、制定规范
说明项目的目标,阐述项目功能,讨论项目外观和用户界面。
2、建立虚拟环境
要使用Django,首先需要建立一个虚拟工作环境。虚拟环境是系统的一个位置,可以在其中安装包,并将其与其他Python包隔离。
创建目录learning_log并在目录中创建虚拟环境 ---- learning_log$ python -m venv ll_env
3、安装virtualenv
4、激活虚拟环境
learning_log$ source ll_env/bin/activate (ll_env)learning_log$
如上所示,环境被激活,可以在环境中安装包,或使用已安装的包,并且此类包仅在该环境活动状态时才能用。
要停止使用虚拟环境,可执行命令deactivate.
5、安装Django
在虚拟环境中安装Django。
6、在Django中创建项目
(ll_env)learning_log$ django-admin.py startproject learning_log .
创建一个名为learning_log的项目, ---- 注意末尾的句点不要忘记,涉及到目录结构和程序部署服务器。
该命令创建了一个learning_log目录,一个manage.py文件,同时learning_log目录下有settings.py urls.py和wsgi.py三个文件。
manage.py该文件接受命令并将其交给Django的相关部分去运行。我们将用来管理如使用数据库和运行服务器等任务。
settings.py指定Django如何与你的系统交互以及如何管理项目。
urls.py告诉Django应创建哪些网页来响应浏览器请求。
wsgi.py帮助Django提供它创建的文件 ---- wsgi全称:web server gateway interface(Web服务器网关接口)。
7、创建数据库
Django将大部分与项目相关的信息都存储在数据库中,因此需要创建一个数据库。在活动虚拟环境下执行下面命令:
(ll_env)learning_log$ python3 manage.py migrate
首次执行命令migrate时,将让Django确保数据库与项目的当前状态匹配。在新项目中,该命令创建一个数据库。
Synchronize unmigrated apps ---- 同步未迁移的应用程序
Apply all migrations ---- 应用所有的迁移
Django又创建了一个文件db.sqlite3,这是一种使用单个文件的数据库,是编写简单应用程序的理想选择。
8、查看项目
(ll_env)learning_log$ python3 manage.py runserver --snip-- System check identified no issues (0 silenced) #通过检查确认正确的创建了项目。 Django version --snip-- #指出使用的版本和当前设置文件名称。 Starting development server at http://127.0.0.1:8000/ #指出项目URL,当前在本地8000端口侦听请求。
执行runserver命令,在网页中输入本地URL,http://localhost:8000/ 或者http://127.0.0.1:8000/ ---- 看是否成功,不成功换端口。
18.2 创建应用程序
Django项目由一系列应用程序组成,它们协同工作,让项目称为一个整体。
在活动的虚拟环境中执行命令startapp appname
(ll_env)learning_log$ python3 manage.py startapp learning_logs
创建了文件夹learning_logs,其项下有admin.py __init__.py migrations models.py tests.py views.py几个文件。
1、定义模型
打开models.py,在代码层模型就是一个类,在该文件中创建我们需要的模型。
创建的主题模型(class Topic)的属性设置类似数据库中的数据设置。CharField(max_length=200) DateTimeField(auto_now_add=True)都类似。
要获悉可在模型中使用的各种字段,参阅Django Model Field Reference ---- Model field reference | Django documentation | Django
class Topic(models.Model): #创建一个主题的类,也就是模型,继承Model类--也是Django中一个定义了模型基本功能的类。 text = models.CharField(max_length=200) #定义了属性text的类型为字符,最长长度不超过200个字符。 date_added = models.DateTimeField(auto_now_add=True) #定义了属性日期,并且传递了实参,每当用户创建新主题时自动设置当前日期。 def __str__(self): #该方法是,当调用该类时会显示什么内容,它的return值就是被调用时显示的内容。 return self.text #在本案例中就是主题模型被调用时会显示text属性的内容。
2、激活模型
要使用模型,必须让Django将应用程序包含到项目中。
在settings.py中将应用程序文件learning_logs填加进INSTALLED APPS变量下。
接下来让Django修改数据库,使其能够存储与模型Topic相关的信息。
(ll_env)learning_log$ python3 manage.py makemigrations learning_logs
命令makemigrations让Django确定该如何修改数据库,输出表明Django创建了一个0001_initial.py的迁移文件,这个文件将在数据库中为模型Topic创建一个表。
(ll_env)learning_log$ python3 manage.py migrate
该命令与首次创建数据库命令一样,但是输出部分会增加显示learning_logs应用迁移时一切正常。
每当需要修改“学习笔记”管理的数据时,都采取如下三个步骤:修改models.py;对learning_logs调用makemigrations;让Django迁移项目。
3、Django管理网站
1.创建超级用户
(ll_env)learning_log$ python3 manage.py createsuperuser
使用该命令创建超级用户,Django会提示输入超级用户的用户名,邮箱地址,密码。
Django并不存储用户的密码,而是存储该密码派生出来的一个字符串--散列值。
2.向管理网站注册模型
在learning_logs文件夹内的admin.py中注册模型Topic。
from learning_logs.models import Topic admin.site.register(Topic)
使用超级用户访问网站:http://localhost:8000/admin/,Django默认设置user和group可以由超级用户进行管理,还可管理Topic模型数据。
如果打开显示错误,则通过runserver来激活环境。
3.添加主题
4、定义模型Entry
为用户在学习笔记中添加的条目定义模型。每个条目都与特定的主题相关联,即多个条目可关联到同一主题。
在models.py中添加Entry类:
class Entry(models.Model): #创建Entry类 #这里下面的属性均没有用self模式,这个怎么理解。 topic = models.ForeignKey(Topic,on_delete=models.CASCADE) #调用的是数据库语言中外键的级联删除的意思,具体再研究 text = models.TextField() #文本字符串不设置最长字符数 date_added = models.DateTimeField(auto_now_add=True) #自动加时间戳 class Meta: #在Entry类下面嵌套Meta类,存储用于管理模型的额外信息,看不懂??? verbose_name_plural = 'entries' def __str__(self): #默认输出的方法 return self.text[:50]+"..." #返回文本的前50个字符串,加后面三个点。
5、迁移模型Entry
由于添加了新的模型,需要再次迁移数据库。生成新的迁移文件0002_entry.py。
(ll_env)learning_log$ python3 manage.py makemigrations learning_logs (ll_env)learning_log$ python3 manage.py migrate
6、向管理网站注册Entry
在admin.py文件中注册Entry.
7、Django shell
shell用于在活动的虚拟环境中用终端了解和测试项目的数据。
(ll_env)learning_log$ python3 manage.py shell #启动解释器 >>> from learning_logs.models import Topic #导入Topic模型 >>> Topic.objects.all() #该方法可获取模型的所有实例,返回一个列表, [<Topic:Chess>,<Topic:Rock Climbing>] #称为查询集。 >>> topics = Topic.objects.all() #将返回的查询集存储在topics中, >>> for topic in topics: #遍历topics,可以打印主题id和字符串。 print(topic.id,topic) >>> t = Topic.objects.get(id=1) #获取id为1的主题对象 >>> t.text #调用属性 >>> t.date_added #调用属性 >>> t.entry_set.all() #通过外键关系获取数据,使用相关模型的小写加上_set,来获得该主题下外键所有的输出显示。
每次修改模型后,都需要重启shell才能看到修改结果。要退出shell的话采用ctrl+D。
18.3 创建网页:学习笔记主页
使用Django创建网页的过程通常分为三个阶段:定义URL、编写视图和编写模版。
每个URL都映射到特定的视图。
1、映射URL
首先将基础URL映射到“学习笔记”的主页,目前是默认返回Django的网站。
打开urls.py
添加一行代码来包含模块learning_logs.urls.代码包含name可以与项目中的其他URL区分开来。
from django.conf.urls import include, url from django.contrib import admin urlpatterns = [ #在Django3.1以上版本需要用以下格式,而跟之前的不一样,主要体现在admin.site.urls不用include()函数;第二个用到include()函数的 #第一个实参需要传递一个2元元组,一个参数为url的模式,一个为app_name即应用程序名称。 url(r'^admin/', admin.site.urls), url(r'',include(('learning_logs.urls','learning_logs'), namespace='learning_logs')), ]
同时,我们在learning_logs文件夹中创建另一个urls.py
#定义learning_logs的URL模式。 ----- 该备注表明以下代码确定 from django.conf.urls import url #导入函数urls,用来将URL映射到视图 from . import views #导入模块views,其中句点让python从当前py文件所在文件夹中导入视图。那就是learning_logs中的views模块。 urlpatterns = [ #该变量包含可在learning_logs中请求的网页。 url(r'^$', views.index, name='index'), #以下详解url()函数的各个参数。。。 ]
详解:r'^$' ---- 正则表达式r表示原生字符串,^$表示让python查找开头为空,结尾为空的字符串,也就是排除所有URL。因为python会忽略基础URL,所以该url函数查找匹配的唯一只能是基础URL.也就是匹配基础URL地址之后开头和结尾都为空的字符串。^$一起用有点难理解。
views.index ---- 指定了要调用的视图函数,请求的URL与前述正则表达式相匹配时,Django将调用views.index这个视图。
name='index' ---- 第三个实参,将这个URL模式命名为index,方便其他调用。
2、编写视图
视图函数接受请求中的信息,准备好生成网页所需的数据,再将这些数据发送给浏览器。
打开views.py,在其中定义index()函数。
def index(request): #request是什么意思,就是前面urls.py中的url函数第一个实参传递到这个request里面。 return render(request,'learning_logs/index.html') #两个实参,一个实原始请求对象;一个可用于创建网页的模版。
3、编写模版
在learning_logs文件夹中新建一个文件夹,命名templates;在templates中再新建文件夹learning_logs文件夹,在该文件夹中再新建index.html文件。
注:按照这个新建的规则,这个index.html路径为learning_logs/templates/learning_logs/index.html。但上面的index()函数里用的为何是这样的相对位置。
18.4 创建其他网页
1、模版继承
在创建网站时,把几乎每个网页都有的元素的网页模版做成父模版,其他子模版都从其继承。
1、父模板
首先创建一个base.html模版,存储在index.html所在的目录中。将每个页面都有的元素作为顶端的标题,设置为到主页的链接:
<p> <a href="{% url 'learning_logs:index' %}">Learning Log</a> </p> {% block content %}{% endblock content %} #块标签,块名为content,是一个占位符。
{% %} ---- 表示一个模版标签,是一小段代码,生成要在网页中显示的信息。
在HTML页面中,链接是使用锚标签定义的。 ---- link text
让模版标签来生成URL,如果要修改项目的URL,只需修改urls.py中的URL模式。
2、子模版
重写index.html,使其继承base.html。
{% extends "learning_logs/base.html" %} {% block content %} <p>Learning Log helps you keep track of your learning, for any topic you're learning about.</p> {% endblock content %}
相对于原来的index.html。标题Learing Log改成了从base.html继承。子模版第一行必须包含{% extends %},表明继承路径。
第二行插入名为content的{% block %}标签,不是从父模版继承的都在该标签中输入。
模版继承的优点,只要在子模版中描述该网页特有的内容即可。
2、显示所有主题的页面
所有主题页面显示用户创建的所有主题,它是第一个需要使用数据的网页。
1、URL模式
首先定义一个显示所有主题页面的URL。比如显示所有topics,就使用http://localhost:8000/topics/。在urls.py中添加,
url(r'^topics/$', views.topics, name='topics') #正则匹配topics的链接,在views中添加topics()函数,把名字定义为topics方便调用。
2、视图
在views.py中添加topics()函数。下列为增加部分:
from learning_logs.models import Topic #导入models模块中与数据相关的主题模型 def topics(request): #设定一个函数,形参为让Django从服务器里收到的request对象,这个要再理解一下。 topics = Topic.objects.order_by('date_added') #让主题topic的项目对象按时间升序排序。 context = {'topics':topics} #给模版发送一个上下文,为字典,键为我们将在模版中用来访问数据的名称,值为要发送的数据。 return render(request, 'learning_logs/topics.html', context) #根据我的理解,context应该是传递给这个html文件的,也就是该键值对在html文件中根据键来对应变量值。
3、模板
创建topics.html模板,并且在index.html同一目录下。需要接受字典context,以便能够使用topics()函数提供数据。
首先使用标签{% extends %}来继承base.html
再开始定义content块。标准HTML语言,其中有类似for语言,以及调用变量,以及emtpy的显示,最后用endfor结束。
{% extends "learning_logs/base.html" %} #继承base.html {% block content %} #编写content块 <p>Topics</p> #输出Topics字样 <ul> #输出项目列表,即无序列表的标签 {% for topic in topics %} #for语言,从topics列表中遍历。但是这个变量topics怎么能调用的到?? <li>{{ topic }}</li> #<li></li>表示一个项目列表项,在模板中打印变量,需要将变量名用双花括号括起来。 {% empty %} #循环为空值时,类似for循环中的else。 <li>No topics have been added yet.</li> #另一个项目列表项。 {% endfor %} </ul> {% endblock content %}
4、base.html
在主页的链接后面添加了一个连字符,生成一个topics相匹配的URL链接。
<a href="{% url 'learning_logs:index' %}">Learning Log</a> - <a href="{% url 'learning_logs:topics' %}">Topics</a>
3、显示特定主题的页面
1、URL模式
如要显示主题为Chess的页面,其在topics里面的id为1.则在app下的urls.py中输入代码:
url(r'^topics/(?P<topic_id>\d+)/$', views.topic, name='topic') #注意是大写的P
(?P\d+)为一个整数,?P表示将整数存储在topic_id中,而\d+与包含在两个斜杠内的任何数字相匹配。
发现URL与这个模式匹配时,Django将调用topic()函数,并将topic_id中的值作为实参传递给它。
2、视图
在视图views.py中设定topic()函数,并从数据库中获取与主题相关联的条目。
def topic(request, topic_id): topic = Topic.objects.get(id=topic_id) entries = topic.entry_set.order_by('-date_added') #entries条目按照日期降序排列。 context = {'topic':topic, 'entries':entries} return render(request, 'learning_logs/topic.html', context)
在自己的项目中编写这样的查询时,先在Django shell中进行尝试大有裨益。相比于编写视图和模版,再在浏览器中检查结果,在shell中执行代码可更快获得反馈。
3、模板
新建topic.html模板,在其中显示特定主题以及条目。主要使用topic和entries两个变量,从context中引用。
{% extends 'learning_logs/base.html' %} {% block content %} <p>Topic: {{ topic }}</p> <p>Entries:</p> <ul> {% for entry in entries %} <li> <p>{{ entry.date_added|date:'M d, Y H:i' }}</p> #注意双花括号,调用条目中的时间排序,管道符用于调整日期格式。 <p>{{ entry.text|linebreaks }}</p> #管道符后的linebreaks用于将包含换行符的长条目转换为浏览器能够理解的格式。 </li> {% empty %} <li> There is no entries for this topic yet. </li> {% endfor %} </ul> {% endblock content %}
4、将显示所有主题的页面中的每个主题都设置为链接
修改topics.html,让每个主题都链接到相应的网页。
<li> <a href="{% url 'learning_logs:topic' topic.id %}>{{ topic }}</a> #添加了topic.id是能够传递topic的id,但是这个表达式怎么理解,这里的topic是变量吗? </li>