django养生

模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合。

Django 安装

Windows下安装 Django

如果你还未安装Python环境需要先下载Python安装包。

1、Python 下载地址:https://www.python.org/downloads/

2、Django 下载地址:https://www.djangoproject.com/download/

注意:目前Django 1.6.x以上版本已经完全兼容Python 3.x。

Django 安装

下载 Django 压缩包,解压并和Python安装目录放在同一个根目录,进入 Django 目录,执行python setup.py install,然后开始安装,Django将要被安装到Python的Lib下site-packages。

[图片上传失败...(image-697485-1530838002396)]

然后是配置环境变量,将这几个目录添加到系统环境变量中: C:\Python33\Lib\site-packages\django;C:\Python33\Scripts。 添加完成后就可以使用Django的django-admin.py命令新建工程了

[图片上传失败...(image-a01c57-1530838002396)]

Linux 上安装 Django

yum 安装方法

以下安装位于 Centos Linux 环境下安装,如果是你的 Linux 系统是 ubuntu 请使用 apt-get 命令。

默认情况下 Linux 环境已经支持了Python。你可以在终端输入Python命令来查看是否已经安装。

Python 2.7.3 (default, Aug  1 2012, 05:14:39) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 

安装 setuptools

命令:

yum install python-setuptools

完成之后,就可以使用 easy_install 命令安装 django

easy_install django

之后我们在python解释器输入以下代码:

[root@solar django]# python
Python 2.7.3 (default, May 15 2014, 14:49:08)
[GCC 4.8.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import django
>>> django.VERSION
(1, 6, 5, 'final', 0)
>>>

我们可以看到输出了Django的版本号,说明安装成功。

Windows下安装django

pip 命令安装方法

pip install Django

如果 pip < 1.4,安装方法如下:

pip install https://www.djangoproject.com/download/1.11a1/tarball/

Django与Python版本对应的关系

[图片上传失败...(image-c8fe70-1530838002396)]

安装Django 1.11.4

pip install Django==1.11.4

检查是否安装成功

输入以下命令进行检查:

>>> import django
>>> django.get_version()  #获取当前的版本号

[图片上传失败...(image-9beef2-1530838002396)]

如果输出了Django的版本号说明安装正确。


创建Django项目

创建第一个项目

使用 django-admin 来创建 HelloWorld 项目:

在合适位置创建一个目录

打开黑屏终端进入到上一步创建的目录下

django-admin startproject HelloWorld(项目名)

创建完成后我们可以查看下项目的目录结构:

$ cd HelloWorld/
$ tree
|-- HelloWorld
|   |-- __init__.py
|   |-- settings.py
|   |-- urls.
|   `-- wsgi.py
`-- manage.py

目录说明:

  • HelloWorld: 项目的容器。
  • manage.py: 一个实用的命令行工具,可让你以各种方式与该 Django 项目进行交互。
  • HelloWorld/init.py: 一个空文件,告诉 Python 该目录是一个 Python 包。
  • HelloWorld/settings.py: 该 Django 项目的设置/配置。
  • HelloWorld/urls.py: 该 Django 项目的 URL 声明; 一份由 Django 驱动的网站"目录"。
  • HelloWorld/wsgi.py: 一个 WSGI 兼容的 Web 服务器的入口,以便运行你的项目。

接下来我们进入 HelloWorld 目录输入以下命令,启动服务器:

python manage.py runserver 0.0.0.0:8000

启动django后,不能访问,报400错误。

原因:没有开启允许访问

处理:编辑HelloWorld目录下setting.py ,把其中的

ALLOWED_HOSTS=[]改成ALLOWED_HOSTS=['*'] ##* 表示任意地址。

Django 模型

Django 对各种数据库提供了很好的支持,包括:PostgreSQL、MySQL、SQLite、Oracle。

Django 为这些数据库提供了统一的调用API。 我们可以根据自己业务需求选择不同的数据库。

MySQL 是 Web 应用中最常用的数据库。

数据库配置

修改项目的__init__ 的文件 添加

import pymysql
pymysql.install_as_MySQLdb()

我们在项目的 settings.py 文件中找到 DATABASES 配置项,将其信息修改为:

HelloWorld/HelloWorld/settings.py: 文件代码:82行
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',  # 或者使用 mysql.connector.django
        'NAME': '数据库名称',
        'USER': '用户名',
        'PASSWORD': '密码',
        'HOST':'主机名',
        'PORT':'端口号',
    }
}

这里添加了中文注释,所以你需要在 HelloWorld/settings.py 文件头部添加 # -*- coding: UTF-8 --*。

上面包含数据库名称和用户的信息,它们与 MySQL 中对应数据库和用户的设置相同。Django 根据这一设置,与 MySQL 中相应的数据库和用户连接起来。


定义模型

创建 APP

Django规定,如果要使用模型,必须要创建一个app。我们使用以下命令创建一个 TestModel 的 app:

django-admin startapp TestModel

目录结构如下:

HelloWorld
|-- TestModel
|   |-- __init__.py 
|   |-- admin.py  配置后台
|   |-- models.py 模型
|   |-- tests.py  测试
|   `-- views.py  视图

设计表

班级表结构 grades

班级名称   gname                 
成立时间   gdate                 
女生总数  ggirlnum               
男生总数  gboynum                
是否删除  isDelelte              
                               
                               
学生表结构  students

学生姓名  sname 
学生性别  ssex
学生年龄  sage
学生简介  scontend
是否删除 isDelete                          

我们修改 TestModel/models.py 文件,代码如下:

HelloWorld/TestModel/models.py: 文件代码:
# models.py
from django.db import models

class Grades(models.Model):
    gname = models.CharField(max_length=20)
    gdate = models.DateTimeField()
    ggirlnum = models.IntegerField()
    gboynum = models.IntegerField()
    isdelete = models.BooleanField(default=False)

class Students(models.Model):
    sname = models.CharField(max_length=20)
    ssex = models.BooleanField(default=True)
    sage = models.IntegerField()
    scontented = models.CharField(max_length=20)
    isdelete = models.BooleanField(default=False)
    sgrade = models.ForeignKey("Grades")

以上的类名代表了数据库表名,且继承了models.Model,类里面的字段代表数据表中的字段(name),数据类型则由CharField(相当于varchar)DateField(相当于datetime), max_length 参数限定长度。

在settings.py文件中,将TestModel应用加入到INSTALLED_APPS选项中

接下来在settings.py中找到INSTALLED_APPS这一项,如下:

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'TestModel',               # 添加此项 你的项目名称
)
在命令行中运行:
$ python manage.py makemigrations TestModel  # 让 Django 知道我们在我们的模型有一些变更 #在migrations目录下生成一个迁移文件,此时数据库中还没有生成数据表
$ python manage.py migrate   #m 创建表结构
#$ python manage.py migrate TestModel   # 创建表结构
看到几行 "Creating table…" 的字样,你的数据表就创建好了。
Creating tables ...
……
Creating table TestModel_test  #我们自定义的表
……

表名组成结构为:应用名_类名(如:TestModel_test)。

注意:尽管我们没有在models给表设置主键,但是Django会自动添加一个id作为主键。

测试数据库

(一) 进入到python shell

执行 python manage.py shell

(二) 引入包

from  myApp.models  import  Grades,Students #导入models的类
from  django.utils  import  timezone #导入时区(如果存在写入时间的字段)
#from  datetime import  * #导入时间模块

(三) 查询所有数据

类名.objects.all()

如 :Grades.objects.all()

(四) 添加数据

对象 = 类名()

如: g = Grades()

g.字段名 = 值

保存

g.save()

注意:如果给gdate添加时间应该设置为 timezone.now() 否则会报错

解决办法:

g.gdate = timezone.now()

[图片上传失败...(image-493dab-1530838002396)]

(五) 查看某个对象

类名.objects.get(pk=2) #pk 相当于自增id

如果返回的是对象 那么在models里面的类 添加

def __str__(self):
   return self.sname

(六) 修改数据

模型对象.属性 = 新值

如 : g = Grades.objects.get(pk=2)

g.字段名= 值

g.save()

(七) 删除数据

模型对象.delete()

如 : g = Grades.objects.get(pk=2)

g.delete()

注意:物理删除,数据库中的表里的数据被删除了

(八) 给关联的表添加数据

>>> stu = Students()
>>> stu.sname = "薛艳梅"
>>> stu.sgender = False
>>> stu.sage = 20
>>> stu.scontend = "我叫薛艳梅"
>>> stu.sgrade = grade1 #为表 grade1对象  grade1 = Grades.objects.get(pk=1)或者 直接等于grade的自增id
>>> stu.save()

(九) 关联对象

获得关联对象的集合

需求:获取python04班级的所有学生

如:
g = Grades.objects.get(pk=1)
g.students_set.all() #获取所有g表关联Students表里面所有的数据  类名首字母小写
格式:对象.类名_set.all()

Admin站点管理

一 配置Admin应用

在settinngs.py文件中的INSTALLED_APPS中添加'django.contrib.admin' 默认是已经添加好的

二 创建管理员用户

执行 python manage.py createsuperuser

依次输入用户名、邮箱、密码

三 配置中国的时区

修改settings.py文件

LANGUAGE_CODE = 'zh-Hans'

TIME_ZONE = 'Asia/Shanghai'

四 管理数据表

(1) 修改admin.py文件 配置后台显示页面

from .models import Grades,Students
# 注册
admin.site.register(Grades)
admin.site.register(Students)

(2) 配置后台成绩的页面显示

from .models import Grades,Students
# 注册
class GradesAdmin(admin.ModelAdmin):
    #列表页属性
    #显示字段
    list_display = ['pk', 'gname', 'gdate', 'ggirlnum', 'gboynum', 'isdelete']
    #过滤字段
    list_filter = ['gname']
    #搜索字段
    search_fields = ['gname']
    #分页
    list_per_page = 5
    # 添加、修改数据的时候  属性的先后顺序
    # fields = ['ggirlnum','gboynum','gname','gdate','isdelete']
    #添加、修改数据的时候 给属性分组
    fieldsets = [
        ("num",{"fields":['ggirlnum','gboynum']}),
        ("base",{"fields":['gname','gdate','isdelete']}),
    ]
admin.site.register(Grades, GradesAdmin)
注意:fields与fieldsets不能同时使用

(3) 关联对象

需求:在创建一个班级时可以直接添加几个学生
#TabularInline 横着显示添加学生布局 
#StackedInline  #竖着显示添加学生的布局
class StudentsInfo(admin.TabularInline):    
    model = Students
    extra = 2 #2带表添加的学生的个数
class GradesAdmin(admin.ModelAdmin):
    inlines = [StudentsInfo]

(4) 布尔值显示问题(显示成男女)

class StudentsAdmin(admin.ModelAdmin):
    def gender(self):
        if self.ssex:
            return "男"
        else:
            return "女"
    #设置页面列的名称
    gender.short_description = "性别"

    list_display = ['pk','sname','sage',gender,'scontented','sgrade','isdelete']
    list_per_page = 10
    # 执行动作的位置 搜索框的上下位置
    actions_on_top = False
    actions_on_bottom = True
admin.site.register(Students,StudentsAdmin)

(5) 使用装饰器完成注册

@admin.register(tudents)
class StudentAdmin(admin.ModelAdmin):
    pass
@admin.register(Students)
class StudentsAdmin(admin.ModelAdmin):
    pass
#admin.site.register(Students,StudentsAdmin)
# admin.site.register(Grades, GradesAdmin)

五 配置控制器

概述:在django中,视图对web请求进行回应

视图就是一个python函数,在views.py文件中定义

(1) 定义控制器

在myApp下的views.py

HelloWorld
|-- myApp
|   |-- __init__.py
|   |-- admin.py
|   |-- apps.py
|   |-- models.py
|   |-- views.py
|   |-- ....py
|-- projct
|   |-- __init__.py
|   |-- settings.py
|   |-- urls.py
|   |-- urls.pyc
|   |-- view.py
|   |-- view.pyc
|   |-- wsgi.py
|   `-- wsgi.pyc
|-- manage.py
`-- templates
    `-- hello.html

(2) 配置 urls.py 路由

修改project目录下的urls.py文件

from django.conf.urls import url,include
from django.contrib import admin #导入后台登录模块
from myApp import views

urlpatterns = [
    url(r'^admin/', admin.site.urls), #访问后台的路由
    #url(r'^', include('myApp.urls')), #访问在myApp中路由的文件(自定义的)
    url(r'^hello$', views.hello),
    url(r'^setdb$', views.setdb),
    url(r'^getDate$', views.getDate),
]
在myApp下单独创建一个urls.py文件
from django.conf.urls import url
from myApp import views
urlpatterns = [
    url(r'^$',views.index,name='index'),
]

(3) HttpResponse 不通过模板 而是直接通过控制器进行返回 (一般作为调试使用)

from django.http import HttpResponse
from django.http import HttpResponse
def index(request):
    return HttpResponse("我是首页")  #不通过模板 而是直接通过控制器进行返回

(4) 将数据返回给视图进行渲染

数据是字典形式

from django.shortcuts import render #导入发送到模板的方法
from datetime import datetime #导入时间模块
from django.http import HttpResponse 
from myApp.models import Grades #导入班级的类
def hello(request):
    context = {}
    context['hello'] = 'Hello World!'
    context['bool'] = True
    context['myFor'] = 'abcdefg'
    return render(request, 'hello.html', context)

# 数据库操作
def setdb(request):
    g = Grades()
    g.gname = "python1701"
    g.gdate = datetime.now()
    g.ggirlnum = 10
    g.gboylnum = 40
    g.save()
    return HttpResponse("<p>数据添加成功!</p>") #直接进行返回

def getDate(request):
    dataList = {}
    dataList['con'] = Grades.objects.all()
    return render(request,'show.html',dataList)

六 配置 templates 模板

概述:模板是HTML页面,可以根据视图中传递过来的数据进行填充

(1)配置模板路径

修改settings.py文件下的TEMPLATES 59行

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        # 'DIRS': [],
        'DIRS': [BASE_DIR+"/templates",]或[os.path.join(BASE_DIR,'templates')],      #配制模板路径

(2) 模板语法

<h1>{{ hello }}</h1> 双{} 代表存储的是变量

Django 模板标签

if/else 标签

基本语法格式如下:

{% if condition %}
     ... display
{% endif %}

或者:

{% if condition1 %}
   ... display 1
{% elif condition2 %}
   ... display 2
{% else %}
   ... display 3
{% endif %}

根据条件判断是否输出。if/else 支持嵌套。

{% if %} 标签接受 and , or 或者 not 关键字来对多个变量做判断 ,或者对变量取反( not ),例如:

{% if athlete_list and coach_list %}
     athletes 和 coaches 变量都是可用的。
{% endif %}

for 标签

{% for %} 允许我们在一个序列上迭代。

与Python的 for 语句的情形类似,循环语法是 for X in Y ,Y是要迭代的序列而X是在每一个特定的循环中使用的变量名称。

每一次循环中,模板系统会渲染在 {% for %} 和 {% endfor %} 之间的所有内容。

例如,给定一个运动员列表 athlete_list 变量,我们可以使用下面的代码来显示这个列表:

<ul>
{% for athlete in athlete_list %}
    <li>{{ athlete.name }}</li>
{% endfor %}
</ul>

给标签增加一个 reversed 使得该列表被反向迭代:

{% for athlete in athlete_list reversed %}
...
{% endfor %}

可以嵌套使用 {% for %} 标签:

{% for athlete in athlete_list %}
    <h1>{{ athlete.name }}</h1>
    <ul>
    {% for sport in athlete.sports_played %}
        <li>{{ sport }}</li>
    {% endfor %}
    </ul>
{% endfor %}

ifequal/ifnotequal 标签

{% ifequal %} 标签比较两个值,当他们相等时,显示在 {% ifequal %} 和 {% endifequal %} 之中所有的值。

下面的例子比较两个模板变量 user 和 currentuser :

{% ifequal user currentuser %}
    <h1>Welcome!</h1>
{% endifequal %}

和 {% if %} 类似, {% ifequal %} 支持可选的 {% else%} 标签:

{% ifequal section 'sitenews' %}
    <h1>Site News</h1>
{% else %}
    <h1>No News Here</h1>
{% endifequal %}

注释标签

Django 注释使用 {# #}。

{# 这是一个注释 #}

过滤器

模板过滤器可以在变量被显示前修改它,过滤器使用管道字符,如下所示:

{{ name|lower }}

{{ name }} 变量被过滤器 lower 处理后,文档大写转换文本为小写。

过滤管道可以被* 套接* ,既是说,一个过滤器管道的输出又可以作为下一个管道的输入:

{{ my_list|first|upper }}

以上实例将第一个元素并将其转化为大写。

有些过滤器有参数。 过滤器的参数跟随冒号之后并且总是以双引号包含。 例如:

{{ bio|truncatewords:"30" }}

这个将显示变量 bio 的前30个词。

其他过滤器:

  • addslashes : 添加反斜杠到任何反斜杠、单引号或者双引号前面。

  • date : 按指定的格式字符串参数格式化 date 或者 datetime 对象,实例:

    {{ pub_date|date:"F j, Y" }}
    
  • length : 返回变量的长度。

include 标签

{% include %} 标签允许在模板中包含其它的模板的内容。

下面这个例子都包含了 nav.html 模板:

{% include "nav.html" %}

模板继承

模板可以用继承的方式来实现复用。

接下来我们先创建之前项目的 templates 目录中添加 base.html 文件,代码如下:

HelloWorld/templates/base.html 文件代码:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
    <h1>Hello World!</h1>
    <p>菜鸟教程 Django 测试。</p>
    {% block mainbody %}
       <p>original</p>
    {% endblock %}
</body>
</html>

以上代码中,名为 mainbody 的 block 标签是可以被继承者们替换掉的部分。

所有的 {% block %} 标签告诉模板引擎,子模板可以重载这些部分。

hello.html 中继承 base.html,并替换特定 block,hello.html 修改后的代码如下:

{% extends "base.html" %}
{% block mainbody %}<p>继承了 base.html 文件</p>
{% endblock %}

模型 model

Django对各种数据库提供了很好的支持,Django为这些数据库提供了统一的调用API,可以根据不同的业务需求选择不同的数据

一 配置数据库

修改工程目录下的_init_.py文件

import pymysql
pymysql.install_as_MySQLdb()

修改settings.py文件

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': "kaishen",
        'USER':'root',
        'PASSWORD':'sunck',
        'HOST':'localhost',
        'PORT':'3306',
    }
}

Zh

二 开发流程

  1. 配置数据库

  2. 定义模型类

    一个模型类都在数据库中对应一张数据表

  3. 生成迁移文件

  4. 执行迁移生成数据表

    python manage.py makemigrations TestModel
    python manage.py migrate   # 创建表结构
    
  5. 使用模型类进行增删改查(crud)操作

三 ORM

概述

对象-关系-映射

任务

  1. 根据对象的类型生成表结构
  2. 将对象、列表的操作转换为sql语句
  3. 将sql语句查询到的结果转换为对象、列表

优点

极大的减轻了开发人员的工作量,不需要面对因数据库的变更而修改代码

图解

[图片上传失败...(image-31c2b-1530838045053)]

四 定义模型

(1) 模型、属性、表、字段间的关系

一个模型类在数据库中对应一张表,在模型类中定义的属性,对应该模型对照表中的一个字段

(2) 定义属性

详情请见定义属性.md

(3) 创建模型类

(4) 元选项

在模型类中定义Meta类,用于设置元信息

  1. db_table

    定义数据表名,推荐使用小写字母,数据表名默认为项目名小写_类名小写

  2. ordering

    对象的默认排序字段,获取对象的列表时使用

    • ordering['id'] 升序
    • ordering['-id'] 降序

    注意:排序会增加数据库的开销

实例

class Students(models.Model):
    sname    = models.CharField(max_length=20)
    sgender  = models.BooleanField(default=True)
    sage     = models.IntegerField(db_column="age")
    scontend = models.CharField(max_length=20)
    isDelete = models.BooleanField(default=False)
    # 关联外键
    sgrade = models.ForeignKey("Grades")
    def __str__(self):
        return self.snamea
    lastTime = models.DateTimeField(auto_now=True)
    createTime = models.DateTimeField(auto_now_add=True)
    class Meta:
        db_table="students" #手动起表名
        ordering=['id']  #按照id升序

五 模型成员

类属性

(1) objects

是Manager类型的一个对象,作用是与数据库进行交互

当定义模型类是没有指定管理器,则Django为模型创建一个名为objects的管理器

(2) 自定义管理器

class Students(models.Model):
    # 自定义模型管理器
    # 当自定义模型管理器,objects就不存在了
    stuObj = models.Manager()

当为模型指定模型管理器,Django就不在为模型类生成objects模型管理器

(3) 自定义管理器Manager类

模型管理器是Django的模型进行与数据库进行交互的接口,一个模型可以有多个模型管理器

作用

向管理器类中添加额外的方法

修改管理器返回的原始查询集

重写get_queryset()方法

代码示例

class StudentsManager(models.Manager):
    def get_queryset(self):
        return super(StudentsManager,self).get_queryset().filter(isDelete=False)#将isDelete为 False的数据进行返回

class Students(models.Model):
    # 自定义模型管理器
    # 当自定义模型管理器,objects就不存在了
    stuObj = models.Manager()
    stuObj2 = StudentsManager()

创建对象

(1) 目的

向数据库中添加数据

当创建对象时,django不会对数据库进行读写操作,当调用save()方式时才与数据库交互,将对象保存到数据库表中

注意:__init__方法已经在父类models.Model中使用,在自定义的模型中无法使用

(2) 方法

1. 在模型类中增加一个类方法

实例

class Students(models.Model):
    #定义一个类方法创建对象
    @classmethod
    def addStudents(cls, name, age, gender, contend, grade, isD=False):
        stu = cls(sname = name, sage = age, sgender = gender, scontend = contend, sgrade = grade, isDelete=isD)
        return stu
在视图中使用
def addStudents(request):
    g = Grades.objects.get(pk=1)
    # return HttpResponse(g)
    stu = Students.addStudents('李四',10,'0','我是类方法添加的',g)
    stu.save()
    return HttpResponse('添加完成')
2. 在定义管理器中添加一个方法

实例

class StudentsManager(models.Manager):
    def get_queryset(self):
        return super(StudentsManager,self).get_queryset().filter(isDelete=False)
    def addStudents(self, name, age, ssex, contend, grade, lastT, createT, isD=False):
        stu = self.model()
        # print(type(grade))
        stu.sname = name
        stu.sage = age
        stu.ssex = ssex
        stu.scontend = contend
        stu.sgrade = grade
        return stu
class Students(models.Model):
    stuObj2 = StudentsManage() #添加了自己过滤方法的类
在视图中使用
def addStudents2(request):
    g = Grades.objects.get(pk=1)
    stu = Students.stuObj2.addStudents('w1W',30,'0','我是类方法添加的',g)
    stu.save()
    return HttpResponse('***')

六 模型查询

概述

  1. 查询集表示从数据库获取的对象集合
  2. 查询集可以有多个过滤器
  3. 过滤器就是一个函数,基于所给的参数限制查询集结果
  4. 从sql角度来说,查询集合select语句等价,过滤器就像where条件

(1) 查询集

在管理器上调用过滤器方法返回查询集

查询集经过过滤器筛选后返回新的查询集,所以可以写成链式调用

 Entry.objects.filter ( 
...     headline__startswith='What' 
... ).exclude( 
...     pub_date__gte=datetime.now() 
... ).filter ( 
...     pub_date__gte=datetime(2005, 1, 1) 
... ) 
最后返回的QuerySet是headline like 'What%' and put_date<now() and pub_date>2005-01-01 

惰性执行:创建查询集不会带来任何数据的访问,直到调用数据时,才会访问数据库

QuerySet是延迟加载 
只在使用的时候才会去访问数据库,如下: 
>>> q = Entry.objects.filter (headline__startswith="What") 
>>> q = q.filter (pub_date__lte=datetime.now()) 
>>> q = q.exclude(body_text__icontains="food") 
>>> print q 
在print q时才会访问数据库。

直接访问数据的情况

  1. 迭代
  2. 序列化
  3. 与if合用

(2) 返回查询集的方法称为过滤器

1. all() 返回查询集中的所有数据

Students.stuObj2.all()  #获取Students类里面所有数据
Students.stuObj2.all()[0:5] #获取Students类里面前5条数据数据
Students.stuObj2.all()[(int(page)-1)*5:int(page)*5] 分页

url(r'^stu/(\d+)/$', views.stupage),
def stupage(request,page):
    dataList = {}
    dataList['stu'] = Students.stuObj2.all()[(int(page)-1)*5:int(page)*5]

2. filter() 返回符合条件的数据

得到一个集合对象
返回一个与参数匹配的QuerySet,相当于等于(=).

一个条件的情况

  • filter(键=值)
多个条件 并且的关系
  • filter(键=值, 键=值)

    Students.stuObj.filter(sname__contains='孙',ssex=True)
    
  • filter(键=值).filter(键=值)

实例

表.objects.filter(id=2) ---[obj1,]  ,得到一个集合对象,集合里只有一个,跟上first()或者【0】取到一个具体对象

3. exclude() 过滤掉符合条件的数据

返回一个与参数不匹配的QuerySet,相当于不等于(!=)

Students.stuObj.exclude(sname__contains='孙')  #过滤掉sname字段值包含孙的数据

4. order_by(字段名) 排序

实例

按照id升序

Students.stuObj2.order_by('pk')

按照id降序

Students.stuObj2.order_by('-pk')

扩展

如果需要以多个字段为标准进行排序(第二个字段会在第一个字段的值相同的情况下被使用到),使用多个参数就可以了,如下:

Publisher.objects.order_by("state_province", "address")
 [<Publisher: Apress>, <Publisher: O'Reilly>]

我们还可以指定逆向排序,在前面加一个减号 - 前缀:

Publisher.objects.order_by("-name")
[<Publisher: O'Reilly>, <Publisher: Apress>]

尽管很灵活,但是每次都要用 order_by() 显得有点啰嗦。 大多数时间你通常只会对某些 字段进行排序。 在这种情况下,Django让你可以指定模型的缺省排序方式:

class Publisher(models.Model):
  name = models.CharField(max_length=30)
  address = models.CharField(max_length=50)
  city = models.CharField(max_length=60)
  state_province = models.CharField(max_length=30)
  country = models.CharField(max_length=50)
  website = models.URLField()
 
  def __unicode__(self):
    return self.name
 
  class Meta:
    ordering = ['name']  #在查询数据的时候 就按照name升序

现在,让我们来接触一个新的概念。 class Meta,内嵌于 Publisher 这个类的定义中(如果 class Publisher 是顶格的,那么 class Meta 在它之下要缩进4个空格--按 Python 的传统 )。你可以在任意一个 模型 类中使用 Meta 类,来设置一些与特定模型相关的选项。 在 附录B 中有 Meta 中所有可选项的完整参考,现在,我们关注 ordering 这个选项就够了。 如果你设置了这个选项,那么除非你检索时特意额外地使用了 order_by(),否则,当你使用 Django 的数据库 API 去检索时,Publisher对象的相关返回值默认地都会按 name 字段排序。

5. reverse() 对查询结果反向排序

注意: 需要先排序order_by(*field),才能反转

Students.stuObj.all().order_by('pk').reverse()

6. distinct() 从返回结果中剔除重复纪录

不支持

Students.stuObj.all().order_by('pk').distinct('sage')
#DISTINCT ON fields is not supported by this database backend 数据库不支持

7. values() 一条数据就是一个对象(字典),返回一个列表

实例

Students.stuObj.values() #得到QuerySet集合是所有的记录,,里面是字典,key是字段,value是值,

参数:为要返回的字段名和值 (默认返回全部)

类名.objects.values('title','price')#可以加多个字段

连贯操作

Book.objects.filter(id__gt=4).values('title')#查询id大于4,的title,得到一个对象集合,显示书名
    #<QuerySet [{'title': '富爸爸'}]>

8. value_list ( )  得到一个元组格式的数据,只有字段的值,

# ----valuelist 方法查询  ,得到一个元组格式数据,只有字段的值,
    b4 = Book.objects.filter(id__gt=3).values_list('title')
    print(b4)   
    #<QuerySet [('追风筝的人',), ('富爸爸',)]>

(3) 返回单个数据

  1. get() 返回一个满足条件的对象

    注意:

    • 如果没有找到符合条件的对象,会引发"模型类.DoesNotExist"异常
    • 如果找到多个对象,会引发"模型类.MultipleObjectsReturned"异常
    Students.stuObj.get(pk=1) #查询id为1的数据
    
    >>> Entry.objects.order_by('headline')[0] 
    #这是取按headline字段排序后的第一个对象。 
    >>> Entry.objects.order_by('headline')[0:1].get () 
    #这和上面的等同的。 
    
    表.objects.get(id=2)---obj,得到一个单独的对象,确定能找到,可以用,如果找到多个或者没有的,都报错。
  2. count() 返回当前查询集中的对象个数
    Students.stuObj.filter(ssex=True).count() #性别为1的人数
    
  3. first() 返回查询集中的第一个对象
    Students.stuObj.filter(ssex=True).first()  #返回 性别为1 的第一条数据
    
    QuerySet.first(),与get()方法一样,都得到一个对象
    
  4. last() 返回查询集中的最后一个对象
    Students.stuObj.filter(ssex=True).last()  #返回 性别为1 的最后一条数据
    
    QuerySet.last(),与get()方法一样,都得到一个对象
    
  5. exists() 如果QuerySet包含数据,就返回True,否则返回False ---只判断是否有记录
    Students.stuObj.exists()
    Students.stuObj.filter(isDelete=False).exists()
    

(4) 限制查询集

查询集返回列表,可以使用下标的方法进行限制,等同于sql中的limit语句

studentsList = Students.stuObj2.all()[0:5]

>>> Entry.objects.order_by('headline')[0] 
#这是取按headline字段排序后的第一个对象。 
>>> Entry.objects.order_by('headline')[0:1].get () 
#这和上面的等同的。

注意:下标不能是负数

(5) 查询集的缓存

每个查询集都包含一个缓存,来最小化的对数据库访问

在新建的查询集中,缓存首次为空,第一次对查询集求值,会发生数据缓存,django会将查询出来的数据做一个缓存,并返回查询结构,以后的查询直接使用查询集的缓存

以下语句要查询两次数据库

print([c.code for c in Catalog.objects.all()])

print([c.name for c in Catalog.objects.all()])

以下语句可以用上django的缓存机制,只用访问一次数据

cl = Catalog.objects.all();
print([c.code for c in cl])
print([c.name for c in cl])

最好是把所有数据,先遍历一边,在进行访问:

cl = Catalog.objects.all();

[c.code for c in cl]

cl[1];  # 从缓存读数据

cl[1];  # 从缓存读数据

以下四种方法将对象集放进缓存

[c for c in queryset]

bool(queryset)

c in queryset

list(queryset)

# 在对象查询集中使用数据索引要格外小心,因为每次索引都会访问1次数据库,即使是访问同一个元素。注意如下语句:

cl = Catalog.objects.all();

cl[1]; # 访问1次数据库

cl[1]; # 在访问1次数据库

(6) 字段查询

概述

实现了sql中的where语句,作为方法filter()、exclude()、get()的参数

语法

属性名称__比较运算符=值

外键

属性名_id

Students.stuObj2.filter(sgrade_id=1)

(7) 字段值修改

update 和save方法区别

Book.objects.filter(id=5).update(price=1000) #直接更新update 是QuerySet集合对象的方法,推荐

save方法

book = Book.objects.get(id=5)

book.price=400

book.save()


比较运算符

  1. 完全匹配运算符
    __exact 判断,大小写敏感
    __iexact 精确等于 忽略大小写 ilike 'aaa'

    精确等于 like 'aaa'

    filter(sname_exact=False)

    Students.stuObj.filter(sname = 'aw1w') #不区分大小写
    Students.stuObj.filter(sname__exact = 'aw1w') #不区分大小写
    #等同于SELECT ... WHERE headline = 'aw1w'; 
    Students.stuObj.filter(sname__iexact = 'aw1w') #不区分大小写
    
    Blog.objects.get (id__exact=14)  # Explicit form 
    >>> Blog.objects.get (id=14)         # __exact is implied 
    这两种方式是等同的,都是查找id=14的对象。 
    

    注意,如果在exact运算符右侧指定None,那么在翻译成SQL语句时就会按照SQL中的NULL进行比较。

    Students.stuObj.filter(sname__exact=None)
    等价于SQL语法:
    select * from students where sname is null
    

  2. __contains 是否包含,大小敏感
    包含 like '%aaa%'

    __icontains 包含 忽略大小写 ilike '%aaa%',但是对于sqlite来说,contains的作用效果等同于icontains

    studentsList = Students.stuObj2.filter(sname__contains="孙") #查询sname属性中包含孙的所有数据
    Students.stuObj.filter(sname__icontains='w') #不区分大小写
    等同于SELECT ... WHERE sname LIKE '%w%'; 
    
  3. __startswith、__endswith 以value开头或结尾,大小写敏感

    __istartswith 以...开头 忽略大小写

    __iendswith 以...结尾,忽略大小写

    studentsList = Students.stuObj2.filter(sname__startswith="孙") #sname字段以孙子开头的所有数据
    studentsList = Students.stuObj2.filter(sname__endswith="孙") #sname字段以孙子结尾的所有数据
    
    #不区分大小写
    Students.stuObj.filter(sname__istartswith='a')
    Students.stuObj.filter(sname__iendswith='a')
    
    startswith 等同于sql语句中的 name like 'Lennon%', 
    endswith等同于sql语句中的 name like '%Lennon'. 
    
以上四个在前面加上i,就表示不区分大小写iexact、icontains、istartswith、iendswith
  1. __isnull 是否为空
    __isnull=True/False
    filter(sname__isnull=False)
    
  2. __in 是否包含在范围内
    studentsList = Students.stuObj2.filter(pk__in=[2,4,6,8,10])
    #取反  not-in
    studentsList = Students.stuObj2.exclude(pk__in=[2,4,6,8,10])
    
  3. __range() 范围bettwen and
    __range(start_date, end_date)
    models.Tb1.objects.filter(id__range=[1, 2])  
    
  4. __gt、__gte、__lt、__lte 属性名__gt/gte/lt/lte
    Students.stuObj2.filter(sage__gt=30)  #年龄大于30的数据
    
  5. __year、__month、__day、__week_day、__hour、__minute、__second
    Students.stuObj2.filter(lastTime__year=2017)  #查询lastname字段的年份为2017的所有数据
    
    Entry.objects.filter (pub_date__year=2006) 
    不使用Entry.objects.all().filter (pub_date__year=2006),虽然也能运行,all()最好再获取所有的对象时使用。 
    slect * from entry where pub_date_year='2006' 
    简单日期操作
    from datetime import datetime;
    from datetime import date; 
    print(Catalog.objects.filter(create_date__year=2013));    # 时间的年份为2013年
    print(Catalog.objects.filter(create_date__gte=date.today()));    # 时间大于今天
    print(Catalog.objects.filter(create_date__gte=datetime(2013, 5, 23))); # 时间大于2013年5月23日
    print(Catalog.objects.filter(create_date__lte='2013-05-23'))  # 时间小于2013年5月23日
    

聚合函数

使用aggregate()函数返回聚合函数的值

  1. Avg 平均数
  2. Count 统计
  3. Max 最大
  4. Min 最小
  5. Sum 求和

实例

from django.db.models import Max
from django.db.models import aggregates
maxAge = Students.stuObj2.aggregate(Max('sage'))

F对象

可以使用模型的A属性与B属性进行比较,

实例

from django.db.models import F,Q
def grades(request):
    g = Grades.objects.filter(ggirlnum__gt=F('gboynum'))
    print(g)
    return HttpResponse("打印属性值ggirlnum大于gboynfum的字段值")
支持F对象的算术运算
Grades.objects.filter(ggirlnum__gt=F('gboynum')+20)

Q对象

概述: 过滤器的方法中的关键字参数,条件为And模式

需求: 进行or查询

Students.stuObj2.filter(Q(pk__lte=3)) #匹配id小于等于 3的数据
Students.stuObj.filter(Q(pk=1)| Q(pk=2)) #查询id为1 或者 为2 的所有数据
用Q对象实现复杂的查询
Q(question__startswith='Who') | Q(question__startswith='What') 
等同于WHERE question LIKE 'Who%' OR question LIKE 'What%' 
import datetime
Poll.objects.get ( 
    Q(question__startswith='Who'), 
    Q(pub_date=datetime.date(2018,2,26)) | Q(pub_date=datetime.date(2018,2,26)) 
) 
等同于SELECT * from polls WHERE question LIKE 'Who%' AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06') 
取反
Students.stuObj.filter(~Q(pk__in=[1])) #匹配id为1以外的所有数据

注意

  1. id/pk/exect

    >>> Blog.objects.get (id__exact=14) # Explicit form 
    >>> Blog.objects.get (id=14) # __exact is implied 
    >>> Blog.objects.get (pk=14) # pk implies id__exact 
    等同于select * from where id=14 
    

    注意:pk代表当前的主键 而不是id

  2. 转义 %

    >>> Entry.objects.filter (headline__contains='%') 
    等同于SELECT ... WHERE headline LIKE '%\%%'; 
    
  3. Caching and QuerySets

    >>> print [e.headline for e in Entry.objects.all()] 
    >>> print [e.pub_date for e in Entry.objects.all()] 
    应改写为: 
    >> queryset = Poll.objects.all() 
    >>> print [p.headline for p in queryset] # Evaluate the query set. 
    >>> print [p.pub_date for p in queryset] # Re-use the cache from the evaluation.、 
    这样利用缓存,减少访问数据库的次数。 
    
  4. 删除

    Entry.objects.filter (pub_date__year=2005).delete() 
    

    删除所有

    Entry.objects.all().delete()
    
  5. 一次更新多个值

    # Update all the headlines with pub_date in 2007. 
    Entry.objects.filter (pub_date__year=2007).update(headline='Everything is the same') 
    
    >>> b = Blog.objects.get (pk=1) 
    # Change every Entry so that it belongs to this Blog. 
    >>> Entry.objects.all().update(blog=b) 
    
    #更改id在2,4,6的sname和ssex的值
    Students.stuObj.filter(pk__in=[2,4,6]).update(sname='张三',ssex=1)
    #更改所有的数据的字段sname和ssex的值
    Students.stuObj.all().update(sname='张三',ssex=1)
    #删除id在2,4,6的sname和ssex的值
    Students.stuObj.filter(pk__in=[2,4,6,8,10]).delete()
    
    如果用save()方法,必须一个一个进行保存,需要对其就行遍历,如下: 
    for item in my_queryset: 
        item.save() 
    
    注意:save更适合单条数据的更新 update更适合批量次的更新

模型之间的关系

  1. 1 :1
  2. 1 :N
  3. M:N

一、1:1

  1. 使用models.OneToOneField实现
  2. 这个字段在哪一个模型中都是可以使用的
    • 使用了OneToOne的模型,默认它会随着绑定的数据而进行变化,绑定的数据被删除,它也会被删除
  3. 添加顺序
    • OneToOne绑定的表是主表
    • 使用OneToOne的是从表
  4. on_delete
    • models.CASCADE 默认值
      默认CASECADE,默认从表数据跟随主表删除(比较暴力,容易出问题,开发中基本不用)
    • models.PROTECT 保护模式
      PROTECT,保护默认,开发中常用(主表数据有从表数据的时候,主表是不能实现删除的)
    • models.SET_NULL 置空模式
      # 在删除的时候模式有5个,默认是伴随删除,setXXX设置为某一值
      id_person = models.OneToOneField("Person", on_delete=models.SET_NULL, null=True)#注意 要设置为null为True,因为字段默认不为nully
    • SETXXX 当删除,设置对应的字段值为YYY
  5. PROTECT保护的是主表数据
  6. SETXXX 设置的是从表

二、1:N

  1. 使用ForeignKey实现
  2. 在使用的时候也有on_delete
  3. on_delete和一对一完全一样

三、M:N

  1. 使用ManyToMany实现
  2. 最好使用在从表中,非用户表
  3. 模型在生成数据表的时候,会生成专门的一张表来维护多对多的关系,关系表
  4. 关系表中存储的是两张表的id
  5. 关系表存储多对多的关系
  6. 主从关系
    • 使用ManyToMany的可以认为是从表
    • 从表可以管理自己的主表对应数据
    • 主表也可以而管理从表中自己对应的数据
    • 数据都是一个集合
      • add
      • remove
      • clear

创建多对多关系实例:

  • 方式一

    from django.db import models

    class Class5(models.Model):
    name = models.CharField(max_length=32)

    class Class3(models.Model):
    name = models.CharField(max_length=32)

    class Computer(models.Model):
    c5 = models.ForeignKey("Class5")
    c3 = models.ForeignKey("Class3")1234567891011

  • 方式二

    from django.db import models

    class Class5(models.Model):
    name = models.CharField(max_length=32)

    class Class3(models.Model):
    name = models.CharField(max_length=32)
    c5 = models.ManyToManyField("Class5")12345678

多对多关系增加数据

以方式二为例

from django.db import models

# 先获取Class3表数据
obj = models.Class3.objects.filter(name="xxxx").first()
# 添加关系,与Class5中id为1的关联
obj.c5.add(1)

# obj.c5.add(1,2,3)     # 添加多个关系
# obj.c5.add(*[1,2,3])  # 添加多个关系123456789

多对多关系查询数据

from django.db import models

# 先获取Class3表数据
obj = models.Class3.objects.filter(name="xxxx").first()
# 获取所有与当前数据关联的Class5数据
obj.c5.all()123456

多对多关系更新

from django.db import models

# 先获取Class3表数据
obj = models.Class3.objects.filter(name="xxxx").first()

# 更新数据
obj.c5.set([1,2,3])1234567

多对多关系删除

from django.db import models

# 先获取Class3表数据
obj = models.Class3.objects.filter(name="xxxx").first()

# 删除单条数据
obj.c5.remove(1)

# 删除多条数据
obj.c5.remove(1,2,3)    # 或者:obj.c5.remove(*[1,2,3])

# 删除所有与当前数据关联的数据
obj.c5.clear()

模型继承

  1. 抽象
    • 公共特性,人和狗
    • name,legs
    • 不同的职能
    • 人享受,人做饭,狗看门,
  2. 抽象的模型,不会生成表
    • 继承自抽象模型的模型,它会在自己的表中包含抽象模型中的字段
    • 抽象的模型不能实例化(只能子类实例化)
  3. 非抽象模型,会生成自己的表
    • 非抽象模型也可以继承
    • 所有子表的公共数据会在父表中存储
    • 子表特有的属性会在自己额外的表中存储,子表和父表通过外键级联在一起

模型中的属性

  1. 显性(自己直接创建的)
  2. 隐形的(系统为我们创建的)
  3. Manager(默认系统自动创建,对应的字段叫objects)
    • 自定义管理器
    • 继承自models.Manager
    • 定义自己的方法
      • 添加统一的删除过滤
      • 添加创建对象的方法

ORM字段属性的使用

定义属性

概述
    ·django根据属性的类型确定以下信息
        ·当前选择的数据库支持字段的类型
        ·渲染管理表单时使用的默认html控件
        ·在管理站点最低限度的验证

    ·django会为表增加自动增长的主键列,每个模型只能有一个主键列,如果使用选项设置某属性为主键列后,则django不会再生成默认的主键列

    ·属性命名限制
        ·遵循标识符规则
        ·由于django的查询方式,不允许使用连续的下划线

    ·定义属性时,需要字段类型,字段类型被定义在django.db.models.fields目录下,为了方便使用,被导入到django.db.models中
    ·使用方式
        ·导入from django.db import models
        ·通过models.Field创建字段类型的对象,赋值给属性
逻辑删除
    ·对于重要数据都做逻辑删除,不做物理删除,实现方法是定义isDelete属性,类型为BooleanField,默认值为False

字段类型

    ·AutoField
        ·一个根据实际ID自动增长的IntegerField,通常不指定如果不指定,一个主键字段将自动添加到模型中

    ·CharField(max_length=字符长度)
        ·字符串,默认的表单样式是 TextInput

    ·TextField
        ·大文本字段,一般超过4000使用,默认的表单控件是Textarea

    ·IntegerField
        ·整数

    ·DecimalField(max_digits=None, decimal_places=None)
        ·使用python的Decimal实例表示的十进制浮点数
        ·参数说明
            ·DecimalField.max_digits
                ·位数总数
            ·DecimalField.decimal_places
                ·小数点后的数字位数

    ·FloatField
        ·用Python的float实例来表示的浮点数

    ·BooleanField
        ·true/false 字段,此字段的默认表单控制是CheckboxInput

    ·NullBooleanField
        ·支持null、true、false三种值

    ·DateField([auto_now=False, auto_now_add=False])
        ·使用Python的datetime.date实例表示的日期
        ·参数说明
            ·DateField.auto_now
                ·每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,默认为false
            ·DateField.auto_now_add
                ·当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为false
        ·说明
            ·该字段默认对应的表单控件是一个TextInput. 在管理员站点添加了一个JavaScript写的日历控件,和一个“Today"的快捷按钮,包含了一个额外的invalid_date错误消息键
        ·注意
            ·auto_now_add, auto_now, and default 这些设置是相互排斥的,他们之间的任何组合将会发生错误的结果

    ·TimeField
        ·使用Python的datetime.time实例表示的时间,参数同DateField

    ·DateTimeField
        ·使用Python的datetime.datetime实例表示的日期和时间,参数同DateField

    ·FileField
        ·一个上传文件的字段

    ·ImageField
        ·继承了FileField的所有属性和方法,但对上传的对象进行校验,确保它是个有效的image

字段选项

字段选项
    ·概述
        ·通过字段选项,可以实现对字段的约束
        ·在字段对象时通过关键字参数指定

    ·null
        ·如果为True,Django 将空值以NULL 存储到数据库中,默认值是 False

    ·blanke
        ·如果为True,则该字段允许为空白,默认值是 False

    ·注意
        ·null是数据库范畴的概念,blank是表单验证证范畴的

    ·db_column
        ·字段的名称,如果未指定,则使用属性的名称

    ·db_index
        ·若值为 True, 则在表中会为此字段创建索引

    ·default
        ·默认值

    ·primary_key
        ·若为 True, 则该字段会成为模型的主键字段

    ·unique
        ·如果为 True, 这个字段在表中必须有唯一值

关系

关系
    ·分类
        ·ForeignKey:一对多,将字段定义在多的端中
        ·ManyToManyField:多对多,将字段定义在两端中
        ·OneToOneField:一对一,将字段定义在任意一端中

    ·用一访问多
        ·格式
            ·对象.模型类小写_set
        ·示例
            grade.students_set

    ·用一访问一
        ·格式
            ·对象.模型类小写
        ·示例
            ·grade.students

    ·访问id
        ·格式
            ·对象.属性_id
        ·示例
            ·student.sgrade_id

视图 view

一 视图的概念

(1) 视图的作用

视图接受web请求,并响应web请求

(2) 视图的本质

视图就是一个python中的函数

(3) 响应

  1. 网页
    • 重定向
    • 错误视图
      • 404
      • 500
      • 400
  2. JSON数据

二 url配置

路由:

处理URL与函数之间关系的程序称为路由

(1) 配置流程

制定根级url配置文件

settings.py文件中的ROOT_URLCONF(默认实现了)

ROOT_URLCONF = 'project.urls'
ROOT_URLCONF = '当前Django项目名.urls'

(2) urlpatterns

一个url实例的列表

url参数
  1. Z正则表达式
  2. 视图名称
  3. 名称 当前反向解析的name的名称
url匹配正则的注意事项:
  1. 如果想要从url中获取一个值,需要对正则加小括号
  2. 匹配正则前方不需要加反斜杠
  3. 正则前需要加r表示字符串不转义

实例

urlpatterns = [
    url(r'^stu/(\d+)/$', views.stupage,name='stupage'),
]

(3) 引入其他url配置

在应用中创建urls.py文件,定义本应用的url配置,在工程urls.py文件中使用include()方法

from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^', include('myApp.urls', namespace="myApp"))
]

myApp文件夹下的URL配置s
from django.conf.urls import url
from . import views/fsrom myApp import views  那种引入views都可以
urlpatterns = [
    url(r'^$', views.index, name="index"),
]

(4) URL的反向解析

概述: 如果在视图、模板中使用了硬编码链接,在url配置发生改变时,动态生成链接的地址

解决: 在使用链接时,通过url配置的名称,动态生成url地址

作用于: 使用url模板

使用方法:

  • 定义url时,需要为include定义namespace属性,为url定义name属性
  • 使用时,在模板中使用url标签,在视图中使用reverse函数,根据正则表达式动态生成地址,减轻后期维护成本。
模板中超链接步骤:

1)在项目urls.py中为include定义namespace属性。

url(r’^’,include(‘booktest.urls’,namespace=’booktest’)),

2)在应用的urls.py中为url定义name属性,并修改为fan2。

url(r’^fan2/$’, views.fan2,name=’fan2’),

3)在模板中使用url标签做超链接,此处为templates/booktest/fan1.html文件。

<html>
<head>
    <title>反向解析</title>
</head>
<body>
普通链接:<a href="/fan2/">普通fan2</a>
<hr>
反向解析:<a href="{%url 'booktest:fan2'%}">反向解析fan2</a>
</body>
</html>

4)回到浏览器中,后退,刷新,查看源文件,两个链接地址一样

[图片上传失败...(image-80ea03-1530838081153)]

5)在应用的urls.py中,将fan2修改为fan_show。

url(r’^fan_show/$’, views.fan2,name=’fan2’),

6)回到浏览器中,刷新,查看源文件,两个链接地址不一样。

[图片上传失败...(image-ea84ae-1530838081153)]

三 视图函数

(1) 定义视图

本质:一个函数

视图参数:

  • 一个HttpRequest的实例
  • 通过正则表达式获取的参数

位置: 一般在views.py文件下定义

(2) 错误视图

404视图

找不到网页(url匹配不成功)时返回

在templates目录下定义404.html

request_path 导致错误的网址

实例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>404页面</title>
</head>
<body>
    <h1>页面丢失</h1>
    <h2>{{request_path}}</h2>
</body>
</html>

配置settings.py

DEBUG 如果为True永远不会调用404.html页面

settings 第26行
DEBUG = False
500视图

在视图代码中出现错误(服务器代码)

400视图

错误出现在客户的操作

四 HttpRequest对象

(1) 概述

服务器接收http请求后,会根据报文创建HttpRequest对象

视图的第一个参数就是HttpRequest对象

django创建的,之后调用试图时传递给视图

也就是在浏览器请求的时候给视图的数据

(2) 属性

  1. path 请求的完整路径(不包括域名和端口)
  2. method 表示请求的方式,常用的有GET、POST
  3. encoding 表示浏览器提交的数据的编码方式 一般为utf-8
  4. GET 类似字典的对象,包含了get请求的所有参数
  5. POST 类似字典的对象,包含了post请求的所有参数
  6. FILES 类似字典的对象,包含了所有上传的文件
  7. COOKIES 字典,包含所有的cookie
  8. session 类似字典的对象,表示当前会话

实例

print(request.path)
print(request.method)
print(request.encoding) #在视图中获取不到
print(request.GET)
print(request.POST)
print(request.FILES)
print(request.COOKIES)
print(request.session)

(3) 方法

is_ajax() 如果是通过XMLHttpRequest发起的,返回True

QueryDict对象

(4) QueryDict对象

request对象中的GET、POST都属于QueryDict对象

方法

get()

  • 作用:根据键获取值d
  • 只能获取一个值
  • www.sunck.wang/abc?a=1&b=2&c=3

getlist()

  • 将键的值以列表的形式返回
  • 可以获取多个值dx
  • www.sunck.wang/abc?a=1&a=2&c=3

(5) GET属性

获取浏览器传递过来给服务器的数据

实例

url为 http://127.0.0.1:8000/sunck/get1?a=1&b=2&c=3
def get1(request):
    a = request.GET.get('a')
    b = request.GET['b']
    c = request.GET.get('c')
    return HttpResponse(a + "  " + b + "  " + c)

http://127.0.0.1:8000/sunck/get2?a=1&a=2&c=3
def get2(request):
    a = request.GET.getlist('a')
    a1 = a[0]
    a2 = a[1]
    c = request.GET.get('c')
    return HttpResponse(a1 + "  " + a2 + "  " + c)

(6) POST属性

使用表单提交实现post请求

关闭csrf

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

实例

def showregist(request):
    return render(request, 'myApp/regist.html')
def regist(request):
    name = request.POST.get("name")
    gender = request.POST.get("gender")
    age = request.POST.get("age")
    hobby = request.POST.getlist("hobby")
    print(name)
    print(gender)
    print(age)
    print(hobby)
    return HttpResponse("post")

五 HttpResponse对象

概述:给浏览器返回数据

HttpRequest对象是由django创建的,HttpResponse对象由程序员创建

(1) 返回用法

不调用模板,直接返回数据

HttpResponse("直接返回给浏览器 不会调用模板")

from django.http import HttpResponse
def index(request):
    return HttpResponse("直接返回给浏览器 不会调用模板")

调用模板

使用render方法

原型:render(request, templateName[, context])

作用: 结合数据和模板,返回完整的HTML页面

参数:

  • request 请求体对象
  • templateName 模板路径
  • context 传递给需要渲染在模板上的数据

实例:

def index(request):
    return render(request,'myApp/index.html',{'传递给模板的键名':"传递过去的数据"})

(2) 属性

  1. content 表示返回的内容

  2. charset 编码格式

  3. status_code 响应状态码 【ˈstætəs'】

  4. _content_type_for_repr 指定输出的MIME类型

    MIME类型-在把输出结果传送到浏览器上的时候,浏览器必须启动是党的应用程序来处理这个输出文档。这可以通过多种类型MIME(多功能网际邮件扩充协议)来完成。在HTTP中,MIME类型被定义在Content-Type header中
    

实例

def showHttResponse(request):
  res = HttpResponse()
  res.content = b'good' #设置内容
  print(res.content)
  print(res.charset)
  print(res.status_code)
  print(res._content_type_for_repr)

(3) 方法

  1. HttpResponse() 使用页面内容实例化HttpResponse对象

  2. write(content) 以文件的形式写入 向浏览器输出内容

  3. flush() 以文件的形式输出缓冲区

  4. set_cookie(key, value='', max_age=None,expires=None) 设置cookie

  5. delete_cookie(key) 删除cookie

  6. Request.COOKIES.get(key) 获取cookie 的值

    注意: 如果删除一个不存在的key,就当什么都没发生

Cookie:会话控制 cookie和session 为了维护客户端的状态 因为HTTP协议是无状态的协议

Cookie常用参数

  • key:键
  • value:值
  • max_age:多久后过期,时间为秒
  • expires:过期时间,为具体时间
  • path:生效路径
  • domain:生效的域名
  • secure:HTTPS传输时应设置为true
  • httponly:值应用于http传输,JavaScript无法获取
def myCookie(request):
    res = HttpResponse()
    print(request.COOKIES)s
    print(request.COOKIES.get('a','b')) #获取cookie键为a的值 如果cookie不存在则返回b
    # res.set_cookie('mycookie','value',expires=2) #设置2秒的过期时间
    res.delete_cookie(key)#删除cookie
    return res

Cookie加解密

# salt:加密盐,内容可随便定义
HttpResponse().set_signed_cookie("key","value",salt="jkkll")


# 解密,加密盐的内容应与加密时的内容保持一致
request.get_signed_cookie("key",salt="jkkll")

注意:如果没有设置Cookie超时时间,表示关闭浏览器之后自动删除Cookie,Cookie尽量避免存储敏感信息。

六 子类HttpResponseRedirect

功能: 重定向,服务器端跳转

简写: redirect(to) to推荐使用反向解析

实例

# 重定向
from django.http import HttpResponseRedirect
from django.shortcuts import redirect
def redirect1(request):
    # return HttpResponseRedirect('/sunck/redirect2')
    return redirect('/sunck/redirect2')
def redirect2(request):
    return HttpResponse("我是重定向后的视图")

重定向参数问题

from django.core.urlresolvers import reverse

1)在booktest/urls.py中,修改fan2如下:

url(r’^fan(\d+)_(\d+)/$’, views.fan2,name=’fan2’),

from django.shortcuts import redirect
from django.core.urlresolvers import reverse

def redirect1(request):
    return redirect(reverse('myApp:fun2',args = (1,2)))
    return HttpResponseRedirect(reverse('app:car',args=[1]))
def fun2(request,arg1,arg2):
    print(arg1,arg2)
    return HttpResponse("我是重定向后的视图")

注意:路径必须使用反向解析 参数必须为元组或者列表的序列类型

关键字参数

1)在booktest/urls.py中,修改fan2如下:

 url(r'^fan(?P<id>\d+)_(?P<age>\d+)/$', views.fan2,name='fan2'),
给匹配的值起下标名的例子
import re
x = re.compile("(?P<id>\d+)_(?P<age>\d+)")
a = x.search('1_2')
print(a.group('id'))
print(a.group('age'))

2)修改templates/booktest/fan1.html文件如下:

<html>
<head>
    <title>反向解析</title>
</head>
<body>
普通链接:<a href="/fan100_18/">fan2</a>
<hr>
<!--关键字参数-->
反向解析:<a href="{%url 'booktest:fan2' id=100 age=18%}">fan2</a>
<!--普通参数-->
反向解析:<a href="{%url 'booktest:fan2' 100 18%}">fan2</a>
</body>
</html>

3)回到浏览器中,刷新,查看源文件如下图:

[图片上传失败...(image-758b3c-1530838081153)]

  • 使用重定向传递关键字参数格式如下:

return redirect(reverse(‘booktest:fan2’, kwargs={‘id’:110,’age’:26}))

七 子类JsonResponse

返回json数据,一般用于异步请求

json = JsonResponse(data)

注意:Content-type类型为application/jsonc

from django.http import HttpResponseRedirect,JsonResponse
def json(request):
  j = JsonResponse({'name':'zs','age':18})
  print(type(j))
  return HttpResponse(j)

八 session

概述: http是无状态的协议,每次请求都是一次新的请求

客户端与服务器端的一次通信就是一次会话

实现状态保持,在客户端或者服务端存储有关会话的数据

存储方式:

  1. cookie 所有的数据存储在客户端,不要存敏感的数据
  2. session 所有的数存储在服务端,在客户端用cookie存储session_id

状态保持的目的:在一段时间内跟踪请求者的状态,可以实现跨页面访问当前的请求者的数据

注意: 不同的请求者之间不会共享这个数据,与请求者一一对应的

(1) 启用session

settings文件中

INSTALLED_APPS  'django.contrib.sessions',  34行
MIDDLEWARE  'django.contrib.sessions.middleware.SessionMiddleware', 44行

(2) 使用session

启用session后,每个HttpRequest对象都有一个session属性,就是一个类似字典的对象

1.设置session

request.session['键'] = '值'

2. 根据键获取session值

get(key, default=None)

request.session.get["k1"],如果不存在则会报错,为了防止出错可以request.session.get('k1',none)

3. 清空所有的会话(但是不会删除Django的session表中的session数据和cookie)

clear()

request.session.clear()

4. 删除当前的会话并删除会话的cookie

flush()

request.session.flush()

del request.session['k1'] 删除

5.导入 logout 清除session

from django.contrib.auth import logout

logout(request)

注意:cookie依然存在 只是清除了cookie的值和session表中的数据

(3) 设置过期时间

  1. 如果不设置,14天后过期

  2. 整数:

    request.session.set_expiry(10)

    但是不会删除Django的session表中的session数据

  3. 时间对象

  4. 0 关闭浏览器时失效

  5. None 依赖全局session失效策略

实例 登录退出

def index(request):
    myDict = 
    myDict['mame'] = '首页'
    print(request.session.get('username','游客'))
    myDict['username'] = request.session.get('username','游客')
    return render(request,'myApp/index.html',myDict)
#登录
def login(request):
    print('走到这里了')
    return render(request,'myApp/login.html')
#登录处理
def doLogin(request):
    print(request.POST.get('uname'))
    print(request.POST.get('password'))
    request.session['username'] = request.POST.get('uname')
    request.session.set_expiry(10) #设置过期时间
    return redirect('/abc/')
#退出登录
from django.contrib.auth import logout as l
def logout(request):
    # request.session.flush()
    # request.session.clear()
    l(request)
    return HttpResponse('<meta http-equiv="refresh" content="3;/abc/">退出成功')

#模板
<form action="{% url 'myApp:dologin' %}" method="post">
    <p>用户名: <input type="text" name="uname"></p>
    <p>密码: <input type="password" name="password"></p>
    <p><input type="submit" value="submit"></p>
</form>

#URL
url(r'^login/$',views.login,name='login'),
url(r'^dologin/$',views.doLogin,name='dologin'),
url(r'^logout/$',views.logout,name='logout'),

配置 settings.py

(4) 存储session的位置

  1. 数据库 默认存储在数据库中

    Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。
    SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)    
    
  2. 缓存 (只存储在本地内存中,如果丢失不能找回,比数据库快)

    SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
    SESSION_CACHE_ALIAS = 'default'        # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
    
  3. 数据库和缓存 (优先从本地缓存中读取,读取不到再去数据库中获取)

    SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'
    SESSION_CACHE_ALIAS = 'default'                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
    
  4. 设置session的公共部分

        SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
        SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路径(默认)
        SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默认)
        SESSION_COOKIE_SECURE = False                            # 是否Https传输cookie(默认)
        SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支持http传输(默认)
        SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(默认)
        SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否关闭浏览器使得Session过期(默认)
        SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次请求都保存Session,默认修改之后才保存(默认)
    

(5) 使用redis缓存session

安装 django-redis-sessions

pip install django-redis-sessions

settings.py进行配置

SESSION_ENGINE = 'redis_sessions.session'
SESSION_REDIS_HOST = 'localhost'
SESSION_REDIS_PORT = 6379
SESSION_REDIS_DB = 0
SESSION_REDIS_PASSWORD = '123456'
SESSION_REDIS_PREFIX = 'session'

总报redis连接 密码输入错误

(6) session常用的操作

# 获取、设置、删除Session中数据
        request.session['k1']
        request.session.get('k1',None)
        request.session['k1'] = 123
        request.session.setdefault('k1',123) # 存在则不设置
        del request.session['k1']
 
        # 所有 键、值、键值对
        request.session.keys()
        request.session.values()
        request.session.items()
        request.session.iterkeys()
        request.session.itervalues()
        request.session.iteritems()
 
 
        # 用户session的随机字符串
        request.session.session_key
 
        # 将所有Session失效日期小于当前日期的数据删除
        request.session.clear_expired()
 
        # 检查 用户session的随机字符串 在数据库中是否
        request.session.exists("session_key")
 
        # 删除当前用户的所有Session数据
        request.session.delete("session_key")
 
        request.session.set_expiry(value)
            * 如果value是个整数,session会在些秒数后失效。
            * 如果value是个datatime或timedelta,session就会在这个时间后失效。
            * 如果value是0,用户关闭浏览器session就会失效。
            * 如果value是None,session会依赖全局session失效策略。

模板 templates

一 概述

模板由两部分组成

  1. HTML代码
  2. 逻辑控制代码

作用

快速生成HTML页面

优点

  1. 模板的设计实现了业务逻辑与现实内容的分离
  2. 视图可以使用任何模板l

模板处理

  1. 加载
  2. 渲染

二 定义模板

(1) 变量

  1. 视图传递给模板的数据

  2. 要遵守标识符规则

  3. 语法

    {{ var }}

  4. 注意

    如果使用的变量不存在,则插入的是空字符串

  5. 在模板中使用语法

    • 字典查询
    • 属性或者方法
    • 数字索引
  6. 在模板中调用对象的方法

    注意:不能传递参数

(2) 标签

语法: {% tag %}

作用:

  1. 在输出中创建文本
  2. 控制逻辑和循环

1、if/else 标签

==, !=, >=, <=, >, < 这些比较都可以在模板中使用

and, or, not, in, not in 也可以在模板中使用

基本语法格式如下:

{% if condition %}
     ... display
{% endif %}

或者:

{% if condition1 %}
   ... display 1
{% elif condition2 %}
   ... display 2
{% else %}
   ... display 3
{% endif %}

{% if a == 10 %}
    <h1>10</h1>
{% elif a == 20 %}
    <h2>20</h2>
{% endif %}

根据条件判断是否输出。if/else 支持嵌套。

{% if %} 标签接受 and , or 或者 not 关键字来对多个变量做判断 ,或者对变量取反( not ),例如:

{% if athlete_list and coach_list %}
     athletes 和 coaches 变量都是可用的。
{% endif %}d

2、for 标签

{% for %} 允许我们在一个序列上迭代。

与Python的 for 语句的情形类似,循环语法是 for X in Y ,Y是要迭代的序列而X是在每一个特定的循环中使用的变量名称。

每一次循环中,模板系统会渲染在 {% for %} 和 {% endfor %} 之间的所有内容。

例如,给定一个运动员列表 athlete_list 变量,我们可以使用下面的代码来显示这个列表:

<ul>
{% for athlete in athlete_list %}
    <li>{{ athlete.name }}</li>
{% endfor %}
</ul>
给标签增加一个 reversed 使得该列表被反向迭代:
{% for athlete in athlete_list reversed %}
...
{% endfor %}
可以嵌套使用 {% for %} 标签:
{% for athlete in athlete_list %}
    <h1>{{ athlete.name }}</h1>
    <ul>
    {% for sport in athlete.sports_played %}
        <li>{{ sport }}</li>
    {% endfor %}
    </ul>
{% endfor %}
{% empty %}
{% for  变量  in  列表 %}
语句1
{% empty %}
语句2
{% endfor %}

注意: 列表为空或者列表不存在时执行语句2

{{ forloop.counter }} 表示当前是第几次循环

遍历字典

views.py

def home(request):
    info_dict = {'site': u'自强学堂', 'content': u'各种IT技术教程'}
    return render(request, 'home.html', {'info_dict': info_dict})

home.html

站点:{{ info_dict.site }} 内容:{{ info_dict.content }}

注意:在模板中取字典的键是用点info_dict.site,而不是Python中的 info_dict['site']

还可以这样遍历字典:

{% for key, value in info_dict.items %}
    {{ key }}: {{ value }}
{% endfor %}

其实就是遍历这样一个 List: [('content', u'自强学堂'), ('site', u'各种IT技术教程')]

实例,在模板进行 条件判断和 for 循环的详细操作:

views.py

def home(request):
    List = map(str, range(100))# 一个长度为100的 List
    return render(request, 'home.html', {'List': List})

假如我们想用逗号将这些元素连接起来:

home.html

{% for item in List %}
    {{ item }}, 
{% endfor %}

效果如下:

[图片上传失败...(image-21fe0b-1530838118122)]

我们会发现最后一个元素后面也有一个逗号,这样肯定不爽,如果判断是不是遍历到了最后一个元素了呢?

用变量 forloop.last 这个变量,如果是最后一项其为真,否则为假,更改如下:

{% for item in List %}
    {{ item }}{% if not forloop.last%},{% endif %} 
{% endfor %}

在for循环中还有很多有用的东西,如下:

变量 描述
forloop.counter 索引从 1 开始算
forloop.counter0 索引从 0 开始算
forloop.revcounter 索引从最大长度到 1
forloop.revcounter0 索引从最大长度到 0
forloop.first 当遍历的元素为第一项时为真
forloop.last 当遍历的元素为最后一项时为真
forloop.parentloop 用在嵌套的 for 循环中,获取上一层 for 循环的 forloop

3、ifequal/ifnotequal 标签

{% ifequal %} 标签比较两个值,当他们相等时,显示在 {% ifequal %} 和 {% endifequal %} 之中所有的值。

下面的例子比较两个模板变量 user 和 currentuser :

{% ifequal user currentuser %}
    <h1>Welcome!</h1>
{% endifequal %}

和 {% if %} 类似, {% ifequal %} 支持可选的 {% else%} 标签:

{% ifequal section 'sitenews' %}
    <h1>Site News</h1>
{% else %}
    <h1>No News Here</h1>
{% endifequal %}

4、注释标签b

comment

作用:注释多行

{% comment %}
    <h1>sunck is a cool man</h1>
    <h1>sunck is a handsome man</h1>
    {{stu.sname}}
{% endcomment %}
Django 注释使用 {# #}。
{# 这是一个注释 #}

注意:注释的代码都不会再浏览器的HTML页面中显示出来

5、include 标签

{% include %} 标签允许在模板中包含其它的模板的内容。

下面这个例子都包含了 nav.html 模板:

{% include "nav.html" %}

注意:如果是myApp下的模板需要写成

{% include "myApp/head.html" %}

6、url 反向解析

作用:反向解析

格式: {% url 'namespace:name' [ 参数1 参数2 ] %}

<a href="{% url 'myApp:logout' %}">退出</a>

反向解析中URL的参数

位置参数

1)在booktest/urls.py中,修改fan2如下:

url(r’^fan(\d+)_(\d+)/$’, views.fan2,name=’fan2’),

2)修改templates/booktest/fan1.html文件如下:

<html>
<head>
    <title>反向解析</title>
</head>
<body>
普通链接:<a href="/fan2_3/">fan2</a>
<hr>
反向解析:<a href="{%url 'booktest:fan2' 2 3%}">fan2</a>
</body>
</html>12345678910

关键字参数

1)在booktest/urls.py中,修改fan2如下:

 url(r'^fan(?P<id>\d+)_(?P<age>\d+)/$', views.fan2,name='fan2'),

2)修改templates/booktest/fan1.html文件如下:

<html>
<head>
    <title>反向解析</title>
</head>
<body>
普通链接:<a href="/fan100_18/">fan2</a>
<hr>
反向解析:<a href="{%url 'booktest:fan2' id=100 age=18%}">fan2</a>
</body>
</html>12345678910
  • 使用重定向传递关键字参数格式如下:

return redirect(reverse(‘booktest:fan2’, kwargs={‘id’:110,’age’:26}))

7、跨站请求伪造 csrf

某些恶意网站包含链接、表单、按钮、js,利用登陆用户在浏览器中认证,从而攻击服务

作用:用于跨站请求伪造保护

防止CSRF

在settings.py文件中的MIDDLEWARE增加(默认就有)

'django.middleware.csrf.CsrfViewMiddleware',

格式:{% csrf_token %}

实例
<form action="{% url 'myApp:dologin' %}" method="post">
    {% csrf_token %}  
</form>

8、模板继承

block、extends

作用:用于模板的继承 可以减少页面的内容的重复定义,实现页面的重用

block标签

在父模板中预留区域,子模板去填充

语法:

{% block  标签名 %}

{%endblock 标签名%}

extends标签

语法:

{% extends  '父模板路径' %}

接下来我们先创建之前项目的 templates 目录中添加 base.html 文件,代码如下:

HelloWorld/templates/base.html 文件代码:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{% block title %}菜鸟教程(runoob.com){% endblock %}</title>
</head>
<body>
   {% include 'nav.html' %}
 
   {% block content %}
    <div>这里是默认内容,所有继承自这个模板的,如果不覆盖就显示这里的默认内容。</div>
   {% endblock %}
   {% include 'bottom.html' %}
</body>
</html>

以上代码中,名为 mainbody 的 block 标签是可以被继承者们替换掉的部分。

所有的 {% block %} 标签告诉模板引擎,子模板可以重载这些部分。

hello.html 中继承 base.html,并替换特定 block,hello.html 修改后的代码如下:

{% extends "base.html" %}
 
{% block title %}欢迎光临首页{% endblock %}
 
{% block content %}
{% include 'ad.html' %}
这里是首页,欢迎光临
{% endblock %}

9、HTML转义

return render(request,'myApp/index.html',{"code":"<h1>xlg is a very good man</h1>"})
{{code}}

将接收到的code当成普通字符串渲染

将接收到的字符串当成HTML代码渲染

  1. safe

    {{code|safe}}
    
  2. autoescape

    {% autoescape off %}
       {{code}}
    {% endautoescape %}
    

(3) 过滤器

语法:{{ var|过滤器 }}

作用:在变量被显示前修改它

模板过滤器可以在变量被显示前修改它,过滤器使用管道字符,如下所示:

{{ name|lower }}

{{ name }} 变量被过滤器 lower 处理后,文档大写转换文本为小写。

过滤管道可以被* 套接* ,既是说,一个过滤器管道的输出又可以作为下一个管道的输入:

{{ my_list|first|upper }} #第一个显示并转化为大写
{{ my_list|last|upper }}  #最后一个显示并转化为大写

以上实例将第一个元素并将其转化为大写。

有些过滤器有参数。 过滤器的参数跟随冒号之后并且总是以双引号包含。 例如:

{{ bio|truncatewords:"30" }}
{{ letter|truncatewords:'20'|upper }} #取出钱20个值并转化为大写

这个将显示变量 bio 的前30个词。

注意: 需要将变量bio的单词或者汉子 空格来划分 否则不起作用 如:bio = ab cd ef 而不是 abcdef

其他过滤器:
  • addslashes : 添加反斜杠到任何反斜杠、单引号或者双引号前面。

    letter:'abc\def'
    {{ letter|addslashes }}
    
  • date : 按指定的格式字符串参数格式化 date 或者 datetime 对象,实例:

    {{ pub_date|date:"F j, Y" }}
    
  • length : 返回变量的长度。

  • join

    格式:值|join:‘#’

    <h1>{{list|join:'#'}}</h1>
    
如果一个变量没有被提供,或者值为false、空,可以使用默认值

default

格式:{{ var|default:'good'}}

<h1>{{test|default:'没有'}}</h1>

django 模板中 加减乘除 求余

加法

{{value|add:10}}
note:value=5,则结果返回15

减法

{{value|add:-10}}
note:value=5,则结果返回-5,加一个负数就是减法了

乘法

{% widthratio 5 1 100%}
note:等同于:(5 / 1) * 100 ,结果返回500,withratio需要三个参数,它会使用参数1/参数2*参数3的方式进行运算,进行乘法运算,使「参数2」=1

除法

{% widthratio 5 100 1%}
note:等同于:(5 / 100) * 1,则结果返回0.05,和乘法一样,使「参数3」= 1就是除法了。

求余 求2的余数

{% if forloop.counter0|divisibleby:2 %}

三 验证码

作用:在用户注册、登陆页面的时候使用,为了防止暴力请求,减轻服务器的压力 防止csrf一种方式

验证码代码

安装pillow模块

pip install pillow

def verifycode(request):
    #引入绘图模块
    from PIL import Image, ImageDraw, ImageFont
    #引入随机函数模块
    import random
    #定义变量,用于画面的背景色、宽、高
    bgcolor = (random.randrange(20, 100), random.randrange(
        20, 100), random.randrange(20, 100))
    width = 100
    height = 50
    #创建画面对象
    im = Image.new('RGB', (width, height), bgcolor)
    #创建画笔对象
    draw = ImageDraw.Draw(im)
    #调用画笔的point()函数绘制噪点
    for i in range(0, 100):
        xy = (random.randrange(0, width), random.randrange(0, height))
        fill = (random.randrange(0, 255), 255, random.randrange(0, 255))
        draw.point(xy, fill=fill)
    #定义验证码的备选值
    str = '1234567890QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'
    #随机选取4个值作为验证码
    rand_str = ''
    for i in range(0, 4):
        rand_str += str[random.randrange(0, len(str))]
    #构造字体对象
    font = ImageFont.truetype(r'C:\Windows\Fonts\AdobeArabic-Bold.otf', 40)
    #构造字体颜色
    fontcolor1 = (255, random.randrange(0, 255), random.randrange(0, 255))
    fontcolor2 = (255, random.randrange(0, 255), random.randrange(0, 255))
    fontcolor3 = (255, random.randrange(0, 255), random.randrange(0, 255))
    fontcolor4 = (255, random.randrange(0, 255), random.randrange(0, 255))
    #绘制4个字
    draw.text((5, 2), rand_str[0], font=font, fill=fontcolor1)
    draw.text((25, 2), rand_str[1], font=font, fill=fontcolor2)
    draw.text((50, 2), rand_str[2], font=font, fill=fontcolor3)
    draw.text((75, 2), rand_str[3], font=font, fill=fontcolor4)
    #释放画笔
    del draw
    #存入session,用于做进一步验证
    request.session['verify'] = rand_str
    #内存文件操作
    import io
    buf = io.BytesIO()
    #将图片保存在内存中,文件类型为png
    im.save(buf, 'png')
    #将内存中的图片数据返回给客户端,MIME类型为图片png
    return HttpResponse(buf.getvalue(), 'image/png')
模板中的代码
<form method="post" action="/verifycodecheck/">
    {%csrf_token%}
    <input type="text" name="verifycode"/>
    <img src="/verifycode/" alt="" onclick="this.src='/verifycode/?id='+Math.random()"></p>
    <input type="submit" value="登陆"/>
    <span>{{flag}}</span>
</form>
在视图中去验证验证码
from django.shortcuts import render,redirect
def verifycodefile(request):
    f = request.session.get("flag", True)
    str = ""
    if f == False:
        str = "请重新输入"
    request.session.clear()
    return render(request,'myApp/verifycodefile.html',{"flag":str})
def verifycodecheck(request):
    code1 = request.POST.get("verifycode").upper()
    code2 = request.session["verify"].upper()
    if code1 == code2:
        return render(request,'myApp/success.html')
    else:
        request.session["flag"] = False
        return redirect('/verifycodefile/')
URL配置
url(r'^verifycode/$', views.verifycode),
url(r'^verifycodefile/$', views.verifycodefile),
url(r'^verifycodecheck/$', views.verifycodecheck),

Django高级

一、静态文件

管理静态文件(例如图像,JavaScript,CSS,字体,图片)

网站通常需要提供其他文件,如图片,JavaScript或CSS。在Django中,我们将这些文件称为“静态文件”。Django提供 django.contrib.staticfiles来帮助你管理它们。

本页介绍如何提供这些静态文件。

配置静态文件
  1. 确保django.contrib.staticfiles包含在你的 INSTALLED_APPS

  2. 在您的设置文件中,定义STATIC_URL,例如:

    STATIC_URL = '/static/'
    
  3. 在您的模板中,或者使用硬编码url的方式 /static/my_app/example.jpg,最好使用static 模板标签通过使用配置的STATICFILES_STORAGE存储来构建给定相对路径的URL (当您想要切换到内容交付网络(CDN)时,用于提供静态文件)。

    {% load static %}
    <img src="{% static "my_app/example.jpg" %}" alt="My image"/>
    
  4. 将您的静态文件存储static在应用程序中调用的文件夹中。例如

    my_app/static/my_app/example.jpg

您的项目可能还会有不与特定应用绑定的静态资产。除了static/在应用程序中使用目录之外,您还可以[STATICFILES_DIRS]在设置文件中定义一个目录列表(),其中Django也将查找静态文件。例如:

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
    '/var/www/static/',
]
配置settings.py
STATIC_URL = '/static/'#比如图片
#普通文件会查找下面的路径  比如导入js,css
STATICFILES_DIRS = [
    os.path.join(BASE_DIR,'static')
]
实例
{% load static from staticfiles %}
{% load staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
    <link rel="stylesheet" type="text/css" href="{% static 'myApp/css/style.css' %}"/>
    <script type="text/javascript" src="/static/myApp/js/jquery-3.1.1.min.js"></script>
    <script type="text/javascript" src="/static/myApp/js/sunck.js"></script>
</head>
<body> 
    <h1>xlg is a good man</h1>
    <img src="/static/myApp/img/2.png"/>
    <img src="{% static 'myApp/img/2.png' %}"/>
</body>
</html>

二、中间件

概述: 一个轻量级、底层的插件,可以介入Django的请求和响应

本质:一个Python类

方法

  1. __init__不需要传参数,服务器响应第一个请求的时候自动调用,用于确定是否启用该中间件

  2. process_request(self,request) 在执行视图之前被调用(分配url匹配视图之前),每个请求上都会调用,返回None或者HttpResponse对象

  3. process_view(self,request,view_func,view_args,view_kwargs) 调用视图之前执行,每个请求都会调用,返回None或者HttpResponse对象see

  4. process_template_response(self,request,response) 在视图刚好执行完后调用,每个请求都会调用,返回None或者HttpResponse对象

    使用render

  5. process_response(self,request,response) 所有响应返回浏览器之前调用,每个请求都会调用,返回HttpResponse对象

  6. process_exception(self,request,exception) 当视图抛出异常时调用,返回HttpResponse对象

自定义中间件

  1. UM和myApp同级->创建工程目录middleware-->目录下创建myApp目录

  2. |-myApp
    |-middleware
    |--myApp
    |----myMiddle.py
    |-project
    

  3. 在middleware下的myApp里 创建一个python文件 myMiddle.py

from django.utils.deprecation import MiddlewareMixin
class MyMiddle(MiddlewareMixin):
    def process_request(self, request):
        print("get参数为:", request.GET.get("a"))
  1. 使用自定义中间件

    配置settings.py文件

    MIDDLEWARE中添加

    'middleware.myApp.myMiddle.MyMiddle'

三、上传图片

概述:文件上传时,文件数据存储在request.FILES属性中

注意:

  1. form表单要上传文件需要加enctype="multipart/form-data"
  2. 上传文件必须是post请求

存储路径

  1. 在static目录下创建upfile目录用于存储接收上传的文件

  2. 配置settings.py文件

    MDEIA_ROOT=os.path.join(BASE_DIR,r'static/upfile')

代码示例

模板

<body>
    <form method="post" action="/savefile/" enctype="multipart/form-data">
        {%csrf_token%}
        <input type="file" name="file"/>
        <input type="submit" value="上传"/>
</form>

视图

def upfile(request):
    return render(request, 'myApp/upfile.html')
import os
from django.conf import settings
def savefile(request):
    if request.method == "POST":
        f = request.FILES["file"]
        # 文件在服务器端的路径
        #os.path.splitext(path)获取文件扩展名
        filePath = os.path.join(settings.MDEIA_ROOT, f.name)
        with open(filePath, 'wb') as fp:
            for info in f.chunks():# 分块写入文件  
                fp.write(info)
        return HttpResponse("上传成功")
    else:
        return HttpResponse("上传失败")

在进行进一步的代码解释之前,需要先讲几个关于上传文件的方法和属性:

myFile.read():从文件中读取整个上传的数据,这个方法只适合小文件;

myFile.chunks():按块返回文件,通过在for循环中进行迭代,可以将大文件按块写入到服务器中;

myFile.multiple_chunks():这个方法根据myFile的大小,返回True或者False,当myFile文件大于2.5M(默认为2.5M,可以调整)时,该方法返回True,否则返回False,因此可以根据该方法来选择选用read方法读取还是采用chunks方法:

     if myFile.multiple_chunks() == False:
         # 使用myFile.read()
      else:
         # 使用myFile.chunks()

myFile.name:这是一个属性,不是方法,该属性得到上传的文件名,包括后缀,如123.exe;

myFile.size:这也是一个属性,该属性得到上传文件的大小。

四、分页

(1) Paginator对象

  1. 创建对象

    格式:Paginator(列表, 整数)

    返回值:返回的分页对象

  2. 属性

    count 对象总数

    num_pages 页面总数

    page_range [1,2,3,4,5] 页码从1开始

  3. 方法

    page(num) 获得一个Page对象,如果提供的页码不存在会抛出"InvalidPage"异常

  4. 异常

    • InvalidPage 当向page()传递的是一个无效的页码时抛出
    • PageNotAnInteger 当向page()传递的不是一个整数时抛出
    • EmptyPage 当向page()传递一个有效值,但是该页面时没有数据时抛出

(2) Page对象c

  1. 创建对象

    Paginator对象的page()方法返回得到Page对象

    不需要手动创建

  2. 属性

    • object_list 当前页上所有的数据(对象)列表
    • number 当前页的页码值
    • paginator 当前page对象关联的paginator对象
  3. 方法

    • has_next() 判断是否有下一页,如果有返回True
    • has_previous() 判断是否有上一页,如果有返回True
    • has_other_pages() 判断是否有上一页或下一页,如果有返回True
    • next_page_number() 返回下一页的页码,如果下一页不存在抛出InvalidPage异常
    • previous_page_number() 返回上一页的页码,如果上一页不存在抛出InvalidPage异常
    • len() 返回当前页的数据(对象)个数

代码示例

路由配置
url(r'^stupage/(\d+)/$',views.stuPage,name='page'),
视图
allStu = Students.stuObj.all()
    pag = Paginator(allStu,6)
    # if int(nowPage)>=int(pag.num_pages):
    #     nowPage = pag.num_pages
    try:
        page = pag.page(nowPage)
    except:
        page = pag.page(pag.num_pages)
    # print(page)
    return render(request,'myApp/studentPage.html',{'students':page})
模板
<body>
    <ul>
        {% for stu in students %}
        <li>
            {{stu.sname}}-{{stu.sgrade}}
        </li>
        {% endfor %}
    </ul>
    <ul>
        {% for index in students.paginator.page_range %}
            {% if index == students.number %}
                <li>
                {{index}}
            </li>
            {% else %}
                <li>
                <a href="{% url 'myApp:page' index %}">{{index}}</a>
            </li>
            {% endif %}
        {% endfor %}
    </ul>
</body>

五、Ajax

需要动态生成,请求JSON数据
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="/static/myApp/js/jquery-3.1.1.min.js"></script>
</head>
<body>
    <h1>学生信息列表</h1>
    <button id="btn">显示学生信息</button>
    <script type="text/javascript" src="/static/myApp/js/sunck.js"></script>
</body>
</html>
Jquery代码
<script src="{% static 'js/jquery-1.8.3.min.js' %}"></script>
    <script>
        $(function () {
            $('button').click(function () {
            {% comment %}                
                $.ajax({
                    type:'get',
                    url:'{% url "myApp:stuinfo" %}',
                    dataType:'json',
                    success:function(status,data){
                        console.log(status)
                    }
                })
           {% endcomment %}
                $.get('{% url "myApp:stuinfo" %}',function(data,status){
                    str = ''
                    if(status == 'success'){
                        for(var i in data.data){
{#                            console.log(data.data[i][0])#}
                            str += '<li>'+data.data[i][0]+' '+data.data[i][1]+'</li>';
                        }
                        $('ul').append(str)
                    }
                })
            })
        })
    </script>
</head>
<body>
<ul>
</ul>
<button>点击</button>
</body>
视图
def ajaxstudents(request):
    return render(request, 'myApp/ajaxstudents.html')
from django.http import JsonResponse
def studentsinfo(request):
    s = request.is_ajax() #判断是否是ajax请求
    if not s:
        return HttpResponse('不是ajax请求')
    stus = Students.objects.all()
    list = []
    for stu in stus:
        list.append([stu.sname, stu.sage])
    return JsonResponse({"data":list})
#使用json.dumps写法 传到前台的json需要解析
import json
return HttpResponse(json.dumps({'data':l}))

#ajax获取的数据改成
data = JSON.parse(data).data#如果使用jsondump需要去解析
for(var i in a){
    console.log(a[i][0])
}

六、富文本

pip install django-tinymce

在站点中使用

(1) 配置settings.py文件

INSTALLED_APPS添加 'tinymce',

Settings.py添加

TINYMCE_DEFAULT_CONFIG = {
    'theme':'advanced',
    'width':600,
    'height':400, 
}

(2) 创建一个模型类

from tinymce.models import HTMLField
class Text(models.Model):
    str = HTMLField()

进行文件迁移

python manage.py makemigrations

python manage.py migrateas

(3) 配置站点

admin.py文件

from .models import Text
admin.site.register(Text)
python manage.py createsuperuser #创建站点用户

在自定义视图中使用

{% load static from staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>富文本</title>
    <script type="text/javascript" src="/static/tiny_mce/tiny_mce.js"></script>
    <script type="text/javascript">
        tinyMCE.init({
            'mode':'textareas',
            'theme':'advanced',
            'width':800,
            'height':600,
        })
    </script>
</head>
<body>
    <form action="/saveedit/" method="post">
        <textarea name="str">sunck is a good man</textarea>
        <input type="submit" value="提交"/>
    </form>
</body>
</html>

七、celerys

http://docs.jinkan.org/docs/celery/

问题:

用户发起request,并且要等待response返回。但是在视图中有一些耗时的操作,导致用户可能会等待很长时间才能接受response,这样用户体验很差

网站每隔一段时间要同步一次数据,但是http请求是需要触发的

(1) celery

  1. 任务task

    本质是一个python函数,将耗时操作封装成一个函数

  2. 队列queue

    将要执行的任务放队列里

  3. 工人worker

    负责执行队列中的任务

  4. 代理broker

    负责调度,在部署环境中使用redis

(2) 解决

celery来解决

  1. 将耗时的操作放到celery中执行
  2. 使用celery定时执行

(3) 安装

  • pip install celery
  • pip install celery-with-redis
  • pip install django-celery

(4) 配置settings.py

INSTALLED_APPS 添加

djcelery

在settings下方添加如下代码
import djcelery
djcelery.setup_loader()#初始化
BROKER_URL='redis://:密码@127.0.0.1:6379/0'#0带表16个库中使用第0个库
CELERY_IMPORTS=('myApp.task') #myApp是项目名

(5) 在应用目录下创建task.py文件 也就是myApp下

from celery import task
import time  

@task
def tt():
    print("xlg is a good man")
    time.sleep(5)
    print("xlg is a nice man")

(6) 迁移,生成celery需要的数据库表

python manage.py migrate

(7) 在工程目录下的project目录下创建celery.py的文件

from __future__ import absolute_import

import os
from celery import Celery
from django.conf import settings

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'whthas_home.settings')

app = Celery('portal')

app.config_from_object('django.conf:settings')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)


@app.task(bind=True)
def debug_task(self):
    print('Request: {0!r}'.format(self.request))

(8) 在工程目录下的project目录下的init.py文件中添加

from .celery import app as celery_app

(9) 视图

from .task import tt
def celery(request):
    tt.delay()#添加到celery中执行,不会阻塞
    return render(request, 'myApp/celery.html')

(10) 启动redis

C:\redis64-2.8.2101>redis-server.exe redis.windows.conf

C:\redis64-2.8.2101>Redis-cli.exe

c127.0.0.1:6379>auth '123456'

(11) 启动服务

python manange.py runserver 0.0.0:8000

(12) 启动worker

python manage.py celery worker --loglevel=info

推荐阅读更多精彩内容