Django2 web实战01-启动项目

0 前言 = 瞎说

能写web的语言有好多。python算是难度较低,入门较快的脚本语言。Django是python的web框架,详情不多叙,见:

Django-wiki介绍

# Django历史---------->>>>>这段可以不看。
在 Web 早期阶段,开发者手动编写每个页面。更新网站要编辑 HTML;
重新设计要重新制作每一个网页,而  且一次只能改一个网页。
随着网站体量的增大,这种方式立马变得繁琐、浪费时间,最终变得不切实际。 

NCSA(National Center for Supercomputing Applications,国家超级计算应用中心,
第一款图形 Web 浏览器 Mosaic 就是在这里开发出来的)一群富于创新的黑客
解决了这个问题,他们让 Web 服务器派生外部程序,  动态生成 HTML。
他们把这一协议称为通用网关接口(Common Gateway Interface,CGI),
自此,Web 完全  变了样。如今,很难想象 CGI 带来的变革:
CGI 不再把 HTML 页面视作硬盘中存储的文件,而是把页面看  做资源,
可以按需动态生成。 

CGI 的开发促使了第一代动态网站的出现。然而,CGI 自身也有问题:
CGI 脚本包含大量重复的样板代码,  导致代码难以复用,而且新手难以编写和理解。 

PHP 解决了这些问题中的多数,在 Web 开发界引起了一阵风暴。
PHP 现在是创建动态网站最流行的工具,  
多门类似的语言(ASP、JSP,等等)都参照了 PHP 的设计原则。
PHP 的主要创新是易于使用:PHP 代码直  接嵌入普通的 HTML 中;
对学过 HTML 的人来说,学习曲线极为平缓。 

但是,PHP 也有自身的问题:就是因为易于使用,写出的代码凌乱、重复,设计不周。
更糟的是,PHP 没有  为程序员提供多少防止安全漏洞的保护机制,
很多 PHP 开发者意识到这一点再去学习相关的知识就晚了。 

上述问题以及类似的缺陷直接促使了“第三代”Web 开发框架的涌现。
Web 开发的新方式也提升了人们的雄  心,现在 Web 开发者每天所做的工作越来越多。 

Django 就是为了迎接这些雄心而诞生的。
Django 是从真实的应用中成长起来的,由美国堪萨斯州劳伦斯的一个 Web 开发团队编写。
它诞生于 2003 年秋天,那时 Lawrence Journal-World 报社的 Web 开发者 
Adrian Holovaty 和 Simon Willison 在尝试使用 Python 构建应用。 

World Online 团队负责制作和维护本地的几个新闻网站,在新闻界特有的快节奏开发
环境中逐渐发展壮大。  
那些网站(包括 LJWorld.com、Lawrence.com 和 KUsports.com)的
记者(和管理层)不断要求增加功能,
而且整个应用要在紧张的周期内快速开发出来,通常只有几天或几小时。
因此,Simon 和 Adrian 别无他法,只  能开发一个节省时间的 Web 开发框架,
这样他们才能在极短的截止日期之前构建出易于维护的应用。 

经过一段时间的开发后,那个框架已经足够驱动世界上最大的在线网站了。
2005 年夏天,团队(彼时 Jacob Kaplan-Moss 已经加入)决定把框架作为开源软件发布出来。
他们在 2005 年 7 月发布了那个框架,将其命名  为 Django——取自爵士吉他手 Django Reinhardt。 

这段历史相当重要,因为说清了两件要事。首先是 Django 的“发力点”。
Django 诞生于新闻界,因此它提供了几个特别适合“内容型”网站使用的功能(如管理后台)。
这些功能适合 Amazon.com、craigslist.org 和 washingtonpost.com 这样动态的数据库驱动型网站使用。 

不过,不要因此而灰心。虽然 Django 特别适合开发这种网站,
但是这并没有阻碍它成为开发任何动态网站的有效工具。(某些方面“特别”高效与某些方面不高效是由区别的。) 

第二点是,Django 最初的理念塑造了开源社区的文化。Django是从真实代码中提取出来的,
而不是科研项目或商业产品,它专注于解决Django的开发者自身所面对的问题。因此,
Django一直在积极改进,几乎每一天都有变化。
Django框架的维护者一心确保它能节省开发者的时间,确保开发出的应用易于维护,
而且在高负载下的性能良好。 

使用 Django 能在极短的时间内构建全面动态的网站。Django 的主旨是让你集中精力在有趣
的工作上,减轻  重复劳作的痛苦。为此,它为常用的 Web 开发模式提供了高层抽象,为常
见的编程任务提供了捷径,还为解  决问题提供了清晰的约定。与此同时,Django 尽量做到
不挡路,允许你在必要时脱离框架。 

写项目就好比骑自行车,看完上面的背景,接下来就上代码体验了,直接开撸。

按照草拟的项目结构,大致上有5块内容:

  • 启动项目
  • 新增用户
  • 文件安全
  • 缓存列表
  • 项目部署
00.项目结构图pro.jpg

项目初步规划要完成一个电影列表/详情的查看,评论,投票,得分,支持文件上传,内容安全, 注册/登陆/登出,内容缓存等。

大致构思了下,要写好还是需要处理不少细节问题的。比如了解下Django的编码套路,处理关联关系,数据库的CRUD等,一步步的来吧。

本文主要实现项目的第一部分,启动项目部分。

1 环境准备

  • 安装python 默认的就是最新的3.6.*

不同平台安装方式可能不同,自行百度一个。
https://www.python.org/downloads/

  • 安装django 默认的是最新的2.*
pip:pip install packages缩写,是python的包管理工具,用于安装python包。
pip install django // 安装最新版
pip install django==2.*.* // 安装具体版本
  • 安装pycharm IDE

从网上下载一个就行了,然后搜一个激活码完事儿。

  • 安装mysql

什么平台都一样,mac/linux/win,都记得配置环境变量;我用的Mac环境。

这个软件安装部分,还是得多百度,可能存在平台差异。

顺便提一句,如果有任何语言的开发基础,其实回头看python都会觉得简单,老说没有python基础,看书啊! 突然宋小宝的画面出现了,哈哈哈。有时候一门语言可能知道40%左右就可以开干了,边撸边回头翻书就好。

2 创建项目 MyMovie

命令行创建

django-admin

cd workspace
django-admin startproject MyMovie

可以用pycharm创建
默认项目结构

MyMovie\
    MyMovie\
        __init__.py\
        settings.py\
        urls.py\
        wsgi.py\
    manage.py\

项目结构解析:

  • 外层MyMovie根目录是项目容器。这个外层名称对Django来说没什么用,可以根据喜好改名。

  • 根目录manage.py 是一个命令行实用脚本,可以根据不同方式与Django项目交互。如数据库迁移,跑测试,启动开发server等,会经常用到manage.py。

  • 内层MyMovie目录,是项目的Python包。导入这里面的内容时要用该目录名称。如MyMovie.urls。

  • 内层MyMovie/init.py是个空文件,目的是让Python知道该目录是Python包。

  • 内层MyMovie/settings.py是Django项目的配置。

  • 内层MyMovie/urls.py是整个项目的URL配置,即Django驱动的网站的目录。每个Web app的请求都会被指向urls文件中已配置过的匹配的第一个view。

  • 内层MyMovie/wsgi.py是兼容WSGI的web服务器的接口,用于服务项目。SWGI,Web Server Gateway Interface,让Django项目与web服务器互相交互的接口。如将Django项目部署到Docker上。

2.1 解析settings.py文件

TIME_ZONE = 'UTC'. # 时区

# Django中自带的激活的全部Django应用,自建的app也要配置到这里面
INSTALLED_APPS = [
   'django.contrib.admin', # 管理后台
   'django.contrib.auth', # 身份验证系统
   'django.contrib.contenttypes', # 内容类型框架
   'django.contrib.sessions', # 会话框架
   'django.contrib.messages', # 消息框架
   'django.contrib.staticfiles', # 管理静态文件的框架
]

Django项目中默认包含这些app,为常见场景做的约定。
如果用到数据库表,使用之前要在数据库中先建表,为此,运行以下命令:

python manage.py migrate 

数据库配置
用mysql示例,前提是系统中有数据库,且user/password等信息都OK;
终端创建mymovie数据库

# 命令行进入mysql 并输入密码
mysql -uroot -p
# 查看当前存在的数据库
mysql> show databases;
# 创建数据库
mysql> create database mymovie;
Query OK, 1 row affected (0.10 sec)
mysql> show databases;
+--------------------+
| Database  |
+--------------------+
| blog_project_db |
| information_schema |
| mymdb |
| mymovie |
| mysql |
| performance_schema |
| sys |
+--------------------+
7 rows in set (0.00 sec)
mysql>

在MyMovie/init.py文件中配置mysql数据库:

import pymysql

pymysql.install_as_MySQLdb()

settings.py文件中配置mysql

DATABASES = {
   'default': {
     'ENGINE': 'django.db.backends.mysql', # 数据库引擎
     'NAME': 'mymovie', 
     'USER': 'root',
     'PASSWORD': 'root@123456',
     'HOST': '127.0.0.1',
     'PORT': '3306',
   }
}

2.2 创建core应用

Django应用遵循MVT模式,与传统的MVC模式无异,只是叫法不同:

MVT模型 职责说明
Models 用来处理数据库读写操作
Views 用来处理HTTP请求,启动模型的操作然后返回HTTP响应数据
Templates 用来展示响应内容

2.2.1 命令行 创建app

cd MyMovie
python manage.py startapp core

2.2.2 注册新建的core app

settings.py文件中 添加刚创建的core app
每个注册的app后面必须带【,】

INSTALLED_APPS = [
   'core',
   'django.contrib.admin',
   'django.contrib.auth',
   'django.contrib.contenttypes',
   'django.contrib.sessions',
   'django.contrib.messages',
   'django.contrib.staticfiles',
]

2.2.3 创建Model Movie

Django model是从Model衍生继承过来的,有多个Fields字段。

数据库方面,一个Model对应一个数据库表,一个Model实例对应一行,Field字段对应一列。

Django model 对应数据库
Model类
Model实例
Field字段

用Django的ORM,用Python和Django来写model类来处理数据库,而不是直接写SQL语句。

core/models.py

from django.db import models
# 编写第一个model Movie
class Movie(models.Model):
 NOT_RATED = 0
 RATED_G = 1
 RATED_PG = 2
 RATED_R = 3
 # 评分 级别
 RATINGS = (
 (NOT_RATED, 'NR - 没有评分'),
 (RATED_G, 'G - 普通观众'),
 (RATED_PG, 'PG - 父母的引导和规范'),
 (RATED_R, 'R - 限制级'),
 )

 title = models.CharField(max_length=140)
 plot = models.TextField()
 year = models.PositiveIntegerField()
 rating = models.IntegerField(
 choices=RATINGS,
 default=NOT_RATED
 )

 runtime = models.PositiveIntegerField()
 website = models.URLField(blank=True)

 def __str__(self):

 return '{} ({})'.format(self.title, self.year)
  • Movie继承自models.Model,models.Model是所有Django模型的基类。

  • title 会转成数据库表中的一个列字段,长度为140,类型为varchar;

  • plot 会转成数据库的text列,

  • year 会转成数据库的integer列,Django存储之前会验证数据,确保是0或者更高。

  • rating 多选列。是一个integer列。可选参数choices有一个集合的游标。

Django会向model种新增一个实例方法:get_rating_display(),返回存储在模型中符合第二个参数条件的数据。

  • runtime 与year一样。

  • website 大多数数据库列字段没有URL类型,但数据驱动web应用经常需要存储URL。URLField默认是长度200的varchar列,也可以通过max_length参数设置。URLField自带了验证逻辑,可以鉴别该URL是否为有效。blank参数由admin应用使用,用来确认该参数是否可以为空。

__str__(self)方法,将Django模型转化成可视化的字符串,类似java中的toString()。有助于debug以及输出对象内容。

Django的ORM会自动增加一个字增长的列:id。我们无需关心该字段。

Django的DRY理论:Donnot Repeat Yourself

2.2.4 数据库迁移

我们有了模型,需要在数据库中创建匹配该模型的表table。

用Django可以生成该表。

cd MyMovie
python manage.py makemigrations
# 一键操作  迁移所有的app的数据库  根据model建表
python manage.py migrate  

终端执行结果

$ python manage.py migrate
Operations to perform:
 Apply all migrations: admin, auth, 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 auth.0009_alter_user_last_name_max_length... OK
 Applying sessions.0001_initial... OK

执行完之后,数据库中就会生成一张表: core_movie

mysql> show tables;
+----------------------------+
| Tables_in_mymovie |
+----------------------------+
| auth_group  |
| auth_group_permissions  |
| auth_permission |
| auth_user |
| auth_user_groups  |
| auth_user_user_permissions |
| core_movie  |
| django_admin_log  |
| django_content_type |
| django_migrations |
| django_session  |
+----------------------------+
11 rows in set (0.00 sec)
mysql>

在Django的应用中才会存在数据库迁移,而不是在项目目录。

2.2.5 创建第一个Movie

类似python,Django提供了一个交互式的REPL来尝试一下。
Django的交互脚本完全链接数据库,所以我们可以在shell中进行model的增删改查。

cd MyMovie
python manage.py shell
# 执行CRUD

也可以在pycharm的python console窗口中进行操作,一样的道理。
创建一条Movie数据:

sleuth = Movie.objects.create(title='Sleuth', plot='an snobbish writer who loves games', year=1972, runtime=138,)
clear
Traceback (most recent call last):
 File "<input>", line 1, in <module>
NameError: name 'clear' is not defined
sleuth.id
3
sleuth.get_rating_display()
'NR - 没有评分'

查看数据库表中是否有刚才创建的Movie对象;
可以在命令行中访问数据库表,也可以在pycharm的database窗口中可视化查看。

mysql> select * from core_movie;
+----+--------+------------------------------------+------+--------+---------+---------+
| id | title | plot  | year | rating | runtime | website |
+----+--------+------------------------------------+------+--------+---------+---------+
| 1 | Sleuth | an snobbish writer who loves games | 1972 | 0 |  138 |  |
+----+--------+------------------------------------+------+--------+---------+---------+
1 row in set (0.00 sec)
mysql>

objects, 是模型model的默认manager。是一个查询model表的接口。同时提供了一个create()方法来创建和保存实例。每个model必须至少有一个manager,Django默认提供了一个manager。通常可以自定义manager,这个会在后续会详述。

id,是数据库表的主键,Django自动生成的。

get_rating_display(), 由Django生成,因为rating字段提供了一个choices的元祖。我们在调用create()的时候没有提供rating字段值,因为rating字段有默认值0.

get_rating_display()方法查找并返回响应的value。Django将生成一个方法,遍历拥有choices参数的所有字段。

接下来用Django Admin app来创建一个后台,来管理movies。

2.2.6 创建Movie的admin

快速生成一个后端UI,来快速填充项目数据。
为了让Django的admin app使用我们新建的models,执行下列步骤:

1 注册我们的model
2 创建一个superuser,可以访问后台
3 运行开发server
4 浏览器中访问后台

将Movie注册到admin中,
core/admin.py

from django.contrib import admin
from core.models import Movie
admin.site.register(Movie)

注册成功。
创建一个superuser
hubery
hubery2018

$ python manage.py createsuperuser
Username (leave blank to use 'hubery'): hubery
Email address: 934531487@qq.com
Password:
Password (again):
Superuser created successfully.

运行开发server

$ python manage.py runserver
Performing system checks...
System check identified no issues (0 silenced).
July 19, 2018 - 05:35:58
Django version 2.0.6, using settings 'MyMovie.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

浏览器中打开提示的链接:

http://127.0.0.1:8000/

会显示出Django加载成功的页面。


01.浏览器查看开发server.jpg

进入admin后台页:
浏览器中输入:

http://127.0.0.1:8000/admin

根据提示,输入刚才创建的superuser信息,成功登陆进去之后,可以可以在线编辑数据。


02.admin后台页面可以可视化的编辑内容.png

为了方便,我们可以趁热打铁的录入一批movies。


03.admin后台手动录入数据.jpg

2.2.7 创建MovieList视图

当Django接收到一个请求request,它用request的路径和项目的URLConf来匹配,找到找到在URLConf中配置的view并将该请求传进去,这个view会返回一个HTTP response。

Django视图View可以是函数,也可以是

FBVs Function-Based Views 基于函数的View
CBVs Class-Based Views 基于类的View

我们写一个视图展示movie列表。

core/views.py

from django.http import HttpResponse
from django.views.generic import ListView
from core.models import Movie

# 基于class的视图
class MovieList(ListView):
   model = Movie
   # 基于函数的视图
   def test(request):
     return HttpResponse('hello world.')

ListView至少需要一个model属性。将会查询model的所有行rows,将结果传递给template展示,在返回结果中渲染出template。同时提供了好多回调函数,使得我们可以用来替换默认行为。

ListView是怎么知道如何查询Movie的所有对象的? 针对这个问题,我们得研究下managerQuerySet类。每个model都有一个默认manager。Manager主要用来提供各种方法来查询对象,如all(), 这些方法返回QuerySet。

QuerySet类是Django查询数据库的结果集,有很多方法,包含:filter()来限制查询结果。QuerySet一个很好的特性是:它是惰性的,在我们从QuerySet获取model之前,不会赋值。另一个不错的功能是:例如filter()之类的方法,使用查找表达式,这些表达式可以是字段名称,也可以跨越关系模型。

我们将会在整个项目中这样处理。

注:所有manager类(objects)都有一个all()方法,返回一个QuerySet,

Movie.objects.all()

相当于:

select * from core_movie;

所以,ListView会检查该ModelList视图是否拥有model属性,如果存在,它会知道Model类拥有一个默认manager(即objects),并且这个manager拥有all()方法

2.2.8 添加用来展示movieList的模版template

模版引擎会搜索project和app工程结构下的templates文件目录,根据视图view来找相应的template;

ListView同时也提供了一个约定/规矩/惯例template的存放目录位置,我们自定义的模版必须遵守这个约定,如:
<app_name>/<model_name>_list.html

core/movie_list.html

即:在core app中,
app_name是core, model_name是Movie;
所以模版文件名为:
<model_name>_list.html =>>> movie_list.html

movie_list.html模版的存放位置:
core/templates/core/movie_list.html
这样,模版引擎就能准确找到视图对应的模版。

测试:

  1. 将movie_list.html的目录变化一下,
    core/templates/core/movie_list.html
    变成:
    core/templates/movie_list.html
    刷新浏览器就会报错,在模版引擎中对应的正确位置上没发现模版:
TemplateDoesNotExist at /movies
core/movie_list.html
  1. 然后再将目录还原:
    core/templates/movie_list.html
    变成:
    core/templates/core/movie_list.html
08自定义模版的存放位置.png

所以, 切记一定要严格遵守模版引擎的约定:正确存放自定义模版的位置,且:模版文件名也要按照约定来命名,两个条件缺一不可。
依照惯例,模版的命名: core/movie_list.html
settings.py的配置文件中,有一个默认的template目录,将从这个目录查找模版。
如果不遵从这个惯例约定,可能就加载不出模版。
这个目录可以被各个app覆盖。
'DIRS': [os.path.join(BASE_DIR, 'templates')]
'APP_DIRS': True,

TEMPLATES = [
 {
 'BACKEND': 'django.template.backends.django.DjangoTemplates',
 'DIRS': [os.path.join(BASE_DIR, 'templates')]
 ,
 'APP_DIRS': True,
 'OPTIONS': {
 'context_processors': [
 'django.template.context_processors.debug',
 'django.template.context_processors.request',
 'django.contrib.auth.context_processors.auth',
 'django.contrib.messages.context_processors.messages',
 ],
 },
 },
]

开始编码template
core/templates/core/movie_list.html
没有相应目录,就地创建

<!DOCTYPE html>
<html>
<body>
 <ul>
 {% for movie in object_list %}
 <li>
 {{ movie }}
 </li>
 {% empty %}
 <li>
 没有电影列表。
 </li>
 {% endfor %}
 </ul>
 <p>
 用的 https?
 {{ request.is_secure|yesno }}
 </p>
</body>
</html>

Django的template是标准HTML,内置了变量variables和标签tags。

内置参数 参数说明
{{variables}} variables等待被赋值的变量, 将指定变量的值插入到这里
{% for item in item_list %} 模版标签 只要能让模版系统做些事儿的 就是标签
{{ ship_date[管道符]date: “F J, Y” }} 过滤器类似Unix的管道,把ship_date传递给date过滤器,并给date过滤器指定“F J, Y”参数

其他特性用到了再细分讨论,先忙正事儿。
Django内置template和filters

按照这个逻辑,模版中,{% extends '' %} {% block %}
{% for movie in object_list %}
{% url %} 是标签;
{{ movie }}是变量。

2.2.9 通过URLConf将请求定位到视图View

接下来就是将视图链接到URLConf上。

之前的篇幅中已经准备好了model,view和template,我们得利用URLConf告知Django哪个request应该被路由指向到Movielist视图上。

什么是URLConf?每个项目中都有个根URLConf,由Django创建的。

对于Django开发者来说,最好的办法是每个app应用都有自己的URLConf。那么接下来,根URLConf会通过include()函数 引入各个app目录下的URLConf。

在core下创建一个URLConf, 即创建文件:core/urls.py,

from django.urls import path
from core import views

app_name = 'core'

urlpatterns = [
 # 基于class的视图,必须调用View基类的静态函数as_view()
 # 返回一个可调用的字符串  传入path中
 path('movies',
 views.MovieList.as_view(),
 name='MovieList',),
 # 基于函数的视图,可以直接将函数传入url,可直接执行
 url(r'$', views.test),
]
05.视图属性不同注册方式不同.jpg

最简单的理解,一个URLConf就是一个拥有urlpatterns属性的module,是一个path集合

一个path由一个描述字符串的字符串组成,描述了有问题的和可调用的字符串。

FBVs Function-Based Views 基于函数的View
CBVs Class-Based Views 基于类的View

CBVs不可调用,所以View的基类用一个静态函数as_view()返回一个可调用的字符串;

FBVs可以作为回调直接传入,不需要()操作符,直接可以执行。

每个path()都应该起个名字,这样对于当我们需要在template中引用这个path时很有用。

既然一个URLConf可以被其他URLConf引用include(),所以我们可能不知道view的全路径。

Django提供了一个reverse()函数和url模版标签,可以通过一个name找到一个view的全路径。

app_name变量,设定了URLConf属于哪个app。这样,我们可以区分一个被命名的path,不至于多个app中有同名path的时候Django无法区分。类似于命名空间如,appA:index, appB:index, appC:index。

将core/urls.py链接到MyMovie/urls.py;
编辑MyMovie/urls.py

from django.contrib import admin
from django.urls import path, include
import core.urls

urlpatterns = [
 path('admin/', admin.site.urls),
 path('', include(core.urls, namespace='core')),
]

2.2.10 运行开发server 查看结果

cd MyMovie
python manage.py runserver

浏览器中输入:

http://127.0.0.1:8000/movies

其中,基于函数的视图直接返回了HttpResponse对象,可以在任何http测试工具中测试:

http://127.0.0.1:8000/test

这个外界测试Http接口,后续会单独介绍如何写api。

2.3 创建Movie相关页面

既然已经完成了项目的布局,那我们可以加快进度。我们已经记录了每个movie的信息。我们来创建一个视图,专门显示具体的movie信息。

我们需要做三件事:

2.3.1 创建MovieDetailView

类似Django提供的ListView,同时也提供了DetailView,来显示单个model的详细信息。

core/views.py

from django.http import HttpResponse
from django.shortcuts import render
from django.views.generic import (
 ListView, DetailView
)
from core.models import Movie

# 基于class的视图
class MovieList(ListView):
 model = Movie

# movie详情 视图
class MovieDetail(DetailView):
 model = Movie

DetailView需要一个path()对象,引入一个pk或者slug,因此DetailView可以向QuerySet传递参数来查询特定的model实例。

一个slug是一个简短的URL友好标签,通常用于内容繁多的网站。

2.3.2 创建movie_detail.html模版

已经有了视图view,那么紧接着创建一个对应的模版。

Django的template支持复用,标记block部分,其他模版可以重写该block部分。这样就可以抽象出一个基类模版,其他模版可以继承该基类模版进行拓展。

基类模版, MyMovie/templates/base.html

目录默认可能没有,手动创建

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>
 {% block title %} {% endblock %}
 </title>
 <style>
 .mymdb-masthead {
 background-color: #EEEEEE;
 margin-bottom: 1em;
 }
 </style>
</head>
<body>
 <div class="mymdb-masthead">
 <div class="container">
 <nav class="nav">
 <div class="navbar-brand">MyMDB</div>
 <a class="nav-link"
 href="{% url 'core:MovieList' %}">
 Movies
 </a>
 </nav>
 </div>
 </div>
 <div class="container">
 <div class="row">
 <div class="clo-sm-8 mymdb-main">
 {% block main %} {% endblock %}
 </div>
 <div class="col-sm-3 offset-sm-1 mymdb-sidebar">
 {% block sidebar %}
 {% endblock %}
 </div>
 </div>
 </div>
</body>
</html>

该base.html包含三块block:title/main/sidebar

{%block %}: 告诉模版引擎,这部分内容可以被子模版覆盖。

{% block title %}MyMovie{% endblock %}, 创建了一个title的block,其他templates可以替换该部分。如果其他模版没替换,即沿用默认基类的。

href="{% url 'core:MovieList' %}" url标签会生成一个URL的path,URL的名字应该这样命名:<app_namespace>:<name>, 比如:core是namespace,name是MovieList,那么url标签后面跟的就是core:MovieList。

创建一个简单的模版
core/templates/core/movie_detail.html

{% extends 'base.html' %}
{% block title %}
 {{ object.title }} - {{ block.super }}
{% endblock %}
{% block main %}
 <h1>{{ object }}</h1>
 <p class="lead">
 {{ object.plot }}
 </p>
{% endblock %}
{% block sidebar %}
 <div>
  这个电影排名:
 <span class="badge badge-primary">
 {{ object.get_rating_display }}
 </span>
 </div>
{% endblock %}

这个模版只有很少的HTML部分,因为大部分HTML在base.html中已经包含了。movie_detail.html需要做的就是给base.html中定义的blocks提供数值。

看下新的标签:

{% extends 'base.html' %} 用extends来实现继承另外一个模版。Django先找到base模版先执行,然后再替换blocks。

{% object.title %} - {% block.super %}

{% block.super %} 魔法变量,从base模版中的block中获取内容,提供base模版中渲染后的文本。

{{ object.get_rating_display }} Django模版不用()来执行函数,直接用函数名字就可以执行。

2.3.3 将MovieDetail视图配置到URLConf中

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

app_name = 'core'
urlpatterns = [
 # 基于class的视图,必须调用View基类的静态函数as_view()
 # 返回一个可调用的字符串  传入path中
 path('movies',
 views.MovieList.as_view(),
 name='MovieList',),
 # 向MovieDetail视图中传递pk参数 获取特定的movie对象
 path('movie/<int:pk>',
 views.MovieDetail.as_view()),
 # 基于函数的视图,可以直接将函数传入url,可直接执行
 url(r'$', views.test),
]

浏览器中输入:

http://127.0.0.1:8000/movie/2

待截图显示

2.4 处理Movie列表分页 点击跳转

2.4.1 给MovieList.html增加点击跳转到详情页功能

core/movie_list.html

{% extends 'base.html' %}
{% block title %}
 所有Movies
{% endblock %}
{% block main %}
 <ul>
 {% for movie in object_list %}
 <li>
 <a href="{% url 'core:MovieDetail' pk=movie.id %}">
 {{ movie }}
 </a>
 </li>
 {% endfor %}
 </ul>
{% endblock %}

url标签,用了一个叫pk(primary key)的参数,因为MovieDetail URL需要一个pk参数。如果没有参数,渲染过程中Django会抛一个NoReverseMatch的异常,导致500错误。

刷新浏览器

http://127.0.0.1:8000/movies

会发现movies列表显示的是超链接样式,点击可以进入到详情页。
截图:

2.4.2 设置排序

Model中新增一个内部类 Meta 指定ordering字段

from django.db import models
# 编写第一个model Movie
class Movie(models.Model):
 #省略之前的部分 
 # Model内部类 可以指定Model的信息。
 class Meta:
 ordering = ('-year', 'title')
 def __str__(self):
 return '{} ({})'.format(self.title, self.year)

ordering字段,指定排序参照的字段,year降序,title。对应sql语句:

order by year desc, title;

刷新浏览器,可以发现,movie列表是降序排列。

2.4.3 添加分页

分页有点儿问题 代码先放上, 先跳过这部分

既然movies已经排序了,直接给加上分页。Django的ListView视图额外已经内置了分页,所以直接用就好。

Pagination由GET参数控制page页的显示。

将分页功能加到block main的底部;

{% extends 'base.html' %}
{% block title %}
 所有Movies
{% endblock %}
{% block main %}
 <ul>
 {% for movie in object_list %}
 <li>
 <a href="{% url 'core:MovieDetail' pk=movie.id %}">
 {{ movie }}
 </a>
 </li>
 {% endfor %}
 </ul>
 {% comment 添加分页 先注释掉 %}{% if is_paginated %}
 <nav>
 <ul class="pagination">
 <li class="page-item">
 <a href="{% url 'core:MovieList' %}?page=1"
 class="page-link">First</a>
 </li>
 {% if page_obj.has_previous %}
 <li class="page-item">
 <a class="page-link"
 href="{% url 'core:MovieList' %}?page={{ page_obj.previous_page_num}}">
 {{ page_obj.previous_page_number }}
 </a>
 </li>
 {% endif %}
 <li class="page-item active">
 <a href="{% url 'core:MovieList' %}?page={{ page_obj.number }}">
 {{ page_obj.number }}
 </a>
 </li>
 {% if page_obj.has_next %}
 <li class="page-item">
 <a href="{% url 'core:MovieList' %}?page={{ page_obj.next_page_number }}"
 class="page-link">
 {{ page_obj.next_page_number }}
 </a>
 </li>
 {% endif %}
 <li>
 <a href="{% url 'core:MovieList' %}?page=last"
 class="page-link">
 Last
 </a>
 </li>
 </ul>
 </nav>
 {% endif %}{% endcomment %}
{% endblock %}

2.4.4 404错误

如果URL输入错误,就会出现404,资源不存在错误;可以自定义404提示页面,这个也无伤大雅,回头补充。

2.4.5 测试视图和模版

类似于java的junit测试,先不写了。

2.5 添加Person和model关系

建模部分,涉及到外键, 一对多, 多对多关系的梳理。
Django2 web实战01-启动项目-model处理

3 小结

综上,你会发现,用Django的套路是:

  • 配置项目属性,settings.py
  • 创建一个应用core
  • 在core/models.py中编写模型处理数据库数据
  • 再core/views.py中编写视图用来处理HTTP
  • 在core/urls.py中配置应用路径,使得视图view可到达
  • 再将core/urls.py链接到Django项目的urls.py中
  • 如果需要展示视图的http返回内容或者数据库返回内容,借助Django模版

Django用的是MVT模式,本质就是MVC。

至于MVT各个模块之间怎么衔接起来的,那就问Django了呵。开个玩笑,Django框架本身处理的非常完美,后续好好梳理下这个调用的流程。

宗旨:尽量不重复造轮子。

关于源码,还在梳理整合,需要留言,回头发布到github上。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容

  • 对于java中的思考的方向,1必须要看前端的页面,对于前端的页面基本的逻辑,如果能理解最好,不理解也要知道几点。 ...
    神尤鲁道夫阅读 797评论 0 0
  • MTV模型 Django的MTV分别代表: Model(模型):负责业务对象与数据库的对象(ORM) Templa...
    lkning阅读 794评论 0 0
  • Vue面试题整理一:vue的优点是什么?1:低耦合。视图(View)可以独立于Model变化和修改,一个ViewM...
    ClarkM阅读 221评论 0 1
  • 切换到创建项目的目录 cd C:\Users\admin\Desktop\DjangoProject创建名为pr...
    在努力中阅读 3,256评论 2 3
  • 点我查看本文集的说明及目录。 本项目相关内容( github传送 )包括: 实现过程: CH1 创建一个博客应用 ...
    学以致用123阅读 4,387评论 0 31