Python基础知识18——Django入门

本章内容来自书籍,记录下来仅方便复习,如有侵权,请联系作者删除。
+++python学习笔记+++

一、建立项目

学习如何使用Django(http://djangoproject.com)
开发“学习笔记”(Learning Log)项目,这是一个在线系统日志。
Django是一个Web框架——一套用于开发交互式网站的工具,能够响应网页请求,读写数据,管理用户等。

建立项目时,首先需要以规范的方式对项目进行描述,再建立虚拟环境,以便在其中创建项目。

1. 指定规范
阐述项目功能,外观,用户界面。

编写一个名为“学习笔记”的Web应用程序,让用户记录主题,在主题中增加日志条目。邀请用户注册或登录,创建新主题,添加新条目,阅读既有的条目。

2. 建立虚拟环境

虚拟环境是系统的一个位置,可以在其中安装包,并将其与其他的Python包隔离。将项目的库与其他项目分离是有益的。

为项目新建一个目录,名为learning_log,在终端切换到该目录,并创建一个虚拟环境。这里运行了venv模块,并使用它来创建一个名为ll_env的虚拟环境。

创建命令:leaning_log>python -m venv ll_env

3. 安装virtualenv
如果不能使用模块venv,我们可以安装virtualenv包。
安装命令:pip install virtualenv
…linux…(省略)

4. 激活虚拟环境
建立虚拟环境后,需要使用下面的命令来激活它
Windows激活命令:ll_env\Scripts\activate

要停止使用虚拟环境,可执行命令deactivate

5. 安装Django
在虚拟环境中安装
(ll_env)learning_log> pip install Django
Django仅在虚拟环境处于活动状态时可用。

6. 在Django中创建项目
(ll_env)learning_log> django-admin.py startproject learning_log .
命令末尾的句点让新项目使用合适的目录结构。如果忘记句点,将创建的文件和文件夹删除(ll_env除外),再重新运行这个命令。

(ll_env)learning_log\learning_log>dir(查看目录)
settings.py:指定Django如何与你的系统交互以及如何管理项目;
urls.py:告诉Django应创建哪些网页来响应浏览器请求;
wsgi.py:帮助Django提供它创建的文件,是web server gateway interface(Web服务器网关接口)首字母的缩写。

7. 创建数据库

Django将大部分与项目相关的信息都存储在数据库中,因此我们要创建一个供Django使用的数据库。
创建数据库,在虚拟环境下执行以下命令
(ll_env)learning_log> python manage.py migrate

(ll_env)learning_log> dir
db.sqlite3 learning_log ll_env manage.py

我们将修改数据库称为迁移数据库,首次执行命令migrate时,将让Django确保数据库与项目的当前状态匹配。

8. 查看项目

核实Django是否正确地创建了项目,可执行命令runserver

(ll_env)learning_log> python manage.py runserver

运行结果:

(ll_env) D:\django\learning_log>python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

(1)System check identified no issues (0 silenced).
October 07, 2019 - 21:07:47
(2)Django version 2.2.6, using settings 'learning_log.settings'
(3)Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

(1)Django通过检查确认地创建了项目
(2)Django的版本,当前使用的设置文件的名称
(3)指出项目的URL,端口号

打开浏览器,输入url和端口号,可以正常访问。
如果想要关闭服务器,按住Ctrl+C即可。
如果要切换端口号,可以在runserver命令后添加端口号
端口号修改为8001:Python manage.py runserver 8001

二、创建应用程序

当前终端窗口应该还运行着runserver
再打开一个终端端口,切换到manage.py所在的目录,然后:
激活该虚拟环境:ll_env\Scripts\activate
执行命令startapp:python manage.py startapp learning_logs

查看项目目录,可以看到其中新增了一个文件夹,learning_logs,其中最重要的文件是,models.py, admin.py, views.py
models.py:定义我们要在应用程序中管理的数据
admin.py:
views.py:

1. 定义模型

打开models.py,让我们创建自己的模型。

from django.db import models

# Create your models here.
  • 第一步:修改models.py
    我们自己创建Topic类,继承Model——Django中定义了模型基本功能的类。Topic类只有两个属性:text和date_added。
class Topic(models.Model):
    text = models.CharField(max_length=200)
    date_added = models.DateTimeField(auto_now_add=True)
    
    def __str__(self):
        return self.text

(1)属性text是一个CharField——由字符或文本组成的数据。必须使用max_length告诉Django在数据库中预留多少空间。

(2)属性date_added是一个DateTimeField——记录日期和时间的数据。传递实参auto_add_now=True,当用户创建新主题时,让Django将这个属性设置成当前日期和时间。

(3)方法__str__(),显示模型的简单表示,这里返回噢存储在text中的字符串。

2. 激活模型
如果要使用模型,首先让Django将应用程序包含到项目中。为此,打开learning_log目录下的settings.py,找到列表INSTALLED_APPS,添加我的应用程序learning_logs

settings.py

--snip--
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    #我的应用程序
    'learning_logs',
]
--snip--
  • 第二步:需要让Django修改数据库,使其能够存储于模型Topic相关的信息。为此,在终端窗口中执行下面的命令:
    (ll_env)learning_log> python manage.py makemigrations learning_logs
运行结果:

(ll_env) D:\django\learning_log>python manage.py makemigrations learning_logs
Migrations for 'learning_logs':
  learning_logs\migrations\0001_initial.py
    - Create model Topic

makemigrations让Django确定该如何修改数据库,使其能够存储与我们定义的新模型相关联的数据。

  • 第三步:下面来应用这种迁移,让Django替我们修改数据库:
    (ll_env)learning_log> python manage.py migrate
运行结果:

(ll_env) D:\django\learning_log>python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, learning_logs, sessions
Running migrations:
(1)  Applying learning_logs.0001_initial... OK

我们检查(1)处的输出行,在这里Django确认为learning_logs应用迁移时一切正常(OK)。

每当需要修改学习笔记管理的数据时,都采取如下三个步骤:
step1: 修改models.py
step2: 对learning_logs调用makemigrations
python manage.py makemigrations learning_logs
step3: 让Django迁移项目
python manage.py migrate

3. Django管理网站
为应用程序定义模型时,Django提供的管理网站(admin site)让你能够轻松地处理模型。
(1)创建超级用户
Django允许你创建具有所有权限的用户——超级用户。

(ll_env) learning_log> python manage.py createsuperuser

运行结果:

(ll_env) D:\django\learning_log>python manage.py createsuperuser
Username (leave blank to use 'higirl'): ll_admin
Email address:
Password:
Password (again):
Superuser created successfully.

创建超级用户时,需要输入用户名和密码,电子邮件地址可以为空。

(2)向管理网站注册模型

admin.py

from django.contrib import admin

# Register your models here.

from learning_logs.models import Topic #(code1)
admin.site.register(Topic) #(code2)

我们在(code1)处导入注册模型,在(code2)处让Django通过管理网站管理我们的模型。此时我们可以使用超级账户访问管理网站http://localhost:8000/admin。我们能够添加和修改用户和用户组,管理刚刚定义的模型Topic相关的数据。

(3)添加主题
添加第一个主题,单击Topics进入主题网页,单击Add topic,在添加主题的表单中输入Chess,单击Save,返回到主题管理页面。再创建第二个主题Rock Climbing,单击Save回到主题管理页面,可以看到刚刚添加的两个主题。

4. 定义模型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,on_delete=models.CASCADE) #(code1)
    text = models.TextField() #(code2)
    date_added = models.DateTimeField(auto_now_add=True)
    
    class Meta: #(code3)
        verbose_name_plural = 'entries'
        
    def __str__(self): #(code4)
        return self.text[:50] + "..."

Entry也继承Django基类Model,分析如下:
(code1):属性topic,是一个ForeignKey实例,外键。将这个条目关联到了主题Topic。

在django2.0后,定义外键和一对一关系的时候需要加on_delete选项,此参数为了避免两个表里的数据不一致问题。
参数说明:
on_delete有CASCADE、PROTECT、SET_NULL、SET_DEFAULT、SET()五个可选择的值
CASCADE:此值设置,是级联删除。
PROTECT:此值设置,是会报完整性错误。
SET_NULL:此值设置,会把外键设置为null,前提是允许为null。
SET_DEFAULT:此值设置,会把设置为外键的默认值。
SET():此值设置,会调用外面的值,可以是一个函数。
一般情况下使用CASCADE就可以了。
来自:https://www.cnblogs.com/phyger/p/8035253.html

(code2):属性text,是一个TextField实例,不需要长度限制
(code3):属性date_added,让我们能够按创建时间顺序呈现条目
(code4):在Entry类中嵌套Meta类,用于管理模型的额外信息,它让我们能够设置一个特殊属性。
(code5):方法__str__()告诉Django,呈现条目时应该显示哪些信息,由于条目中包含的文本可能过长,我们让Django只显示text的前50个字符。

5. 迁移模型Entry(条目)

由于我们创建了新模型,因此需要再次迁移数据库。
step2:
(ll_env)learning_log>python manage.py makemigrations learning_logs

运行结果:

(ll_env) D:\django\learning_log>python manage.py makemigrations learning_logs
Migrations for 'learning_logs':
  learning_logs\migrations\0002_entry.py #result1
    - Create model Entry

result1表示生成新的迁移文件,它告诉Django如何修改数据库,使其能够存储与模型Entry相关的信息。

step3:
(ll_env)learning_log> python manage.py migrate

运行结果:

(ll_env) D:\django\learning_log>python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, learning_logs, sessions
Running migrations:
  Applying learning_logs.0002_entry... OK #result2

result2表示,执行命令migrate,Django应用了这种迁移且一切顺利。

6. 向管理网站注册Entry

注册模型Entry,修改admin.py

from django.contrib import admin
from learning_logs.models import Topic,Entry
admin.site.register(Topic)
admin.site.register(Entry)

浏览器访问http://localhost:8000/admin/

单击Entries,点击Add链接,下拉列表选择Chess/Rock Climbing。可以往text文本框中添加条目。单击Save表示保存成功。

7. Django shell
输入一些数据后,就可以通过交互式终端会话,以编程的方式查看这些数据。这种交互式环境称为Django shell,是测试项目和排除其故障的理想之地。

示例:
(ll_env)learning_log> python manage.py shell

(ll_env) D:\django\learning_log>python manage.py shell #(code1)
>>> from learning_logs.models import Topic #(code2)
>>> Topic.objects.all() #(code3)
<QuerySet [<Topic: Chess>, <Topic: Rock Climbing>]> #(result1)

(code1):启动一个Python解释器,可使用它来探索存储在项目数据库中的数据。
(code2):导入模块learning_logs.models中的模型Topic
(code3):获取模型Topic的所有实例

(result1):返回一个列表,称为查询集(queryset)

操作查询集:
(1)遍历查询集,查看分配给每个主题对象的ID

>>> topics = Topic.objects.all()
>>> for topic in topics:
...     print(topic.id,topic)
...
1 Chess
2 Rock Climbing

(2)通过id获取该对象并查看其任何属性

>>> t = Topic.objects.get(id=1)
>>> t.text
'Chess'
>>> t.date_added
datetime.datetime(2019, 10, 8, 2, 39, 3, 408620, tzinfo=<UTC>)
>>> t.entry_set.all()
<QuerySet [<Entry: The opening is the first part of the games, roughl...>, <Entr
y: In the opening phase of the game, it's important t...>]>
>>>

每次修改模型后,都要重启shell,才能看到修改的效果,要退出shell会话,可按Ctrl+Z,再按回车键。

三、创建网页:学习笔记主页

使用Django创建网页的过程通常分为三个阶段:定义URL,编写视图,编写模板。

1. 映射URL
用户通过在浏览器中输入URL以及单击连接来请求网页,因此我们需要确定项目需要哪些URL。

  • (1) 打开项目主文件夹learning_log中的文件urls.py
from django.contrib import admin
from django.urls import path,include #(code1)

urlpatterns = [ #(code2)
    path('admin/', admin.site.urls), #(code3)
    #添加路径
    path('', include(('learning_logs.urls','learning_logs'), namespace='learning_logs')), #(code4)
]

(code1):导入为项目和管理网站管理URL的函数和模块
(code2):定义变量urlpatterns,包含项目中应用程序的URL
(code3):包含模块admin.site.urls,定义了可在管理网站中请求的所有URL
(code4):添加一行代码来包含learning_logs.urls。包含实参namespace=‘learning_log’,与其他项目的URL区分开。

  • (2) 在文件夹learning_logs中创建urls.py文件
from django.urls import path
from . import views

app_name='learning_logs' #(code1)
urlpatterns = [ #(code2)
    path('', views.index, name = 'index'), #(code3)
]

首先导入函数path,将URL映射到视图,让Python从当前的urls.py模块所在的文件夹中导入视图模块views,

(code1):定义列表urlpatterns,包含可在应用程序learning_logs中请求的网页。
(code2):接受三个实参,正则表达式,指定要调用的视图,将这个URL模式的名称指定为index。

2. 编写视图

from django.shortcuts import render #(code1)

# Create your views here.

def index(request): #(code2)
    return render(request, 'learning_logs/index.html')

(code1):根据视图提供的数据渲染响应。

(code2):URL请求与我们刚才定义的模式匹配时,Django将在文件views.py中查找函数index(),再将请求对象传递给这个视图函数。这个函数调用render中的代码,传递两个实参:请求对象,一个可用于创建网页的模板。

3. 编写模板

在文件夹learning_logs中新建一个文件夹templates,在文件夹templates中新建一个文件夹命名为learning_logs,在新的文件夹learning_logs中新建文件index.html。这样建立了Django能够明确解读的结构。

结构如下:

learning_logs/
|---templates/
	|---learning_logs/
		|---index.html

index.html文件如下:

index.html

<h1>Learning Log</h1>
<p>Learning Log helps you keep track of your learning, for any topic you'readonly
learning about.</p>

现在可以通过http://localhost:8000/访问Django网页了。
运行过程:Django接受请求的URL,发现URL与正则表达式模式匹配,因此调用函数views.index(),然后使用index.html包含的模板来渲染网页。这里实现了将URL、视图和模板分离。

四、创建其他网页

扩充学习笔记的项目。创建两个显示数据的网页,其中一个列出所有的主题,另一个显示特定主题的所有条目。对于每个网页,我们都将指定URL模式,编写一个视图函数,并编写一个模板。

1. 模板继承
创建网页时,几乎都有一些所有网页都包含的元素,在这种情况下,可编写一个包含通用元素的父模板,并让每个网页都继承这个模板,而不必在每个网页中重复定义这些通用元素。

(1)父模板
使用模板标签,用大括号和百分号({% %})表示,模板标签是一小段代码,生成要在网页中显示的信息。

base.html

<p>
  <a href="{% url 'learning_logs:index' %}">Learning Log</a> #code1
</p>

{
     % block content %}{
     % endblock content %}

code1:模板标签生成一个URL,该URL与learning_logs/url.py中定义名为index的URL模式匹配。

(2)子模板

index.html

{
     % extends "learning_logs/base.html" %} #code1

{
     % block content %} #code2
  <p>Learning Log helps you keep track of your learning, for any topic you'readonly
  learning about.</p>
{
     % endblock content %}

code1:子模板的第一行必须包含标签{% extends %},告诉Django继承了那个模板
code2:插入一个名为content的{% block %}标签,定义content模块。不是从父模板继承的内容都包含在content块中。

2. 显示所有主题的页面

  • (1)URL模式
    给主题添加链接,并用主题id设置url,如:http://localhost:8000/topics/1/
urls.py

'''定义learning_logs的URL模式'''

from django.urls import path
from . import views

app_name='learning_logs'
urlpatterns = [
    # 主页
    path('', views.index, name = 'index'),
    # 显示所有的主题
    path('topic/',views.topics,name='topics'),
    # 特定主题的详细页面(?P\d+) 改为 /
    path('topics//',views.topic,name='topic')
]

发现URL与这个模式匹配时,Django将调用视图函数topic(),并将存储在topic_id中的值作为实参传递给它。在这个函数中,我们将使用topic_id的值来获取相应的主题。

  • (2)视图
    函数topic()需要从数据库中获取指定的主题以及与之相关联的所有条目。如下所示:
views.py

from django.shortcuts import render
from .models import Topic

# Create your views here.

def index(request):
    '''学习笔记的主页'''
    return render(request, 'learning_logs/index.html')

def topics(request):
    """显示所有的主题"""
    topics = Topic.objects.order_by('date_added')
    context = {
     'topics':topics}
    return render(request, 'learning_logs/topics.html', context)
    
def topic(request, topic_id):
    """显示单个主题及其所有的条目"""
    topic = Topic.objects.get(id=topic_id)
    entries=topic.entry_set.order_by('-date_added')
    context = {
     'topic':topic, 'entries':entries}
    return render(request, 'learning_logs/topic.html', context)
  • (3)模板
    topic模板需要显示主题的名称和条目的内容。
topic.html

{
     % 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> #code1
	  <p>{
     {
      entry.text|linebreaks }}</p> #code2
	</li>
  {
     % empty %}
    <li>
	  There are no entries for this topic yet.
	</li>	
  {
     % endfor %}
  </ul>

{
     % endblock content %}

竖线(|)表示模板过滤器——对模板变量的值进行修改的函数。
code1:过滤器date,时间戳格式化
code2:过滤器linebreaks:将包含换行符的长条目转换为浏览器能够理解的格式,以免显示为一个不间断的文本块。

  • (4)将显示所有主题的页面中的每个主题都设置为链接
    修改模板topics,让每个主题都链接到相应的网页。
topics.html

{
     % extends "learning_logs/base.html" %}

{
     % block content %}
	<p>Topics</p>
	
	<ul>
	  {
     % for topic in topics %}
		<li>
		  <a href="{% url 'learning_logs:topic' topic.id %}">{
     {
      topic }}</a>
		</li>
	  {
     % empty %}
	    <li>No topic have been added yet.</li>
	  {
     % endfor %}	
	</ul>
	
{
     % endblock content%}

以下是学习过程中遇到的问题:

(1) 生成数据库表时报错
https://www.cnblogs.com/phyger/p/8035253.html
Django在根据models生成数据库表时报 init() missing 1 required positional argument: ‘on_delete’
在django2.0后,定义外键和一对一关系的时候需要加on_delete选项,此参数为了避免两个表里的数据不一致问题,不然会报错:

解决方法:
引用外键的时候,传递实参on_delete。
herobook=models.ForeignKey(‘BookInfo’,on_delete=models.CASCADE,)

(2)Django中url的正则表达式错误
https://blog.csdn.net/u012041204/article/details/80208949

WARNINGS:?: (2_0.W001) Your URL pattern ‘^KaTeX parse error: Expected group after '^' at position 69: …begins with a '^̲', or ends with…’.
This was likely an oversight when migrating to django.urls.path().

今天在使用Django时遇到上面的警告,虽然只是警告,但是却会导致网站无法访问。这问题是Django新版本改变导致URL中不需要再使用正则表达式了,只需要路径就OK了。

解决方法:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', views.login,name='login'),
    path('',views.index,name='index'),                              #此处设置为首页,以前写法是'^$',新版本不再使用^、$,只需要''就可以
]

(3)runserver发生错误
https://blog.csdn.net/weixin_40841752/article/details/79335345

django.core.exceptions.ImproperlyConfigured:
Specifying a namespace in include() without providing an app_name is not supported.
Set the app_name attribute in the included module,
or pass a 2-tuple containing the list of patterns and app_name instead.
意思为:
在include方法里面指定namespace却不提供app_name是不允许的。
在包含的模块里设置app_name变量,或者在include方法里面提供app_name参数。

解决方法
方法1:在proj/urls.py中修改

from django.urls import path,include
from app import urls as app_url
urlpatterns = [
    path('', include((common_url,'common'), namespace='common')),
]

方法2:在app/urls.py中修改

from django.urls import path
from .views import index
app_name='common'
urlpatterns = [
    path('',index,name='index'),
]

(4)endblock找不到

django.template.exceptions.TemplateSyntaxError: Unclosed tag on line 5: ‘block’.
Looking for one of: endblock.

解决方法:调整格式正确{% %},注意括号,百分号中英文格式等。
https://stackoverflow.com/questions/29255433/unclosed-tag-block-looking-for-one-of-endblock

(5) lilyxiaoyy的博客,有挺多文章
http://blog.sina.com.cn/xyy890130
python3 Django显示主题的页面
http://blog.sina.com.cn/s/blog_6436b8ec0102x5mr.html

你可能感兴趣的:(Django)