定义模型Entry
要记录学到的国际象棋和攀岩知识,需要为用户可在学习笔记中添加的条目定义模型。每个条目都与特定主题相关联,这种关系被称为多对一关系,即多个条目可关联到同一个主题。下面是模型Entry的代码
models.py
from django.db import models
# Create your models here.
class Topic(models.Model):
"""用户学习的主题"""
text = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
"""返回模型的字符串表示"""
return self.text
class Entry(models.Model):
"""学到的有关某个主题的具体知识"""
topic = models.ForeignKey(Topic)
text = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name_plural = 'entries'
def __str__(self):
"""返回模型的字符串表示"""
return self.text[:50] + "..."
像Topic一样,Entry也继承了Django基类Model(见)。第一个属性topic是一个ForeignKey实例(见)。外键是一个数据库术语,它引用了数据库中的另一条记录;这些代码将每个条目关联到特定的主题。每个主题创建时,都给它分配了一个键(或ID)。需要在两项数据之间建立联系时,Django使用与每项信息相关联的键。稍后我们将根据这些联系获取与特定主题相关联的所有条目。接下来是属性text,它是一个TextField实例(见)。这种字段不需要长度限制,因为我们不想限制条目的长度。属性date_added让我们能够按创建顺序呈现条目,并在每个条目旁边放置时间戳
我们在Entry类中嵌套了Meta类。Meta存储用于管理模型的额外信息,在这里,它让我们能够设置一个特殊属性,让Django在需要时使用Entries来表示多个条目。如果没有这个类,Django将使用Entrys来表示多个条目。最后,方法str()告诉Django,呈现条目时应显示哪些信息。由于条目包含的文本可能很长,我们让Django只显示text的前50个字符(见)。我们还添加了一个省略号,指出显示的并非整个条目
迁移模型Entry
由于我们添加了一个新模型,因此需要再次迁移数据库。你将慢慢地对这个过程了如指掌:修改models.py,执行命令python manage.py makemigrations app_name,再执行命令python manage.py migrate
from django.db import models
# Create your models here.
class Topic(models.Model):
"""用户学习的主题"""
text = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
"""返回模型的字符串表示"""
return self.text
class Entry(models.Model):
"""学到的有关某个主题的具体知识"""
topic = models.ForeignKey(Topic, on_delete=models.CASCADE)
text = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name_plural = 'entries'
def __str__(self):
"""返回模型的字符串表示"""
return self.text[:50] + "..."
生成了一个新的迁移文件——0002_entry.py,它告诉Django如何修改数据库,使其能够存储与模型Entry相关的信息。执行命令migrate,我们发现Django应用了这种迁移且一切顺利
向管理网站注册Entry
我们还需要注册模型Entry。为此,需要将admin.py修改成类似于下面这样
admin.py
from django.contrib import admin
# Register your models here.
from learning_logs.models import Topic, Entry
admin.site.register(Topic)
admin.site.register(Entry)
返回到http://localhost/admin/,你将看到learning_logs下列出了Entries。单击Entries的Add链接,或者单击Entries再选择Add entry。你将看到一个下拉列表,让你能够选择要为哪个主题创建条目,还有一个用于输入条目的文本框。从下拉列表中选择Chess,并添加一个条目。下面是我添加的第一个条目
当你单击Save时,将返回到主条目管理页面。在这里,你将发现使用text[:50]作为条目的字符串表示的好处:管理界面中,只显示了条目的开头部分而不是其所有文本,这使得管理多个条目容易得多。再来创建一个国际象棋条目,并创建一个攀岩条目,以提供一些初始数据。下面是第二个国际象棋条目
Django shell
输入一些数据后,就可通过交互式终端会话以编程方式查看这些数据了。这种交互式环境称为Django shell,是测试项目和排除其故障的理想之地。下面是一个交互式shell会话示例
(ll_env) PS D:\learning_log> python manage.py shell
Python 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:43:08) [MSC v.1926 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from learning_logs.models import Topic
>>> Topic.objects.all()
<QuerySet [<Topic: Rock Climbing>, <Topic: Chess>]>
在活动的虚拟环境中执行时,命令python manage.py shell启动一个Python解释器,可使用它来探索存储在项目数据库中的数据。在这里,我们导入了模块learning_logs.models中的模型Topic,然后使用方法Topic.objects.all()来获取模型Topic的所有实例;它返回的是一个列表,称为查询集(queryset)
创建网页:学习笔记主页
使用Django创建网页的过程通常分三个阶段:定义URL、编写视图和编写模板。首先,你必须定义URL模式。URL模式描述了URL是如何设计的,让Django知道如何将浏览器请求与网站URL匹配,以确定返回哪个网页。每个URL都被映射到特定的视图——视图函数获取并处理网页所需的数据。视图函数通常调用一个模板,后者生成浏览器能够理解的网页。为明白其中的工作原理,我们来创建学习笔记的主页。我们将定义该主页的URL、编写其视图函数并创建一个简单的模板。鉴于我们只是要确保“学习笔记”按要求的那样工作,我们将暂时让这个网页尽可能简单。Web应用程序能够正常运行后,设置样式可使其更有趣,但中看不中用的应用程序毫无意义。就目前而言,主页只显示标题和简单的描述
映射URL
用户通过在浏览器中输入URL以及单击链接来请求网页,因此我们需要确定项目需要哪些URL。主页的URL最重要,它是用户用来访问项目的基础URL。当前,基础URL(http://localhost:8000/)返回默认的Django网站,让我们知道正确地建立了项目。我们将修改这一点,将这个基础URL映射到“学习笔记”的主页。打开项目主文件夹learning_log中的文件urls.py,你将看到如下代码
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('learning_logs.urls')),
]
前两行导入了为项目和管理网站管理URL的函数和模块 。这个文件的主体定义了变量urlpatterns。在这个针对整个项目的urls.py文件中,变量urlpatterns包含项目中的应用程序的URL。代码包含模块admin.site.urls,该模块定义了可在管理网站中请求的所有URL。我们需要包含learning_logs的URL
我们添加了一行代码来包含模块learning_logs.urls。这行代码包含实参namespace,让我们能够将learning_logs的URL同项目中的其他URL区分开来,这在项目开始扩展时很有帮助。默认的urls.py包含在文件夹learning_log中,现在我们需要在文件夹learning_logs中创建另一个urls.py文件
from django.urls import path
from . import views
app_name='learning_logs'
urlpatterns = [
# 主页
path('', views.index, name='index'),
# 显示所有的主题
#path('topics/', views.topics, name='topics'),
# 特定主题的详细页面
#path("topics/(?P<topic_id>\d+)/", views.topic, name='topic'),
]
为弄清楚当前位于哪个urls.py文件中,我们在这个文件开头添加了一个文档字符串。接下来,我们导入了函数url,因为我们需要使用它来将URL映射到视图。我们还导入了模块views ,其中的句点让Python从当前的urls.py模块所在的文件夹中导入视图。在这个模块中,变量urlpatterns是一个列表,包含可在应用程序learning_logs中请求的网页
实际的URL模式是一个对函数url()的调用,这个函数接受三个实参。第一个是一个正则表达式。Django在urlpatterns中查找与请求的URL字符串匹配的正则表达式,因此正则表达式定义了Django可查找的模式。我们来看看正则表达式r'$'。其中的r让Python将接下来的字符串视为原始字符串,而引号告诉Python正则表达式始于和终于何处。脱字符()让Python查看字符串的开头,而美元符号让Python查看字符串的末尾。总体而言,这个正则表达式让Python查找开头和末尾之间没有任何东西的URL。Python忽略项目的基础URL(http://localhost:8000/),因此这个正则表达式与基础URL匹配。其他URL都与这个正则表达式不匹配。如果请求的URL不与任何URL模式匹配,Django将返回一个错误页面。url()的第二个实参指定了要调用的视图函数。请求的URL与前述正则表达式匹配时,Django将调用views.index(这个视图函数将在下一节编写)。第三个实参将这个URL模式的名称指定为index,让我们能够在代码的其他地方引用它。每当需要提供到这个主页的链接时,我们都将使用这个名称,而不编写URL
编写视图
视图函数接受请求中的信息,准备好生成网页所需的数据,再将这些数据发送给浏览器——这通常是使用定义了网页是什么样的模板实现的。learning_logs中的文件views.py是你执行命令python manage.py startapp时自动生成的,当前其内容如下
from django.shortcuts import render
# Create your views here.
当前,这个文件只导入了函数render(),它根据视图提供的数据渲染响应。下面的代码演示了该如何为主页编写视图
from django.shortcuts import render
# Create your views here.
def index(request):
"""学习笔记的主页"""
return render(request, 'learning_logs/index.html')
URL请求与我们刚才定义的模式匹配时,Django将在文件views.py中查找函数index(),再将请求对象传递给这个视图函数。在这里,我们不需要处理任何数据,因此这个函数只包含调用render()的代码。这里向函数render()提供了两个实参:原始请求对象以及一个可用于创建网页的模板。下面来编写这个模板
编写模板
模板定义了网页的结构。模板指定了网页是什么样的,而每当网页被请求时,Django将填入相关的数据。模板让你能够访问视图提供的任何数据。我们的主页视图没有提供任何数据,因此相应的模板非常简单。在文件夹learning_logs中新建一个文件夹,并将其命名为templates。在文件夹templates中,再新建一个文件夹,并将其命名为learning_logs。这好像有点多余(我们在文件夹learning_logs中创建了文件夹templates,又在这个文件夹中创建了文件夹learning_logs),但建立了Django能够明确解读的结构,即便项目很大,包含很多应用程序亦如此。在最里面的文件夹learning_logs中,新建一个文件,并将其命名为index.html,再在这个文件中编写如下代码
<p>Learning Log</p>
<p>Learning Log helps you keep track of your learning, for any topic you're
learning about.</p>
这个文件非常简单。对于不熟悉HTML的读者,这里解释一下:标签<p></p>标识段落;标签<p>指出了段落的开头位置,而标签</p>指出了段落的结束位置。这里定义了两个段落:第一个充当标题,第二个阐述了用户可使用“学习笔记”来做什么。现在,如果你请求这个项目的基础URL——http://localhost:8000/,将看到刚才创建的网页,而不是默认的Django网页。Django接受请求的URL,发现该URL与模式r'^$'匹配,因此调用函数views.index(),这将使用index.html包含的模板来渲染网页