Django学习笔记 (1)

Django学习笔记

1.简单的博客系统
	1.1 Django起步
		1.1.1 Django简介
		1.1.2 安装 Django
		1.1.3 创建项目
		1.1.4 创建应用
		1.1.5 网站配置
		1.1.6 知识点
	1.2 编写博客的数据模型类
	  1.2.1 数据模型类
	  1.2.2 发布博客文章
	  1.2.3 知识点
	1.3 显示博客信息
	  1.3.1 显示文章标题
	  1.3.2 查看文章内容
	  1.3.3 知识点
2. 用户管理

1. 简单的博客系统

1.1 Django起步

1.1.1 Django简介

• 容易上手,开发速度快:
• 囊括了网站开发中的用户管理、内容管理、网站地图、 RSS 等常用的众多插件:
• 安全性强,比如 Django 默认解决了 SQL 注入、跨站攻击等问题;
• 应用广泛,类型多样化。使用 Django 开发的网站包括公司提供的各类在线服务网站
社会组织和政府机构网站等,其类型包括但不限于管理系统、社交网站、计算平台等。

1.1.2 安装 Django

$ sudo pip install Django==1.10.1
$ python
>>>import django
>>>print(django.get_version())#打印出当前Django的版本号

② 或

$ git clone https://github.com/django/django.git 
$ sudo pip install -e ./django

1.1.3 创建项目(project)

$ django-admin startproject mysite
$ tree
|--mysite
  |--manage.py
  |--mysite
    |--__init__.py
    |--settings.py
    |--urls.py
    |--wsgi.py

② 或

$ django-admin startproject mysite .#注意命令行末尾的 ‘ . ’
$ tree
|--manage.py
|--mysite
  |--__init__.py
  |--settings.py
  |--urls.py
  |--wsgi.py

③ 之后运行服务

$ python manage.py runserver
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C

1.1.4 创建应用(application)

① 创建第一个blog应用

$ cd /root/mysite/mysite
$ ls
manage.py mysite
$ python manage.py startapp blog
$ tree
|--blog
  |--admin.py
  |--apps.py
  |--__init__.py
  |--migrations
    |--__init__.py
  |--models.py
  |--tests.py
  |--views.py
|--db.splite3
|--manage.py
|--mysite
  |--__init__.py
  |--settings.py
  |--urls.py
  |--wsgi.py

② 各个部分的简介
1)manage.py(不可删除,后续要用)
在创建一个Django项目后,manage.py被自动生成在项目的根目录中,它是对django-admin.py的简单封装,同样能够实现命令行操作,同时还有自己的特点。

django-admin命令对应着django-admin.py文件,他在Django安装后保存在Django安装目录的 /bin 下面。

而manage.py只在建立了一个项目之后,才存在于项目的根目录中。

2)mysite
mysite是所建项目的管理功能目录,因用户所创建的项目名称的不同而异,一般不在其中输入应用部分的内容。

其中的几个文件常用于面向整个项目进行参数匹配:

a.settings.py:这个文件中包含了项目的初始化设置,可以针对整个项目进行有关参数配置,比如配置数据库、添加应用等。

b.urls.py:这是一个URL配置文件,主要是将URL映射到应用程序上,该对象被称为URLconf。

c.wsgi.py:(Web Server Gateway Interface),WSGI是Python所选择的服务器和应用标准,Django也会使用。

d.pycacha:只有在网站运行后,他才会出现,是一个文件夹,其中都是以 .pyc 结尾的文件。

e.views.py:重要的文件!用户保存响应各种请求的函数或者类。如果编写的是函数,则称之为基于函数的视图;如果编写的是类,则称之为基于类的视图。views.py就是保存函数或者类的视图文件。

【注】
本质:类似 web.py 中,对象"render = web.template.render(“templates/)”;
作用:调用渲染文件,如HTML

3)db.splite3
这是一个默认的数据库。SQLite是Python默认安装的数据库。这个默认文件是在 ./mysite/settings.py 中配置生成的)。

1.1.5 网站配置

在Django项目中,主管信息注册(对本项目进行各种信息声明)的文件是 ./mysite/settings.py 。下面选择几个目前会用到的进行说明。

  • DEBUG:其值为True或者False。

    “开发模式”——在开发过程中需要设置为True,为了在测试功能时让Django能够显示详细的报错信息。

    “生产环境”——如果将项目部署到真正要对外发布的服务器上,必须将其值修改为False,从而避免暴露项目的内部信息。

  • ALLOWED_HOSTS:

    在DEBUG为True时,其值可以为空。
    当部署到生产环境中时,要把主域名填写到这里,才能通过域名访问到本网站。

  • INSTALLED_APPS:非常重要的配置项,所有的应用只有写到这里才能生效。

下面的INSTALLED_APPS列表中, #1 是新增加的,就是所建立的应用名称,其他各项是Django默认具有的应用。

INSTALLED_APPS = [
	'django.contrib.admin',
	'django.contrib.auth',
	'django.contrib.contenttypes',
	'django.contrib.sessions',
	'django.contrib.messages',
	'django.contrib.staticfiles',
	'blog',	#1
]
  • DATABASES:在这里可以配置数据库。Django能够支持多种数据库,比如常见的MySQL、PostgreSQL、Oracle等。

    默认配置数据库为 SQLite

    如果用到了其他数据库,可以到官网1查询相关配置方式

  • LANGUAGE_CODE:设置项目的语言,一般情况下可以不用修改。

    汉语设置为

     LANGUAGE_CODE = ‘zh-hans’
    
  • TIME_ZONE:设置市区,通常使用东八区,设置为

      TIME_ZONE = 'Asia/Shanghai'
    

1.1.6 知识点

.1 开发模式

系统尚处于开发阶段,还没有正式对外部客户提供服务,这种模式下很多配置都是为了开发而定的,比如在Django开发模式中,不需要配置Apache或者Nginx等服务器,也能够运行网站,这是因为Django本身就提供了简单的Web服务器功能,但是这仅限于开发过程,当网站被正式部署之后,即转换为“生产模式时,就需要对部分配置进行修改。

在开发模式中,Django会自动检测到修改的代码并重新加载,不需要每次修改代码后重新启动Web服务器。只有在新增加文件后,才需要重启Django服务。

运行Django服务的指令是

python manage.py runserver
.2 项目和应用

Django安装好之后,就有了django-admin这个默认命令,可以用

django-admin startproject [project_name] 

命令创建一个Django项目。项目是由若干个“应用(app)”组成的,实现具体功能。创建应用可以使用命令

python manage.py startapp [app_name]

也可以使用命令

django-admin startapp [app_name]

创建了项目和应用(例如 mysite & blog)之后,会生成一些默认的文件,他们要么是一些默认的配置,如 settings.py ,要么是空文件,仅仅是为了占据一个位置而已,如应用里面的 views.py , models.py 等。

每个应用都要在项目的 settings.py 文件的 INSTALLED_APPS 中进行声明,告诉Django这个应用是本项目的一部分。

Django将很多默认的功能(比如用户管理功能)视为一个应用。此外,Django是一个开放的系统,任何人都可以开发第三方应用,这些第三方应用通常用于解决某个常见的问题,开发者在项目中使用第三方应用的主要目的就是节省时间、快速开发。这些第三方应用如果要在项目中使用,也要在 settings.py 的INSTALLED_APPS中注册后才能生效。

3.文档导读
  • Writing your first Django app2
  • 使用 Django 的网站 3
    -Django FAQ 4

1.2 编写博客的数据模型类

设计数据库和表结构是做网站的基础。在Django中,我们不需要使用SQL语句直接跟数据库打交道,而是完全用Python的方式创建数据模型,之后交给Django完成数据库的操作。

编写博客的数据模型类的基本知识如下图所示。

属性对应
属性对应
编写博客的数据模型类
简单的数据模型类基本结构
字段和属性
字段
类属性/变量
数据库表的字段
字段的属性
数据迁移

1.2.1 数据模型类

利用Django开发网站系统,一般情况下要先编写数据模型,就是在 ./blog/models.py 中写一个类,这个类与数据库中的数据表具有对应关系。

下面就在 ./blog/models.py 中编写博客的数据模型类 Blog,本质上他是一个继承了 django.db.models.Model 的类

from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User

#创建类 BlogArticle(),继承自 django.db.models.Model

class BlogArticles(models.Model):
	# object * 4
	#models.CharField()  [1]
	title = models.CharField(max_length=300)

	#models.ForeignKey() [2]
	author = models.ForeignKey(User, related_name="blog_posts")

	#models.TextField() [3]
	body = models.TextField()

	#models.DateTimeField() [4]
	publish = models.DateTimeField(default = timezone.now)

	#class * 1 [5]
	class Meat:
		ordering = ("-publish",)

	# function * 1 [6]
	def __str__(self):

		return self.title

在这个类中,定义了一些属性,每个属性对应着将来数据库表中的一个字段。在后面的讲解中,讲到数据模型类中的这些属性时,常常称之为字段。

语句 [1]

title  = models.CharField(max_length=300)

规定了字段 title 的属性为 CharField() 类型,并且以参数 max_length=30 的形式说明字段的最大数量。

语句 [2]

author = models.ForeignKey(User, related_name="blog_posts")

通过字段 author 规定了博客文章和用户之间的关系——一个用户对应多篇文章, ForeignKey() 就反映了这种“一对多”关系。类 User 就是 BlogArticles 的对应对象,relateed_name="blog_posts"的作用是允许通过类User反向查询到BlogArticles。

语句 [5]

class Meta:
	ordering = ("-publish",)

类似Python中的元类但不同。此处通过 ordering = ("-publish",)规定了 BlogArticles 实例对象的显示顺序,即按照 publish 字段值的倒序显示。

BlogArticles 类的数据模型编写好了,将来数据库表的基本结构就是按照上述各字段及其属性而定的。

接下来的操作将根据数据模型建立数据库表

cd /root/mysite/
mysite $ python manage.py makemigrations
Migrations for 'blog':
	blog/migrations/0001_initial.py:
	-Create model BlogArticles

注意观察上面的执行结果,提示信息中告诉我们在 blog/migrations 目录中创建了一个 BlogArticles 模型,可以打开它:

[mysite]$ more /blog/migrations/0001_initial.py
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-04-15 11:23
from __future__ import unicode_literals

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone


class Migration(migrations.Migration):

    initial = True

    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ]

    operations = [
        migrations.CreateModel(
            name='BlogArticles',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
	                ('title', models.CharField(max_length=300)),
	                ('body', models.TextField()),
	                ('publish', models.DateTimeField(default=django.utils.timezone.now)),
	                ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASC
	ADE, related_name='blog_posts', to=settings.AUTH_USER_MODEL)),
	            ],
	        ),
	    ]

这个文件时在执行 python manage.py makemigrations 之后Django自动生成的。

还可以用另一种方法看该文件的本质

mysite$ python manage.py sqlmigrate blog 0001
BEGIN;
--
--Create model Blog
--
CREATE TABLE "blog_blogarticles"("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, “title” varchar(300) NOT NULL, "body" text NOT NULL, "publish" datetime NOT NULL, "author_id" integer NOT NULL REFERENCES "auth_user" ("id"));
CREATE INDEX "blog_blogarticles_author_id_87911f69" ON "blog_blogarticles" ("author_id");
COMMIT;

本质为SQL语句

上述文件的功能是:创建一个名称为 blog_blogarticles 的数据库表。这个表的名称由两部分组成,第一部分 blog 是本应用的名称,第二部分 blogarticles 是在models.py 中创建的数据模型类的名称,中间用单下划线连接(应用名称_数据模型类名称 )

再观察数据库表中的字段名称,除 id 是自动生成的,其他都是在数据模型类 BlogArticles 中生命的字段及其属性。

上面创建了一个能够建立数据库表的文件,下面在此基础上创建数据库 :

[mysite]$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, blog, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying blog.0001_initial... OK
  Applying sessions.0001_initial... OK

本书中的项目使用 SQLIte 数据库,并且在 setting.py 中规定了数据库文件存放在项目根目录中。

对于db.sqlite3这个文件,可以在浏览器(Firefox为例)中安装插件:SQLite MAnager 来查看

安装后重启 --> 点击SQLite Manager --> 打开 db.sqlite3 。

在 Tables 下列出了本项目中目前已有的数据库表,除 blog_blogarticles 是刚刚通过 BlogArticles 模型建立的外,别的都是项目默认创建的数据库表。

先只看 blog_articles ,左边显示该数据库表结构,仔细观察每个字段的数据类型,将这里的结果和前面在数据模型类 BlogArticles 类中所规定的字段及其属性进行对照,进一步理解数据模型类中的各个属性含义。

这样接建立了博客的数据库——这是基础,接下来就是发布博客,并保存到这个数据库中。

1.2.2 发布博客

我们先用最简单的方式实现博客文章的发布,使用 Django 默认的管理功能就可以发布文章。要使用此功能,必须先创建超级管理员。

[mysite]$ python manage.py createsuperuser
Username (leave blank to use 'qiwsir'):admin
Email address: [email protected]
Password:
Password (again):
Superuser created successfully.

注意,在这里输入密码时,Django默认不能太简短。

然后运行服务器

[mysite]$ python manage.py runserver
Performing system checks...

System check identified no issues(0 silenced).
October 06, 2016 - 21:17:21 
Django version 1.10 .1, using settings 'mysite.settings' 
Starting development server at http://127.0.0.1:8000/ 
Quit the server with CONTROL-c.

在浏览器的地址栏输入 http://127.0.0.1:8000/admin/ ,就可以打开登录界面。用超级管理员身份进入系统,会见到形如

AUTHENTICATION AND AUTHORIZATION(Django管理)
Site administration(站点管理)
Groups(组)
Users

Groups 和 USers 是 Django 在用户管理应用中默认的。单机 Users 会看到当前项目仅有的一个用户 admin ,当然可以增加用户,留待作业。

下面进行登录设置,编辑 ./blog/admin.py 文件如下

from django.contrib import admin# 系统自动写入
from .models import BlogArticles# [1]

#将类 BlogArticles 注册到admin中
admin.site.register(BlogArticles)# [2]

代码[1]将BlogArticles类引入到当前环境,然后通过代码[2]将该类注册到admin中。

在调试状态下,如果没有新增加的文件,只是将原有文件修改了,则不需要重新启动Django服务(如果Django服务没有启动,请确保启动),然后打开浏览器,刷新页面,将看到新注册的BLog。

BLog
Blog Articles

单机 BLog Articles 右侧的“添加”按钮可以添加博客文章。将该页面对照 ./blog/admin.py 中的 BlogArticles 类,观察这里所填的表单中的各项是否与 BlogArticles 类中的属性对应。

内容填好之后,单机页面右下角的“SAVE”按钮,该博客文章将被保存到数据库中,可以再次使用 SQLite Manager 查看数据库。

从数据库中能够直观的看到刚才所保存的文档。注意“id”这一栏,在发布文章时选择 admin 作为 author ,并且 admin 在 Users 中的 id 是1,所以这记录了他的 id 值,即 author_id 为1——这就是 BLogArticles 类中 ForeignKey() 属性的效果。

为了后续操作需要,多发布几篇文章。
在 ./blog/models.py 中使用了 django.utils.timezone ,需要提前安装一个时区模块

pip install pytz

安装完毕,重启服务。
在文章的列表页,可以看到所有已经发布的文章的标题。

这样显示的列表信息太单一。为了让列表页的信息丰富,还可以在 ./blog/admin.py 中增加部分代码
(【注】运行前必须仅保留一次 admin.site.register 注册)

from django.contrib import admin
from .models import BlogArticles

#继承自 django.contrib.admin.ModelAdmin
class BlogArticlesAdmin(admin.ModelAdmin):

    #将 BlogArticles 类中的所有类数据属性,组合成几个功能
    list_display = ("title", "author", "publish")
    list_filter = ("publish", "author")
    search_fields = ("title", "body")
    raw_id_fields = ("author",)
    date_hierarchy = "publish"
    ordering = ["publish", "author"]

#将两个类 BlogArticles, BlogArticlesAdmin 注册到admin中
admin.site.register(BlogArticles, BlogArticlesAdmin)

注册完成后保存,再刷新浏览器,会看到新的可视化界面。

至于文件中 BlogArticlesAdmin 各个属性的含义,读者可以用控制变量法依次研究每个属性的含义,比如只保留第一个属性 list_display ,并将其他属性都注释掉,看看页面效果,就知道 list_display 的含义了。

1.2.3 知识点

1. 什么是HTTP
  • 请求(request):客户端到服务器
  • 响应(response):服务器到客户端
  • GET:向指定服务器发出请求,主要用于读取信息并显示
  • POST:向指定服务器提交数据,请求服务器进行处理(例如提交表单或者上传文件)

随着技术发展,现在另外一种协议正在被广泛使用——HTTPS。HTTP默认端口是80,HTTPS默认端口是443。

HTTPS比HTTP更加安全,应为HTTP以明文方式封装信息,而HTTPS以加密方式传送信息。

2.什么是URL

URL——“统一资源定位符”
标准格式是:
协议类型://服务器地址(hostname):端口号/路径(filepath)/文件名
http://localhost:8080/login/test.ipynb

其中

  • 协议类型:可以是http或https
  • 服务器地址:通常为域名,比如 localhost,也可以是IP地址,比如 127.0.0.1 。如果是默认的80端口,则不需要加端口
  • 路径:以“/”区别目录。对于GET请求方式,还可以用“?”发起参数,每个参数以“&”隔开,再以“=”连接键和值。如 http://localhost:8080?name=Eric&greet=Hello
  • 文件名:由服务器根据路径匹配相应程序文件

在本地调试,默认路径使用 http://localhost:8000/path 或者 http://127.0.0.1/path 作为地址,也可以修改本地的域名(/etc/hosts in Linux; /system32/drivers/etc/host in Windows)

3. 模型:ORM(对象关系映射)

早期的网站为“静态网页”——把需要在网页上呈现的信息直接写到HTML文件中,现在也有如网站中的“关于本站”这种简单介绍。

随着网站功能的增加,需要为访问者提供动态内容。动态内容就是当用户发起访问请求时, 网站实时的从数据库提取内容并呈现到网页上。在这类动态网站中,大多数是通过数据库实现对数据的保存和读取的,所以数据库是网站最基本和底层的组成部分。

Python本身有读取数据库的模块,所以可以通过SQL语句直接实现程序和数据库的交互。Django用另外一种方式解决了这个问题,在这种方式中不需要开发者使用SQL语句,而是使用更Python化的方式实现对数据库的操作,这就是ORM,即“对象关系映射(Object-Relational-Mapping)”。

ORM的作用是在关系型数据库和业务实体对象之间进行映射这样在操作业务对象时,就不需要再去和复杂的SQL语句打交道,只需要简单的操作对象的属性和方法。

Django的数据模型层大量使用ORM,这种处理方式并非都好,也有缺点。不过在通常的开发中,这种方式能带来很大便利性(【注】他的缺点并不因此消失)。

  • 可移植性强。ORM通常具有很好的可移植性,本教程中使用SQLite数据可,如果想改为MySQL数据库,只需要在settings.py文件中做好新数据库的配置,然后进行迁移数据的操作即可完成数据库的移植,不需要对ORM进行任何修改。
  • 安全性好。使用ORM后很少或者不需要执行SQL,所以这时就不必担心诸如SQL注入等形式的攻击了,况且ORM还提供了一个自动引用和转义输入变量的机制,开发者不用在安全性上花费太多时间,可以将精力集中在程序的业务逻辑和开发上。
  • 查询语法简单。面对较为复杂的查询,如果使用SQL语句,常常要写很多;而是用ORM,因为它本质上就是Python对象,能够让本来复杂的SQL语句变得简洁,所以能够实现更多的技巧。

上述三点,足以让我们至少在相当一部分项目中使用DJango封装的这个数据模型层完成对数据库的操作。就中小项目而言,这种方式足够支撑业务需要,并且能够快速开发。

Django的ORM表现方式就是编写数据模型类,这些类可以写到任何文件中,通常写在每个应用的 models.py 文件中。每个数据模型类都是 继承自 django.db.models.Model 的子类。应用的名称(小写字母)和数据模型类的名称(小写字母)共同组成一个数据库表的名称(形如“appname_modelname”,例如“blog_blogarticles”)。

当数据模型类写好之后,通过执行Django的数据迁移操作 (“python manage.py makemigrations” and “python manage.py migrate”)就能够创建相应的数据库表,用来保存网站项目的数据。以后如果要修改数据库表的结构,只需要修改数据模型类,迁移数据就能够实现数据库结构的调整。所以,即使现在还不太懂SQL,也可以顺利学习Django。当然以后还是需要学习数据库知识和相关语句。

.4 文档导读

.4.1 Object-relational mapping5

.4.2 What is an ORM and where can I learn more about it6

1.3 显示博客信息

将博客内容保存到数据库还不是发布博客的终极目的,之所以要在网上撰写博客(曾经有人说是写自己的网络日记,但这种说法存疑,真正的日记是不会公开的),目的就是通过吸引他人关注达到自己的某种目的,所以博客一定要显示出来。

显示博客信息的基本知识如下图所示:

显示博客信息
ORM含义
读取数据
ModelClass.objects.all()
ModelClass.objects.get()
ModelClass.object.filter()
简单的视图函数
参数
request
其他参数
get_object_or_404()
render()
传给模板的数据对象
模板文件
应用的URL

1.3.1 显示文章标题

要显示文章标题,就要把标题从数据库中读出来。在数据库中专门有一个 title 字段存储文章标题,要怎么才能读到它的具体内容呢?一种方法是通过SQL语句进行读取,另一种方法是使用 Django 进行读取。当我们在 ./blog/models.py 中创建了数据模型后, Django 就会自动提供数据库抽象的API,通过这个 API 可以创建、删除、修改和获取对象(增删改查),对此还有一个专门的名称 ORM。

下面就在交互模式中练习一下对数据库进行增删改查的操作。

[mysite]$ python manage.py shell #[1]
Python 3.5.2(default, xxx)
[GCC 5.4.0 xxx] on linux
Type "help",  "copyright", "credits" or "license" for more information.(InteractiveConsole)
>>>from django.contrib.auth.models import User
>>>from blog.models import BlogtArticles

特别关注语句[1],这里使用 “python manage.py shell” 的方式进入到交互模式中,而不是用读者在学习基础知识时常用的 Python 指令,因为用这样的方式已经将 Django 环境引入到了当前交互模式中。后面两行语句分别引入了两个对象, User 是Django 默认的, BlogArticles 是我们的 ./blog/models.py 中写的。
【注】数据模型类的方法函数"objects"有后缀"-s"

user = User.objects.get(username="admin")

上面这行语句的含义是获得 User 数据模型的字段 username 是 admin 的那个对象,或者说是数据库表 auth_user 中字段 username 的值是 admin 的那条记录(也是对象),因此变量 user 就是一个包含多个字段值的对象实例(如果用 Python 中有关类的知识来理解,可以认为是创建了一个 User 类的实例,这个实例对象有一些属性和方法,下面这个语句就是这个实例对象的属性)

#实例 user,来自类 User
>>>user.username
'admin'
>>>user.id
1
>>>user.password
'xxx'
>>>type(user)

对 BlogArticles 类可以进行类似操作,让然也可以将数据库中的所有记录读取出来。

#实例 blog,来自类 BlogArticles
>>>blogs = BlogArticles.objects.all()
>>>blogs
, , ]>
>>>for blog in blogs:
...    print(blog.title)
...
《aaa》aa
b.b.b
cc cc

貌似我们需要的东西已经被读取出来了——文章标题列表。就是这种方式可以轻易读取到每篇文章的标题,其本质是一个由多个 BlogArticles 类的实例组成的序列对象(【注】每一篇博客就是一个独立的 BlogArticles 类)。对于这个查询结果,在 Django 中被称为 QuerySet。

【注】对比 BLogArticles 类,Blogs实例其中:有 title, author, body, publish;没有 ordering 。

下面就利用刚才学习的知识写一个函数,专门用来读取文章标题,这个函数通常被写在 ./blog/views.py 文件中,当然也可以写到任何其他名称的文件中,但此处我们遵循此常规。

# in ./blog/views.py
from django.shortcuts import render
from .models import BlogArticles

def blog_title(request):# [2]
	blogs = BlogArticles.objects.all()# [3]
	return render(request, "blog/titles.html", {"blogs":blogs})# [4]
	#类比 web.py 中的 "return render.index(name=)",本质为 “处理 reuqest 的部分与渲染部分分离开 ”

像 blog_title() 这个函数,是在视图文件 views.py 中编写的一个函数,这种方式我们称之为“基于函数的视图”,这个函数被叫做“视图函数”。在后面的章节会出现另外一种“基于类的视图”的编写方式。这两种方式有着不同的应用场景,当学习了基于类的视图的编写方式之后,自然能够理解两者的差异。

刚刚编写的函数 blog_title(),是本教程中第一个响应用户的函数,注意他的基本格式。

在语句[2]中,函数的参数是 request ,这个参数负责响应所接收到的请求且不能缺少,并总是处于第一的位置。除这个不可或缺的参数外,还可以根据需要在其后增加别的参数。

语句[3]利用前面在交互模式中使用的语句得到所有的 BlogArticles 对象实例。

语句[4]以 return 结束当前函数,并返回结果。render() 的作用是将数据渲染到指定模板上,**render_to_response() **的效果与之相同,但两者还是有所差别的。render() 方法是 render_to_response() 的快捷方式,会自动使用 Resquest; render() 的第一个参数必须是 request ,然后是模板位置和所传送的数据,数据是用类似字典的形式传送给模板的。

在 render() 中出现的 blog/titles.html 就是标题列表的前端展示页面——即“模板”。在每一个应用中都可以有一个专门的模板目录,现在我们进入应用 blog 的目录 ./blog ,建立一个子目录 templates ,名称和位置必须与此,再按照如下方式建立有关文件和子目录。

|--templates
  |--base.html
  |--blog
  	|--titles.html

templates 目录是 Django 默认的存放本应用所需模板的目录,如果不用自定义的方式指定模板位置, Django 会在运行时自动来这里查找 render() 函数中所指定的模板文件。

在模板目录中,有一个 base.html 文件,这个文件时将所有模板中公共的部分(类似 web.py 中的 “layout.html”)抽取出来,在其他文件中只需要编写个性部分的代码。也就是说,在 Django 的模板文件中,是可以有“继承”功能的。在本项目中,就这样来编写base.html。

base.html




	
	
	
	{% block title %}{% endblock %}# [5]
	# [6]
	# [7]


	
{% block content %}# [8] {% endblock %}
# [9] # [10]

下面对代码的部分内容进行说明。

语句[5]和[8]都是在模板中定义了
即形似下列语句:

{% block name %}
{% endblock %}

在 Django 模板中,{% block name %}是块标签,用它来包裹块内容,表示其间的内容可以自定义, name 是块的名称。语句[5]表示在模板中定义一个 title 的块。块的结束标签都是{% endblock %}。

代码显示,{% block title %}被放在头部分里, {% block content %}被放在体部分里。

语句[6]和[7],形似下列语句:


引入了样式表文件(css),只不过没有放在本地,而是通过网络地址引用远端的文件。

语句[9]和[10],形似下列语句:


则是引入了前端模板文件中常用的 JavaScript*库中的 JQuery 文件及有关其他 JavaScript 文件。

这就是一个基础模板。

下面编写与标题列表相对应的模板,即 ./templates/blog/titles.html 文件:

{% extends "base.html" %}# [11]

{% block title%}blog titles{% endblock %}#  [12]

{% block content %}# [13]
    {% for blog in blogs %}# [14]
  • {{blog.title}}
  • # [15] {% endfor %}

heading_2

paragraph

\
{% endblock %}

上面这段间断的代码包括了常用模板中的内容,下面分别说明。

语句[11]

{% extends "base.html %}

表示继承已经建立的模板 base.html ,还可以理解为对 base.html 的扩展,及下面的代码都是基于 base.html 的,对其中的部分内容给予新的定义。

前面在 base.html 中已经定义了 {% block title %} {% block content %}块,语句[12]和[13]则是在本模板文件中对“父模板”——base.html——中的同名称块标签进行重写

在前面 blog_title() 函数的语句[4]中,以 {“blogs”:blog} 的方式向本模板文件中传入了 blogs 变量所引用的 QuerySet 对象,其包含了所有 BlogArticles 类的实例对象(即“blogs = BlogArticles.objects.all()”),即从数据库表中读出来的所有记录(每条记录都可以看作是一个实例对象),并在模板文件中以变量 blogs ({“blogs”:blogs} 中的第一个 blogs ,如[14]处的 blogs)来代表。在本模板文件语句[13]中的 {% block content %}{% endblock %} 里面,将所有博客标题列表展现出来。

语句[14]中以 {{(blog.title}} 双层花括号 的方式表示此处显示变量引用的数据。 blog 是从 blogs 的 QuerySet 序列中得到一个实例对象, title 是 BlogArticles 数据模型类的一个字段, blog.title 则表示某个实例的 title 字段,以这种方式得到一篇博客(即 BlogArticles 类的实例)的 title 字段(即 类数据属性)的值。

函数和模板都编写好之后,要想通过网页访问,还需要做好另外一项重要的工作,就是进行 URL 配置。首先要配置 ./mysite/urls.py ,在这个文件中配置本项目的各个应用。

from django.conf.urls import url, include
from django.contrib import admin # 上次引入用于注册应用
urlpatterns =[
	url(r'^admin/', admin.site.urls),# 将指定 path 注册到应用中
	url(r'^blog/', include('blog.urls', namespace="blog", app_name="blog")),# ???
]

【注】类似 web.py 中 ./skeleton/bin/app.py 中的对象 urls=(’/’, ‘index’, ‘/admin’, ‘admin.site.urls’,)

如果用户在浏览器的地址栏中输入了类似 http://localhost:8000/blog/ 的地址,通过这里的 URL 配置,将此请求转向到 blog 应用的 urls.py ,即 ./blog/urls.py

在本教程的后续项目中,都是在 ./mysite/urls.py 中配置应用的 URL 后,再到某个应用的 urls.py 文件中配置该应用的具体URL。

接下来就需要配置 ./blog/urls.py 中的URL,当然这个文件还没有,需要先创建,然后配置如下URL:

from django.conf.urls import url
from . import views

urlpatterns =[
	url(r'^$', views.blog_title, name="blog_title"),
]

这里的 “r’^$’” 是正则表达式的写法,因为是在 blog 应用中,所以是访问 blog 应用的根,即 http://127.0.0.1:8000/blog/ 。 views.blog_title 声明了响应这个请求的函数(注意这里调用的是函数对象,没有小括号 “()”)。

接下来就可以在浏览器访问 http://127.0.0.1:8000/blog 或者 http://localhost:8000/blog ,将看到渲染后的网页。

1.3.2 查看文章内容

目标:用户单击文章标题,之后呈现其详细内容
操作:首先为文章标题 title 增加超链接,链接对象为文章详情页。
【注】此处仅为“添加超链接的基本格式”

修改之前仅仅展示文章标题 title

{% for blog in blogs %}
	{{block.title}}
{% endfor %}

修改之后,点击 title 将连接到文章编号 id

{% for blog in blogs %}
	
  • {{block.title}}
  • {% endfor %}

    再查看页面,发现每个标题都有了超链接,此处是通过文章的id获得其详细内容的。

    可以在交互模式下尝试

    [mysite]$ python manage.py shell
    >>>from blog.models import BlogArticles
    >>>article = BlogArticles.objects.get(id=1)
    >>>article.body
    '[data_of_body]'
    >>>article.title
    '[data_of_title]'
    >>>article.author
    
    >>>article.author.username
    'admin'
    >>>article.publish
    datetime.datetime(2016,10,6,14,2,49,682076,tzinfo=)
    

    在交互模式中测试成功,接下来就可以将这种方法用在博客详情页的功能中了。

    第一步编辑视图文件(及其函数) ./blog/views.py,增加响应“查看文章详情请求”的函数 blog_article() :

    def blog_article(request, article_id):# [1]
    	article = BlogArticles.objects.get(id=article_id)# [2]
    	pub = article.publish
    	return render(request, "blog/content.html", {"article":article, "publish":pub})
    

    语句[1]中的函数的参数列表比前面的 blog_title() 多了一个参数 article_id ,如何向函数传入这个参数呢?在后续关于URL配置的部分会讲到。
    语句[2]通过 article_id 参数创建一个 BlogArticles 类的实例,也可以说是读取 id=article_id 的记录。
    对比已经创建的两个函数发现其基本结构是一样。使用基于函数的视图,都是按照这种方式编写函数的。

    然后编写与之对应的模板,创建 ./templates/blog/content.html 文件,如下

    {% extends "base.html" %}
    
    {% block title %}blog article{% endblock %}
    
    {% block content %}
    

    {{article.title}}

    {{article.author.username}}{{publish}}

    {{article.body}}

    广告

    SHIT can't show this img.
    {% endblock %}

    【注】不用修改 base.html ,直接使用第一版即可

    第二步配置 URL ,因为还是针对 blog 这个应用而言,所以不需要修改 ./mysite/urls.py ,即" ‘/blog’ --> ‘blog.urls’ "这个映射路径不变。
    只需要在 ./blog/urls.py 中增加新的 URL 路径

    from django.conf.urls import url
    from . import views
    
    urlpatterns=[
    	url(r'^$', views.blog_title, name="blog_title"),
    	url(r'(?P\d)/$', views.blog_article, name="blog_detail"),# [MARK]
    ]
    

    在函数 blog_article(request, article_id) 的参数里表中,除了 request 外,还有 article_id ,这个参数的目的是获得 URL 中每篇博客文章的 id ,即 ./blog/templates/blog/titles.html 文件中的超链接 所产生的请求地址中最后的数字,例如 http://127.0.0.1/blog/1 。因此,使用 r’(?P\d)/KaTeX parse error: Expected 'EOF', got '\d' at position 67: …(?P\̲d̲)/’ 来捕捉住url中的最后一部分并命名为 ’article_id’== ”)

    现在访问博客标题网页+文章编号(id)( 形如 http://127.0.0.1:8000/blog/1 ),然后查看某博客的详细内容,注意提前确认 Django 已经启动。

    注意观察网址,可以修改 id 。如果 id 数值超过文章总数量,将报错"DoesNotExist at /blog/[id]"以及显示详细的报错信息,这是因为我们正处于DEBUG状态。
    为了避免出现上述错误,应该在响应此请求的函数中对这种异常请求进行处理。

    修改 ./blog/views.py 中的函数 blog_article()

    from django.shortcuts import render, get_object_or_404# [1]
    from .models import BlogArticles
    
    def blog_article(request, article_id):
    	#article = BlogArticles.objects.get(id=article_id)
    	article = get_object_or_404(BlogArticles, id=article_id)# [2]
    	pub = article.publish
    	return render(request, "blog/content.html", {"article":article, "publish":pub})
    

    语句[1]引入了 get_object_or_404() 方法,这个方法能够帮助我们简化对请求网页不存在时的异常处理。当请求对象不存在时,会抛出 DoesNoeExist 异常。对于这个异常,可以用 try…except… 捕获,在 except 中可以使用 raise Http404() 来处理。这里我们使用 get_object_or_404() ,省略了一些代码,此方法的参数列表为 get_object_or_404(klass, *args, **kwargs) 。

    • klass:一般是数据模型类的一个类(继承自 django.db.models.Model )
    • *args 和 *kwargs:查询时的条件参数

    Django中有很多默认方法,能够帮助我们提高开发效率,减少代码量。

    这时,再次请求那个不存在的地址,将显示404错误(即 “Page not found(404”)。

    至此,一个博客就创建完成了,虽然简陋但显示了 Django 在网站开发中的最基本结构。
    为了练习,可以将代码删除后凭借自己的理解单独做一遍,直到对此过程熟悉位置。

    1.3.3 知识点

    1. Django 的 MTV

    在动态网站开发中,普遍遵循 MVC 模式,这种模式是在1980年代为程序语言 Smalltalk 发明的一种软件架构。 MVC 模式的目的是实现一种动态的程序设计,使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。

    MVC(Model-View-Controller)模式就是把数据存取逻辑、业务逻辑和表现逻辑组合在一起的模式。在MVC模式中,Model代表数据存取层;View代表系统中选择显示什么和怎么显示的部分;Controller是系统中根据用户输入和需要访问模型,以决定使用哪个视图的部分。MVC模式已经成为网站开发的共识,Django同样遵循这种模式,或者说Django是一种MVC的框架,具体表现如下:

    • M:数据存储和读取部分,由 Django 的数据模型部分承担
    • V:确定要显示哪些数据及如何显示,由视图和模板进行处理
    • C:根据客户端请求映射到相应的视图,由 Django 框架根据 URLconf 设置,调用相应的函数

    在 Django 中, Controller 部分事实上是有框架自动处理的,不需要开发者做什么,于是 Django 就转变为模型(Model)、模板(Template)和视图(Views)三部分了,可以称之为 MTV 模式。

    • M:模型(Modes),即数据存取层,模型是网站项目的基础,主要负责处理与数据相关的事务,如读取、写入数据等
    • T:模板(Template),即表现层,处理与表现有关的事务,例如如何在页面中显示相关内容
    • V:视图(Views),即业务逻辑层,包含存取模型(blogs = BlogArticles.object.get(…))及调取相应模板(return render(…, ''xxx.html", {“blogs”:blogs))的相关逻辑,是M(模型)和T(模板)之间的桥梁。当Django得到用户的请求(request)后,根据URL映射关系调用相应的视图,试图则调用和处理有关数据。与模板相比,视图确定访问者能看到哪些数据,而模板确定用什么方式看到这些数据

    Alt

    在图中可以看到很多“中间件”,这就是Django的核心,所有的请求、返回命令都由中间件来完成。这些中间件被定义在 settings.py 的 MIDDLEWARE_CLASSES 中。

    1. Django核心理念
      Django是一个开发框架,并且是有理念的开发框架。
    • 更Python化。Django使用Python编写的,他的很多约定也遵循Python的规范,有Python开发经验的用户能够很容易从事Django开发。
    • DRY。这是很重要的原则,即 Don’t repeat yourself (不要重复自己)。这个原则可以作为所有程序开发的原则,但做起来有难度。Django中通过某些方式,在这方便做得相当不错,比如允许在数据模型中创建方法实现某些针对该模型实例的常见操作。在教程后续,读者要牢记该原则并领会其含义。
    • 松耦合与灵活。Django使用MTV模式,实现了数据库访问、业务逻辑、模板显示等层面的松耦合和各自的灵活操作。在模板系统层面,用户可以自由的配置多种数据库,当然也可以不用Django封装的数据库API,而是通过SQL语句直接操作数据库。不过本教程的示例中这些事情都不需要读者做。只是告诉读者可以不遵循Django默认的任何东西,而是根据自己的需要自定义。
    • 快速开发。Django在不同层面上都提供了快速开发所需要的工具,比如通用视图(???),能够非常简单的组建一个网站。
    1. URL配置和查询
      本章节所讲解的URL配置,是在Django开发中常用的一种方式。

    当用户用过浏览器请求某个URL时,Django会根据路径依次在 URLconf 中查询,并将第一个符合条件的映射关系作为查询结果,例如,访问 http://localhost:8000/blog/1/ ,其过程如下:

    .1 localhost:8000 这是主域名部分,不进行查询
    .2 /blog/ 首先在 ./mysite/urls.py 中查询,遇到符合条件的URL映射( url(r’^blog/’, include(“blog.urls”, namespace=“blog”,app_name=“blog”)), ),根据此映射中的描述,到 blog.urls( ./blog/urls.py )中查询。
    .3 /1/ 在 ./blog/urls.py 中由URL( r’(?P\d)/$’, views.blog_article, name=“blog_detail” )配置,请求的路径正好符合这里的正则表达式,从而确定了最终访问的视图函数 views.blog_article 。

    这种URL的配置方法显然在多应用的项目中是比较合适的。如果是单应用的项目,可以直接在 ./mysite/urls.py 中配置 .3 中所示的内容(即 直接配置到视图)

    from django.conf.urls import url
    fro . import views
    
    urlpatterns=[
    	url(r'^$', views.blog_title, name="blog_title"),
    	url(r'^content/(?P\d)/$', views.blog_article, name="blog_detail"),
    ]
    

    上例中 “^” 的作用在于准确说明访问路径。假设有两个URL( /bar/content 和 /content/),显然这两个路径不同,如果用 r’content/’ 则可以匹配这两个路径(路径末尾有 cotent 就会被匹配上),就会出现按照顺序先查到谁就匹配谁,可能没有满足我们预定的需求;而 r’^content’ 只能匹配 /content/。本质上,这里其实使用的是正则表达式, "^“的作用是“匹配字符开始位置”, " ^content/” 表示匹配那些以 content/ 开头的字符串;$的作用是匹配输入字符的结尾位置,可以简单理解为”“ article_id 的整数”之后就结束当前URL。

    “?P\d’” 也是表示正则表达的方式,这里匹配的是一个数字字符,并且该数字字符赋予 article_id 参数,传给 views.blog_article() ,这样 views.blog_article() 函数的第一个参数 request ,和第二个参数 “xxx/xxx.html” 都将接收 article_id 传入的值。

    对于 URLconf 中的视图函数的写法,本教程使用 views.blog_article 的方式,也可以写成字符串,如 “views.blolg_article” ,两种写法没有优劣之分,但在同一个项目中要统一。

    .4 文档导读

    .4.1 URL7

    .4.2 URL dispatcher8


    1. https://docs.djangoproject.com/en/1.10/ref/databases/ ↩︎

    2. https://docs.djangoproject.com/en/1.10/intro/tutorial01 ↩︎

    3. https://Djangosites.org/ ↩︎

    4. https://docs.djangoproject.com/en/1.11/faq/ ↩︎

    5. https://en.wikipedia.org/wiki/Object-relational_mapping ↩︎

    6. https://stackovertlow.com/questions/1279613/what-is-an-orm-and-where-can-i-learn-more-about-it ↩︎

    7. https://en.wikipedia.org/wiki/URL ↩︎

    8. https://docs.djangoproject.com/en/1.10/topics/http/urls/ ↩︎

    你可能感兴趣的:(学习笔记,Django)