Django进阶篇

阅读目录:

一、Django框架

 1.MVC框架

  MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。具有耦合性低、重用性高、生命周期成本低等优点。

 

 

2.Django的MTV框架

  Django框架的设计模式借鉴了MVC框架的思想,也是分成三部分,来降低各个部分之间的耦合性。不同之处在于它拆分的三部分为:Model(模型)、Template(模板)和View(视图),也就是MTV框架。

Model(模型):负责业务对象与数据库的对象(ORM)

       Template(模版):负责如何把页面展示给用户

       View(视图):负责业务逻辑,并在适当的时候调用Model和Template

此外,Django还有一个urls分发器,它的作用是将一个个URL的页面请求分发给不同的view处理,view再调用相应的Model和Template

 

二、Django模板(Template)系统

 1.常用用法

两种特殊符号:

(1)变量相关使用 {{ }}

(2)逻辑相关使用 {%  %}

2.变量

在Django的模板语言中按此语法使用:{{ 变量名 }}。

当模版引擎遇到一个变量,它将计算这个变量,然后用结果替换掉它本身。 变量的命名包括任何字母数字以及下划线 ("_")的组合。 变量名称中不能有空格或标点符号。

点(.)在模板语言中有特殊的含义。当模版系统遇到点("."),它将以这样的顺序查询:

字典查询(Dictionary lookup)
属性或方法查询(Attribute or method lookup)
数字索引查询(Numeric index lookup)

{# 取l中的第一个参数 #}
{{ l.0 }}
{# 取字典中key的值 #}
{{ d.name }}
{# 取对象的name属性 #}
{{ person_list.0.name }}
{# .操作只能调用不带参数的方法 #}
{{ person_list.0.dream }}

  

 1 def test(request):
 2     content1 = ['列表值1', '列表值2', {'id': '10024087', 'name': 'felix'}]
 3     content2 = {'id': '10024087', 'name': 'felix', 'age': 18, 'items': 123}
 4     content3 = [['列表值1', '列表值2', '10024087', 'felix'], ['列表值1', '列表值2', '10024087', 'felix']]
 5     p1 = Person('felix', 18)
 6     p2 = Person('new', 16)
 7     p_list = [p1, p2]
 8     import datetime
 9     now = datetime.datetime.now()
10     link = '''<a href="http://www.baidu.com">百度</a>'''
11     script = "<script>for (i=0;i<100;i++) {alert(123);}</script>"
12     p_text = '''
13 
14     如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾。
15     参数:截断的字符数
16     {{ value|truncatechars:9}}'''
17     if request.method == 'GET':
18         return render(
19             request,
20             'test.html',
21             {
22                 'data_str': '字符串值',
23                 'data_tuple': ('元组值1', '元组值2'),
24                 'data_list': content1,
25                 'data_dict': content2,
26                 'data': [
27                     {'id': '10024087', 'name': 'felix', 'age': 18},
28                     {'id': '10024088', 'name': 'alina', 'age': 19},
29                     {'id': '10024089', 'name': 'allen', 'age': 20}
30                 ],
31                 'person': p1,
32                 'p_list': p_list,
33                 'size': 102400000,
34                 'now': now,
35                 'link': link,
36                 'script': script,
37                 'p_text': p_text,
38                 'a': 100,
39                 'b': 10,
40                 'c': 1,
41                 'content3': content3
42 
43             })
44     if request.method == 'POST':  # 获取对象
45         file = request.FILES.get('file')
46         print(file.chunks)
47         img_dir = os.path.join(BASE_DIR, 'static', 'file', file.name)
48         print(img_dir)
49         with open(os.path.join(BASE_DIR, 'static', 'file', file.name), 'wb') as f:
50             for chunk in file.chunks():
51                 f.write(chunk)
52             # return HttpResponse('上传文件成功!')
53             # return render(request,'test.html',{'msg':'上传文件成功!'})
54             return render(
55                 request,
56                 'test.html',
57                 {
58                     'data_str': '字符串值',
59                     'data_tuple': ('元组值1', '元组值2'),
60                     'data_list': content1,
61                     'data_dict': content2,
62                     'data': [
63                         {'id': '10024087', 'name': 'felix', 'age': 18},
64                         {'id': '10024088', 'name': 'alina', 'age': 19},
65                         {'id': '10024089', 'name': 'allen', 'age': 20}
66                     ],
67                     'person': p1,
68                     'p_list': p_list,
69                     'size': 102400000,
70                     'now': now,
71                     'link': link,
72                     'script': script,
73                     'p_text': p_text,
74                     'msg': '上传文件成功!',
75                     'img_dir': r"..\static\file\{}".format(file.name)
76                 })
传递的数据

 

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>模板标记语言学习---重点</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
</head>
<body>

{#增加导航条组件#}
{% include 'nav.html' %}

<div class="container" style="margin-top: 100px">
    <div class="row">
        <h3>Django模板语言如下:</h3>
        取字符串:
        <p>{{ data_str }}</p>
        <hr>
        取元组数据:
        <p>{{ data_tuple.0 }}</p>
        <p>{{ data_tuple.1 }}</p>
        <hr>
        取列表数据:
        <p>{{ data_list }}</p>
        <p>{{ data_list.0 }}</p>
        <p>{{ data_list.1 }}</p>
        <p>{{ data_list.2.id }}</p>
        <p>{{ data_list.2.name }}</p>
        <hr>
        取整个字典数据:
        <p>{{ data_dict }}</p>
        取字典中key对应的值
        <p>{{ data_dict.id }}</p>
        <p>{{ data_dict.name }}</p>
        <p>{{ data_dict.age }}</p>
        <p>{{ data_dict.items }}</p>
        <hr>
        循环列表:
        {% for lst in data_list %}
            <ul type="circle">
                <li style="color: #19ff24"><h5>{{ lst }}</h5></li>
            </ul>
        {% endfor %}
        <hr>
        嵌套(列表里包含字典):
        {% for lst in data_list.2.values %}
            <ul type="circle">
                <li style="color: #ff3296"><h5>{{ lst }}</h5></li>
            </ul>
        {% endfor %}
        <hr>
        for循环 字典的key
        {% for item in data_dict %}
            <ul type="circle">
                <li style="color: blue"><h5>{{ item }}</h5></li>
            </ul>
        {% endfor %}

        for循环 字典的value
        {% for item in data_dict.values %}
            <ul type="square">
                <li style="color: red"><h5>{{ item }}</h5></li>
            </ul>
        {% endfor %}
        <hr>
        <div class="col-md-4">
            <table class="table table-bordered ">
                <thead>
                <tr>
                    <th>工号</th>
                    <th>姓名</th>
                    <th>年龄</th>
                    <th>操作</th>
                </tr>
                </thead>
                <tbody>
                {% for item in data %}
                    <tr>
                        <td>{{ item.id }}</td>
                        <td>{{ item.name }}</td>
                        <td>{{ item.age }}</td>
                        <td>
                            <a class="btn" href="/edit/?id={{ item.id }}">编辑</a>
                            <a class="btn" href="/del/?id={{ item.id }}">删除</a>
                        </td>
                    </tr>
                {% endfor %}
                </tbody>
            </table>
        </div>

        {% if data_dict.id == '10024087' %}
            <p>工号为:10024087的人员如下:</p>
            <p>姓名:{{ data_dict.name }}</p>
            <p>年龄:{{ data_dict.age }}</p>

        {% elif data_dict.id == '10024088' %}
            <p>工号为:10024088的人员如下:</p>
            <p>姓名:{{ data_dict.name }}</p>
            <p>年龄:{{ data_dict.age }}</p>
        {% endif %}
    </div>
    <hr>
    <div>
        <h4>类对象:</h4>
        {{ person }}
        {{ person.name }} | {{ person.age }} | {{ person.sing }}
    </div>
    <hr>
    {{ p_list.0.name }} | {{ p_list.0.age }} | {{ p_list.0.sing }},
    {{ p_list.1.name }} | {{ p_list.1.age }} | {{ p_list.1.sing }}。
    <hr>
    <p>默认值 default</p>
    {{ bucunzai | default:'这个值不存在,使用默认值!' }}
    <hr>
    <p>长度 length 只输出长度值</p>
    {{ data|length }}
    <hr>
    <p>文件大小:使用filesizeformat</p>
    {{ size|filesizeformat }}
    <hr>
    <h3>切片</h3>
    {{ data_list|slice:'1:-1' }}
    <hr>
    <h2>时间格式化</h2>
    <h4>{{ now|date:'Y-m-d H:i:s' }}</h4>
    <hr>
    <h2>safe</h2>
    {{ link|safe }}

    {#    {{ script|safe }}#}
    <hr>
    <h2>截取:truncatechars</h2>
    {{ p_text|truncatechars:50 }}
    <hr>
    <h2>自定义filter</h2>
    {% load my_filter %}
    {{ p_list.1.name }}
    {{ p_list.1.name|sb }}
    {{ p_list.1.name|str:'你是个SB' }}

    <hr>
    <div>
        <form action="/test/" method="post" enctype="multipart/form-data">
            <label for="">选择文件:</label>
            <input name="file" type="file">
            <button type="submit">上传</button>
        </form>
        <p style="color: blue">{{ msg }}</p>
    </div>
    <hr>
    {% if a > b > c %}
        <p>a>b>c</p>

    {% else %}
        <p>不成立</p>
    {% endif %}
    <hr>
    {% if a > b and b > c %}
        <p>a>b and b>c</p>
    {% else %}
        <p>不成立</p>
    {% endif %}
    <hr>
    <p>Django模板语言中,属性的优先级大于方法的优先级</p>
    {{ data_dict.items }}
    <hr>
    <hr>
    {% for i in content3 %}
        <hr>
        外层循环:
        {{ forloop.counter }}
        {{ i }}
        {% for j in i %}
            <hr>
            内层循环:
            {{ forloop.counter }}
            <hr>
            {{ j }} @父循环:
            {{ forloop.parentloop.counter }}
        {% endfor %}

    {% endfor %}

    <div>
        <img src="{{ img_dir }}" alt="" height="600px" width="100%">
    </div>

    <hr>

    {#取普通变量#}
    {{ name }}
    {{ age }}

    取字典里的key值
    {{ data_dict.keys.0 }}
</div>


<script src="/static/jquery-3.3.1.min.js"></script>
<script src="/static/bootstrap/js/bootstrap.min.js"></script>
</body>
</html>
模板语言

 

 3.Filters(过滤)

(1)常用内置过滤

add 将参数增加到改值
{{ value|add:"2" }}
  
capfirst 将英文字符串第一个字母大写
{{ value|capfirst }}

center  将内容居中
{{ value|center:"15" }}

cut 移除指定的字符
{{ value|cut:" " }}

date 格式化输出时间
{{ value|date:"Y-m-d H:M:S" }}
{{ value|date:"Y年m月d日" }}

格式化字符    描述    示例输出
a    'a.m.'或'p.m.'(请注意,这与PHP的输出略有不同,因为这包括符合Associated Press风格的期间)    'a.m.'
A    'AM'或'PM'。    'AM'
b    月,文字,3个字母,小写。    'jan'
B    未实现。
c    ISO 8601格式。 (注意:与其他格式化程序不同,例如“Z”,“O”或“r”,如果值为naive datetime,则“c”格式化程序不会添加时区偏移量(请参阅datetime.tzinfo) 。    2008-01-02T10:30:00.000123+02:00或2008-01-02T10:30:00.000123如果datetime是天真的
d    月的日子,带前导零的2位数字。    '01'到'31'
D    一周中的文字,3个字母。    “星期五”
e    时区名称 可能是任何格式,或者可能返回一个空字符串,具体取决于datetime。    ''、'GMT'、'-500'、'US/Eastern'等
E    月份,特定地区的替代表示通常用于长日期表示。    'listopada'(对于波兰语区域,而不是'Listopad')
f    时间,在12小时的小时和分钟内,如果它们为零,则分钟停留。 专有扩展。    '1','1:30'
F    月,文,长。    '一月'
g    小时,12小时格式,无前导零。    '1'到'12'
G    小时,24小时格式,无前导零。    '0'到'23'
h    小时,12小时格式。    '01'到'12'
H    小时,24小时格式。    '00'到'23'
i    分钟。    '00'到'59'
I    夏令时间,无论是否生效。    '1'或'0'
j    没有前导零的月份的日子。    '1'到'31'
l    星期几,文字长。    '星期五'
L    布尔值是否是一个闰年。    True或False
m    月,2位数字带前导零。    '01'到'12'
M    月,文字,3个字母。    “扬”
n    月无前导零。    '1'到'12'
N    美联社风格的月份缩写。 专有扩展。    'Jan.','Feb.','March','May'
o    ISO-8601周编号,对应于使用闰年的ISO-8601周数(W)。 对于更常见的年份格式,请参见Y。    '1999年'
O    与格林威治时间的差异在几小时内。    '+0200'
P    时间为12小时,分钟和'a.m。'/'p.m。',如果为零,分钟停留,特殊情况下的字符串“午夜”和“中午”。 专有扩展。    '1 am','1:30 pm' / t3>,'midnight','noon','12:30 pm' / T10>
r    RFC 5322格式化日期。    'Thu, 21 Dec 2000 16:01:07 +0200'
s    秒,带前导零的2位数字。    '00'到'59'
S    一个月的英文序数后缀,2个字符。    'st','nd','rd'或'th'
t    给定月份的天数。    28 to 31
T    本机的时区。    'EST','MDT'
u    微秒。    000000 to 999999
U    自Unix Epoch以来的二分之一(1970年1月1日00:00:00 UTC)。
w    星期几,数字无前导零。    '0'(星期日)至'6'(星期六)
W    ISO-8601周数,周数从星期一开始。    1,53
y    年份,2位数字。    '99'
Y    年,4位数。    '1999年'
z    一年中的日子    0到365
Z    时区偏移量,单位为秒。 UTC以西时区的偏移量总是为负数,对于UTC以东时,它们总是为正。    -43200到43200

default 如果一个变量是false或者为空,使用给定的默认值。 否则,使用变量的值。
{{ value|default:"nothing"}}  如果value没有传值或者值为空的话就显示nothing

default_if_none 只有值为None时才输出指定的默认值
{{ value|default_if_none:"nothing" }}

dictsort 获取字典列表并返回按参数中给出的键排序的列表
{{ value|dictsort:"name" }}
如果value是:

[
    {'name': 'zed', 'age': 19},
    {'name': 'amy', 'age': 22},
    {'name': 'joe', 'age': 31},
]
然后输出将是:

[
    {'name': 'amy', 'age': 22},
    {'name': 'joe', 'age': 31},
    {'name': 'zed', 'age': 19},
]

divisibleby 如果值能被参数整除,则返回True
{{ value|divisibleby:"3" }}

filesizeformat 格式化输出文件大小
{{ value|filesizeformat }}  如果value是123456789,则输出为117.7 MB

first 输出列表中的第一个值
{{ value|first }}

floatformat 浮点数格式化输出,
不指定参数情况下,则将浮点数四舍五入后保留1位小数
value    Template    Output
34.23234    {{ value|floatformat }}    34.2
34.00000    {{ value|floatformat }}    34
34.26000    {{ value|floatformat }}    34.3

指定小数部分:
value    Template    Output
34.23234    {{ value|floatformat:3 }}    34.232
34.00000    {{ value|floatformat:3 }}    34.000
34.26000    {{ value|floatformat:3 }}    34.260

参数为"0"时:它将浮点数四舍五入到最近的整数(个位数)。
value    Template    Output
34.23234    {{ value|floatformat:"0" }}    34
34.00000    {{ value|floatformat:"0" }}    34
39.56000    {{ value|floatformat:"0" }}    40

参数为负数时,四舍五入后保留指定的小数位,但是只显示数字部分
value    Template    Output
34.23234    {{ value|floatformat:"-3" }}    34.232
34.00000    {{ value|floatformat:"-3" }}    34
34.26000    {{ value|floatformat:"-3" }}    34.260

join 使用字符串连接列表,例如Python的str.join(list)
{{ value|join:" // " }}
如果value是列表,则输出将是字符串 。['a', 'b', 'c']"a // b // c"

last 返回列表中的最后一项
{{ value|last }}


length  返回值的长度
{{ value|length }}

length_is 如果值的长度是后面的参数,结果输出True,否则输出False

{{ value|length_is:"4" }}

ljust¶
左对齐给定宽度的字段中的值。
{{ value|ljust:"10" }}
如果value是Django,输出将是。"Django    "
  
lower 将字符串转换为全部小写。
{{ value|lower }}

random 返回给定列表中的随机项。
{{ value|random }}如果value是列表,则输出可以是。['a', 'b', 'c', 'd']"b"

safe
Django的模板中会对HTML标签和JS等语法标签进行自动转义,原因显而易见,这样是为了安全。
但是有的时候我们可能不希望这些HTML元素被转义,比如我们做一个内容管理系统,后台添加的文章中是经过修饰的,
这些修饰可能是通过一个类似于FCKeditor编辑加注了HTML修饰符的文本,如果自动转义的话显示的就是保护HTML标签
的源文件。为了在Django中关闭HTML的自动转义有两种方式,如果是一个单独的变量我们可以通过过滤器“|safe”的
方式告诉Django这段代码是安全的不必转义。

value = "<a href='#'>点我</a>"
{{ value|safe}}

slice 切片
{{value|slice:"2:-1"}}
{{ some_list|slice:":2" }}  如果some_list是,输出将是。['a', 'b', 'c']['a', 'b']

striptags 尽一切努力去除所有[X] HTML标记。
{{ value|striptags }}
如果value是,输出将是。"<b>Joel</b> <button>is</button> a <span>slug</span>""Joel is a slug"

time 根据给定的格式格式化时间。
{{ value|time:"H:i" }}

timesince
将日期格式设为自该日期起的时间(例如,“4天,6小时”)。

采用一个可选参数,它是一个包含用作比较点的日期的变量(不带参数,比较点为现在)。
例如,如果blog_date是表示2006年6月1日午夜的日期实例,并且comment_date是2006年6月1日08:00的日期实例,
则以下将返回“8小时”:
{{ blog_date|timesince:comment_date }}
分钟是所使用的最小单位,对于相对于比较点的未来的任何日期,将返回“0分钟”。

timeuntil
似于timesince,除了它测量从现在开始直到给定日期或日期时间的时间。 例如,如果今天是2006年6月1日,
而conference_date是保留2006年6月29日的日期实例,则{{ conference_date | timeuntil }}将返回“4周”。
使用可选参数,它是一个包含用作比较点的日期(而不是现在)的变量。 如果from_date包含2006年6月22日,
则以下内容将返回“1周”:
{{ conference_date|timeuntil:from_date }}

title 英文字符串的每个单词首字母大写
{{ value|title }}

truncatechars 如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾。
{{ value|truncatechars:9}}

truncatewords 在一定数量的字后截断字符串
{{ value|truncatewords:2 }}

upper 将字符串转换为全部大写。
{{ value|upper }}

urlize 将文本中的URL和电子邮件地址转换为可单击的链接。
{{ value|urlize }}

urlizetrunc 将URL和电子邮件地址转换为可点击的链接,就像urlize一样,但会截断超过给定字符限制的URL。
{{ value|urlizetrunc:15 }}

wordcount 返回单词数量
{{ value|wordcount }}
常用内置过滤器

 

(2)自定义过滤

自定义过滤器只是带有一个或两个参数的Python函数:

  • 变量(输入)的值 - -不一定是一个字符串
  • 参数的值 - 这可以有一个默认值,或完全省略

例如,在过滤器{{var | foo:'bar'}}中,过滤器foo将传递变量var和参数“bar”

自定义filter代码文件摆放位置:

app01/
    __init__.py
    models.py
    templatetags/  # 在app01下面新建一个package package
        __init__.py
        app01_filters.py  # 建一个存放自定义filter的文件
    views.py

  

编写自定义filter

from django import template
register = template.Library()
 
 
@register.filter(name="cut")
def cut(value, arg):
    return value.replace(arg, "")
 
 
@register.filter(name="addSB")
def add_sb(value):
    return "{} SB".format(value)

  

使用自定义filter

{# 先导入我们自定义filter那个文件 #}
{% load app01_filters %}
 
{# 使用我们自定义的filter #}
{{ somevariable|cut:"0" }}
{{ d.name|addSB }}

  

1.定义
在app下面新建python包:templatetags
2.在新建的包中,新建一个python文件
3.在上诉python文件中:
    from django import template

    生成一个注册的实例
    register = template.Library()

    定义并注册一个自定义filter函数

    @register.filter(name="add_str")
    def add_str(arg):
        return "{} hello".format(arg)

4.调用阶段:(
    1.在django模板文件中(HTML文件中),导入刚才新建的Python文件
    {% load py文件名 %}

    2.按照filter的语法调用
        {{ name | add_str }}
自定义过滤器

 

4.逻辑相关

(1)for循环

普通for循环

<ul>
{% for user in user_list %}
    <li>{{ user.name }}</li>
    <li>{{ user.age}}</li>
{% endfor %}
</ul>

 

for循环可以用的参数:

VariableDescription
forloop.counter 当前循环的索引值(从1开始)
forloop.counter0 当前循环的索引值(从0开始)
forloop.revcounter 当前循环的倒序索引值(从1开始)
forloop.revcounter0 当前循环的倒序索引值(从0开始)
forloop.first 当前循环是不是第一次循环(布尔值)
forloop.last 当前循环是不是最后一次循环(布尔值)
forloop.parentloop 本层循环的外层循环

 

 

 

 

 

 

 

 for...empty

<ul>
{% for user in user_list %}
    <li>{{ user.name }}</li>
{% empty %}
    <li>空空如也</li>
{% endfor %}
</ul>

 

(2)if条件判断

{% if condition1 %}
    ...
{% elif condition2 %}
    ...
{% else %}
    ...
{% endif %}

  

<td>
    {% if each.st == 0 %}
        {#                                            <p>暂停</p>#}
        <i class="fa fa-pause fa-2x" style="color: #ffea51"></i>
    {% elif each.st == 1 %}
        {#                                            <p>运行</p>#}
        <i class="fa fa-cog fa-spin fa-2x" style="color: green"></i>
    {% elif each.st == -1 %}
        {#                                            <p>离线</p>#}
        <i class="fa fa-power-off fa-2x" style="color: black"></i>
    {% endif %}
</td>

if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断。

{% if athlete_list and coach_list %}
    Both athletes and coaches are available.
{% endif %}

{% if not athlete_list %}
    There are no athletes.
{% endif %}

{% if athlete_list or coach_list %}
    There are some athletes or some coaches.
{% endif %}

{% if not athlete_list or coach_list %}
    There are no athletes or there are some coaches.
{% endif %}

{% if athlete_list and not coach_list %}
    There are some athletes and absolutely no coaches.
{% endif %}

{% if athlete_list and coach_list or cheerleader_list %}
{% endif %}

{% if somevar == "x" %}
  This appears if variable somevar equals the string "x"
{% endif %}

{% if somevar != "x" %}
  This appears if variable somevar does not equal the string "x",
  or if somevar is not found in the context
{% endif %}

{% if somevar < 100 %}
  This appears if variable somevar is less than 100.
{% endif %}

{% if somevar > 0 %}
  This appears if variable somevar is greater than 0.
{% endif %}

{% if somevar <= 100 %}
  This appears if variable somevar is less than 100 or equal to 100.
{% endif %}

{% if somevar >= 1 %}
  This appears if variable somevar is greater than 1 or equal to 1.
{% endif %}

{% if "bc" in "abcdef" %}
  This appears since "bc" is a substring of "abcdef"
{% endif %}

{% if "hello" in greetings %}
  If greetings is a list or set, one element of which is the string
  "hello", this will appear.
{% endif %}

{% if user in users %}
  If users is a QuerySet, this will appear if user is an
  instance that belongs to the QuerySet.
{% endif %}

{% if somevar is True %}
  This appears if and only if somevar is True.
{% endif %}

{% if somevar is None %}
  This appears if somevar is None, or if somevar is not found in the context.
{% endif %}

{% if somevar is not True %}
  This appears if somevar is not True, or if somevar is not found in the
  context.
{% endif %}

{% if somevar is not None %}
  This appears if and only if somevar is not None.
{% endif %}

  

(3)with

  定义一个中间变量,多用于给一个复杂的变量起别名。

注意等号左右不要加空格。

{% with total=business.employees.count %}
    {{ total }} employee{{ total|pluralize }}
{% endwith %}

{% with business.employees.count as total %}
    {{ total }} employee{{ total|pluralize }}
{% endwith %}

 

5.母版和继承母版

   我们在编写多个HTML内容时,为了避免编写重复的内容和更方便修改部分内容时,通常提取相同的内容作为母版。

如下内容:文件名定义为 base.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="x-ua-compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Title</title>
  {% block page-css %}
  
  {% endblock %}
</head>
<body>

<h1>这是母板的标题</h1>

{% block page-main %}

{% endblock %}
<h1>母板底部内容</h1>
{% block page-js %}

{% endblock %}
</body>
</html>

  
注意:我们通常会在母板中定义页面专用的CSS块和JS块,方便子页面替换。

 

继承母版:在子页面中在页面最上方使用下面的语法来继承母板。

{% extends 'layouts.html' %}

  

具体使用的步骤
1.把共用的HTML部分提取出来,放到base.html文件中
2.在base.html中,通过定义block,把每个页面不同的部分分出来
3.在具体的页面中,先继承母版
4.然后block 名 去指定替换母版相应的位置

注意事项:
1.{% extends 'base.html' %}     母版文件,base.html要加引号
2.{% extends 'base.html' %}     必须放在子页面的第一行
3.可以在base.html中定义很多block ,通常和我们会额外定义page_css和page_js两个块
4.view.py 相应的函数中返回的是对应的子页面文件,不是 base.html

  

6.块(block)

 通过在母板中使用{% block  xxx %}来定义"块"。

 在子页面中通过定义母板中的block名来对应替换母板中相应的内容。

{% block page-main %}
  <p>继承母版后,新的内容部分</p> 
{% endblock %}

  

7.组件

可以将常用的页面内容如导航条,页尾信息等组件保存在单独的文件中,然后在需要使用的地方按如下语法导入即可。

{% include 'navbar.html' %}

 

 8.静态文件相关

加载settings.py文件中的static别名
{% load static %}
{% load static %}
<img src="{% static "images/hi.jpg" %}" alt="Hi!" />
自动在别名下去查找对应的文件
<link href="{% static 'bootstrap/css/bootstrap.min.css' %}" rel="stylesheet">
<link href="{% static 'Dashboard_files/dashboard.css' %}" rel="stylesheet">
<script src="{% static 'jquery-3.3.1.min.js' %}"></script>
<script src="{% static 'bootstrap/js/bootstrap.js' %}"></script>

某个文件多处被用到可以存为一个变量
{% load static %}
{% static "images/hi.jpg" as myphoto %}
<img src="{{ myphoto }}"></img>

获取静态文件路径
{% get_static_prefix %}
使用:
    {% load static %}
    <link href="{% get_static_prefix %}Dashboard_files/dashboard.css" rel="stylesheet">

{% get_static_prefix %}
{% load static %}
<img src="{% get_static_prefix %}images/hi.jpg" alt="Hi!" />

或者
{% load static %}
{% get_static_prefix as STATIC_PREFIX %}

<img src="{{ STATIC_PREFIX }}images/hi.jpg" alt="Hi!" />
<img src="{{ STATIC_PREFIX }}images/hi2.jpg" alt="Hello!" />

  

9.simple_tag

和自定义filter类似,只不过接收更灵活的参数。

定义注册simple tag

@register.simple_tag(name="plus")
def plus(a, b, c):
    return "{} + {} + {}".format(a, b, c)

使用自定义simple tag

{% load app01_demo %}

{# simple tag #}
{% plus "1" "2" "abc" %}

 

10.inclusion_tag

多用于返回html代码片段

示例:

templatetags/my_inclusion.py

from django import template
 
register = template.Library()
 
@register.inclusion_tag('result.html')
def show_results(n):
    n = 1 if n < 1 else int(n)
    data = ["第{}项".format(i) for i in range(1, n+1)]
    return {"data": data}

  

templates/snippets/result.html

<ul>
  {% for choice in data %}
    <li>{{ choice }}</li>
  {% endfor %}
</ul>

  

templates/index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="x-ua-compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>inclusion_tag test</title>
</head>
<body>

{% load inclusion_tag_test %}

{% show_results 10 %}
</body>
</html>

  

三、Django视图(View)系统

 1.概念

一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求并且返回Web响应。

响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片。

无论视图本身包含什么逻辑,都要返回响应。代码写在哪里也无所谓,只要它在你当前项目目录下面。除此之外没有更多的要求了——可以说“没有什么神奇的地方”。为了将代码放在某处,大家约定成俗将视图放置在项目(project)或应用程序(app)目录中的名为views.py的文件中。

from django.http import HttpResponse
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)

  

让我们来逐行解释下上面的代码:

  • 首先,我们从 django.http模块导入了HttpResponse类,以及Python的datetime库。

  • 接着,我们定义了current_datetime函数。它就是视图函数。每个视图函数都使用HttpRequest对象作为第一个参数,并且通常称之为request

    注意,视图函数的名称并不重要;不需要用一个统一的命名方式来命名,以便让Django识别它。我们将其命名为current_datetime,是因为这个名称能够比较准确地反映出它实现的功能。

  • 这个视图会返回一个HttpResponse对象,其中包含生成的响应。每个视图函数都负责返回一个HttpResponse对象。

Django使用请求和响应对象来通过系统传递状态。

当浏览器向服务端请求一个页面时,Django创建一个HttpRequest对象,该对象包含关于请求的元数据。然后,Django加载相应的视图,将这个HttpRequest对象作为第一个参数传递给视图函数。

每个视图负责返回一个HttpResponse对象。

 

2.FBV和CBV

基于函数的view,就叫FBV

def pub_list(request):
    ipaddr = request.META['REMOTE_ADDR']
    all_data = main()
    return render(request, 'pub_list.html', {'all_data': all_data, 'ipaddr': ipaddr})

  

基于类的view,就叫CBV,且先导入from django.views import View类,再继承

class Upload(View):
    def get(self, request):
        return render(request, 'test2.html')

    def post(self, request):
        file_name = request.FILES.get('upload_file')
        print(file_name.size)
        with open(os.path.join(BASE_DIR, 'static', 'file', file_name.name), 'wb') as f:
            for chunk in file_name.chunks():
                f.write(chunk)
        return HttpResponse('上传成功!')

  

基于类的视图,还需要在urls.py文件中,修改对应关系;view.类名.as_view()

url(r'^test2/', views.Upload.as_view()),

  

3.Request对象和Response对象

官网

(1)Request对象

当一个页面被请求时,Django就会创建一个包含本次请求原信息的HttpRequest对象。Django会将这个对象自动传递给响应的视图函数,一般视图函数约定俗成地使用 request 参数承接这个对象。

1.HttpRequest.body 请求体,byte类型 request.POST的数据就是从body里面提取到的

2.HttpRequest.path 表示所请求页面的完整路径的字符串,不包括域名 例: "/music/bands/the_beatles/"

3.HttpRequest.path_info 返回用户访问url,不包括域名

4.HttpRequest.method  请求中使用的HTTP方法的字符串表示,全大写表示
    if request.method == 'GET':
        do_something()
    elif request.method == 'POST':
        do_something_else()

5.HttpRequest.encoding   一个字符串,表示提交的数据的编码方式(如果为 None 则表示使用 DEFAULT_CHARSET 的设置,默认为 'utf-8')。
   这个属性是可写的,你可以修改它来修改访问表单数据使用的编码。
   接下来对属性的任何访问(例如从 GET 或 POST 中读取数据)将使用新的 encoding 值。
   如果你知道表单数据的编码不是 DEFAULT_CHARSET ,则使用它。

6.HttpRequest.content_type  表示请求的MIME类型的字符串,从CONTENT_TYPE标头解析 。


7.HttpRequest.content_params    CONTENT_TYPE 标题中包含的键/值参数字典。

8.HttpRequest.GET   一个类似于字典的对象,包含 HTTP GET 的所有参数。详情请参考 QueryDict 对象。

9.HttpRequest.POST  一个类似于字典的对象,如果请求中包含表单数据,则将这些数据封装成 QueryDict 对象。
    POST 请求可以带有空的 POST 字典 —— 如果通过 HTTP POST 方法发送一个表单,但是表单中没有任何的数据,QueryDict 对象依然会被创建。
    因此,不应该使用 if request.POST  来检查使用的是否是POST 方法;应该使用 if request.method == "POST"

  另外:如果使用 POST 上传文件的话,文件信息将包含在 FILES 属性中。

10.HttpRequest.COOKIES  包含所有cookie的字典。键和值是字符串。

11.HttpRequest.FILES      一个类似于字典的对象,包含所有的上传文件信息。FILES 中的每个键为<input type="file" name="" /> 中的name,值则为对应的数据。
  注意,FILES 只有在请求的方法为POST 且提交的<form> 带有enctype="multipart/form-data" 的情况下才会包含数据。否则,FILES 将为一个空的类似于字典的对象。

12.HttpRequest.META 一个标准的Python 字典,包含所有的HTTP 首部。具体的头部信息取决于客户端和服务器,下面是一些示例:
    CONTENT_LENGTH —— 请求的正文的长度(是一个字符串)。
    CONTENT_TYPE —— 请求的正文的MIME 类型。
    HTTP_ACCEPT —— 响应可接收的Content-Type。
    HTTP_ACCEPT_ENCODING —— 响应可接收的编码。
    HTTP_ACCEPT_LANGUAGE —— 响应可接收的语言。
    HTTP_HOST —— 客服端发送的HTTP Host 头部。
    HTTP_REFERER —— Referring 页面。
    HTTP_USER_AGENT —— 客户端的user-agent 字符串。
    QUERY_STRING —— 单个字符串形式的查询字符串(未解析过的形式)。
    REMOTE_ADDR —— 客户端的IP 地址。
    REMOTE_HOST —— 客户端的主机名。
    REMOTE_USER —— 服务器认证后的用户。
    REQUEST_METHOD —— 一个字符串,例如"GET""POST"。
    SERVER_NAME —— 服务器的主机名。
    SERVER_PORT —— 服务器的端口(是一个字符串)。
   从上面可以看到,除 CONTENT_LENGTH 和 CONTENT_TYPE 之外,请求中的任何 HTTP 首部转换为 META 的键时,
    都会将所有字母大写并将连接符替换为下划线最后加上 HTTP_  前缀。
    所以,一个叫做 X-Bender 的头部将转换成 META 中的 HTTP_X_BENDER 键。

13.HttpRequest.resolver_match
ResolverMatch表示已解析URL 的实例。此属性仅在URL解析发生后设置,这意味着它在所有视图中都可用,但在URL解析发生之前执行的中间件中不可用(process_view()尽管可以使用它 )。
HttpRequest对象属性

 

1.HttpRequest.current_app  The url template tag will use its value as the current_app argument to reverse().

2.HttpRequest.urlconf   This will be used as the root URLconf for the current request, overriding the ROOT_URLCONF setting. See How Django processes a request for details.
urlconf can be set to None to revert any changes made by previous middleware and return to using the ROOT_URLCONF.
通过应用程序设置的属性

 

1.HttpRequest.session 来自SessionMiddleware:一个既可读又可写的类似于字典的对象,表示当前的会话。只有当Django 启用会话的支持时才可用。完整的细节参见会话的文档。

2.HttpRequest.site  来自CurrentSiteMiddleware: 表示当前站点返回的实例Site或 RequestSite返回 的实例get_current_site()。

3.HttpRequest.user  来自AuthenticationMiddleware:AUTH_USER_MODEL表示当前登录用户的实例。如果用户当前未登录,user则将其设置为实例AnonymousUser。您可以将它们区分开来 is_authenticated,如下所示:

    if request.user.is_authenticated:
        ... # Do something for logged-in users.
    else:
        ... # Do something for anonymous users.

****************************************************************************************************************************************************************
    匿名用户
    class models.AnonymousUser

    django.contrib.auth.models.AnonymousUser 类实现了django.contrib.auth.models.User 接口,但具有下面几个不同点:

    id 永远为None。
    username 永远为空字符串。
    get_username() 永远返回空字符串。
    is_staff 和 is_superuser 永远为False。
    is_active 永远为 False。
    groups 和 user_permissions 永远为空。
    is_anonymous() 返回True 而不是False。
    is_authenticated() 返回False 而不是True。
    set_password()、check_password()、save() 和delete() 引发 NotImplementedError。
    New in Django 1.8:
    新增 AnonymousUser.get_username() 以更好地模拟 django.contrib.auth.models.User。
通过中间件设置的属性

 

1.HttpRequest.get_host()

  根据从HTTP_X_FORWARDED_HOST(如果打开 USE_X_FORWARDED_HOST,默认为False)和 HTTP_HOST 头部信息返回请求的原始主机。
   如果这两个头部没有提供相应的值,则使用SERVER_NAME 和SERVER_PORT,在PEP 3333 中有详细描述。

  USE_X_FORWARDED_HOST:一个布尔值,用于指定是否优先使用 X-Forwarded-Host 首部,仅在代理设置了该首部的情况下,才可以被使用。

  例如:"127.0.0.1:8000"

  注意:当主机位于多个代理后面时,get_host() 方法将会失败。除非使用中间件重写代理的首部。

 

2.HttpRequest.get_full_path()

  返回 path,如果可以将加上查询字符串。

  例如:"/music/bands/the_beatles/?print=true"

 

3.HttpRequest.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)

  返回签名过的Cookie 对应的值,如果签名不再合法则返回django.core.signing.BadSignature。

  如果提供 default 参数,将不会引发异常并返回 default 的值。

  可选参数salt 可以用来对安全密钥强力攻击提供额外的保护。max_age 参数用于检查Cookie 对应的时间戳以确保Cookie 的时间不会超过max_age 秒。

        复制代码
        >>> request.get_signed_cookie('name')
        'Tony'
        >>> request.get_signed_cookie('name', salt='name-salt')
        'Tony' # 假设在设置cookie的时候使用的是相同的salt
        >>> request.get_signed_cookie('non-existing-cookie')
        ...
        KeyError: 'non-existing-cookie'    # 没有相应的键时触发异常
        >>> request.get_signed_cookie('non-existing-cookie', False)
        False
        >>> request.get_signed_cookie('cookie-that-was-tampered-with')
        ...
        BadSignature: ...    
        >>> request.get_signed_cookie('name', max_age=60)
        ...
        SignatureExpired: Signature age 1677.3839159 > 60 seconds
        >>> request.get_signed_cookie('name', False, max_age=60)
        False
        复制代码
         


4.HttpRequest.is_secure()

  如果请求时是安全的,则返回True;即请求通是过 HTTPS 发起的。

 

5.HttpRequest.is_ajax()

  如果请求是通过XMLHttpRequest 发起的,则返回True,方法是检查 HTTP_X_REQUESTED_WITH 相应的首部是否是字符串'XMLHttpRequest'。

  大部分现代的 JavaScript 库都会发送这个头部。如果你编写自己的 XMLHttpRequest 调用(在浏览器端),你必须手工设置这个值来让 is_ajax() 可以工作。

  如果一个响应需要根据请求是否是通过AJAX 发起的,并且你正在使用某种形式的缓存例如Django 的 cache middleware, 
   你应该使用 vary_on_headers('HTTP_X_REQUESTED_WITH') 装饰你的视图以让响应能够正确地缓存。
方法

 

注意:键值对的值是多个的时候,比如checkbox类型的input标签,select标签,需要用:

request.POST.getlist("hobby")

 

#FBV
def upload(request):
    """
    保存上传文件前,数据需要存放在某个位置。默认当上传文件小于2.5M时,django会将上传文件的全部内容读进内存。从内存读取一次,写磁盘一次。
    但当上传文件很大时,django会把上传文件写到临时文件中,然后存放到系统临时文件夹中。
    :param request: 
    :return: 
    """
    if request.method == "POST":
        # 从请求的FILES中获取上传文件的文件名,file为页面上type=files类型input的name属性值
        filename = request.FILES["file"].name
        # 在项目目录下新建一个文件
        with open(filename, "wb") as f:
            # 从上传的文件对象中一点一点读
            for chunk in request.FILES["file"].chunks():
                # 写入本地文件
                f.write(chunk)
        return HttpResponse("上传OK")

#CBV 
class Upload(View):
    def get(self, request):
        return render(request, 'test2.html')

    def post(self, request):
        print(request.FILES)
        # print(request.FILES['upload_file'])
        # print(request.FILES['upload_file'].name)
        print(request.FILES.get('upload_file'))
        file_name = request.FILES.get('upload_file')
        print(file_name.size)

        with open(os.path.join(BASE_DIR, 'static', 'file', file_name.name), 'wb') as f:
            for chunk in file_name.chunks():
                f.write(chunk)
        return HttpResponse('上传成功!')
上传文件示例
<form class="form-inline" action="/test2/" method="post" enctype="multipart/form-data">
    <input type="file" name="upload_file">
    <button class="btn btn-primary" type="submit">上传文件</button>
</form>
上传文件之HTML form表单

 

 (2)Response对象

  与由Django自动创建的HttpRequest对象相比,HttpResponse对象是我们的职责范围了。我们写的每个视图都需要实例化,填充和返回一个HttpResponse。

HttpResponse类位于django.http模块中。

作用:

典型用法是将页面内容作为字符串传递给 HttpResponse构造函数:
>>> from django.http import HttpResponse
>>> response = HttpResponse("Here's the text of the Web page.")
>>> response = HttpResponse("Text only, please.", content_type="text/plain")

但是,如果要以增量方式添加内容,可以将其response用作类文件对象:
>>> response = HttpResponse()
>>> response.write("<p>Here's the text of the Web page.</p>")
>>> response.write("<p>Here's another paragraph.</p>")

设置或删除响应头信息
>>> response = HttpResponse()
>>> response['Age'] = 120
>>> del response['Age']

请注意,与字典不同,如果标题字段不存在,del则不会引发KeyError。

要告诉浏览器将响应视为文件附件,请使用 content_type参数并设置Content-Disposition标头。例如,这是您返回Microsoft Excel电子表格的方式:
>>> response = HttpResponse(my_data, content_type='application/vnd.ms-excel')
>>> response['Content-Disposition'] = 'attachment; filename="foo.xls"'

  

1.HttpResponse.content  表示内容的字节字符串,必要时从Unicode对象编码。

2.HttpResponse.charset  一个字符串,表示将在其中编码响应的字符集。如果未在HttpResponse实例化时间给出,则将从中提取 content_type,如果不成功,DEFAULT_CHARSET则将使用该 设置。

3.HttpResponse.status_code  响应的 HTTP状态代码。除非reason_phrase明确设置,否则修改 status_code构造函数外部的值也会修改值 reason_phrase。

4.HttpResponse.reason_phrase    响应的HTTP原因短语。它使用了HTTP标准的默认原因短语。除非明确设置,否则reason_phrase由值确定 status_code。

5.HttpResponse.streaming    这总是返回False。此属性存在,表示中间件不同于常规响应处理流式响应。

6.HttpResponse.closed   如果回复关闭返回True。
属性

 

4.JsonResponse对象

  JsonResponse是HttpResponse的子类,专门用来生成JSON编码的响应。

from django.http import JsonResponse

response = JsonResponse({'foo': 'bar'})
print(response.content)

b'{"foo": "bar"}'
默认只能传递字典类型,如果要传递非字典类型需要设置一下safe关键字参数。

response = JsonResponse([1, 2, 3], safe=False)

def json_test(request):
    data1 = {'name': 'felix', 'age': 18}
    data2 = [11, 22, 33, 44, 55]
    # import json
    # data_str = json.dumps(data1)
    # return HttpResponse(data_str)
    # JsonResponse只支持序列化字典,不支持列表,返回列表需要增加safe=False参数
    from django.http import JsonResponse
    return JsonResponse(data2, safe=False)

  

5.Django shortcut functions

(1)render()

结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的 HttpResponse 对象。

参数:
     request: 用于生成响应的请求对象。

     template_name:要使用的模板的完整名称,可选的参数

     context:添加到模板上下文的一个字典。默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。

     content_type:生成的文档要使用的MIME类型。默认为 DEFAULT_CONTENT_TYPE 设置的值。默认为'text/html'

     status:响应的状态码。默认为200。

   useing: 用于加载模板的模板引擎的名称。

from django.shortcuts import render

def my_view(request):
    # View code here...
    return render(request, 'myapp/index.html', {
        'foo': 'bar',
    }, content_type='application/xhtml+xml')
这个例子相当于:

from django.http import HttpResponse
from django.template import loader

def my_view(request):
    # View code here...
    t = loader.get_template('myapp/index.html')
    c = {'foo': 'bar'}
    return HttpResponse(t.render(c, request), content_type='application/xhtml+xml')

  

 (2)redirect() 重定向

参数可以是:

  • 一个模型:将调用模型的get_absolute_url() 函数
  • 一个视图,可以带有参数:将使用urlresolvers.reverse 来反向解析名称
  • 一个绝对的或相对的URL,将原封不动的作为重定向的位置。

默认返回一个临时的重定向;传递permanent=True 可以返回一个永久的重定向。

 

传递一个具体的ORM对象
将调用具体ORM对象的get_absolute_url() 方法来获取重定向的URL:

from django.shortcuts import redirect
 
def my_view(request):
    ...
    object = MyModel.objects.get(...)
    return redirect(object)

传递一个视图的名称
def my_view(request):
    ...
    return redirect('some-view-name', foo='bar')

传递要重定向到的一个具体的网址
def my_view(request):
    ...
    return redirect('/some/url/')

当然也可以是一个完整的网址
def my_view(request):
    ...
    return redirect('http://example.com/')

默认情况下,redirect() 返回一个临时重定向。以上所有的形式都接收一个permanent 参数;如果设置为True,将返回一个永久的重定向:

def my_view(request):
    ...
    object = MyModel.objects.get(...)
    return redirect(object, permanent=True)  

  

四、Django路由系统

1.URLconf配置
  URL配置(URLconf)就像Django 所支撑网站的目录。它的本质是URL与要为该URL调用的视图函数之间的映射表。

 

基本格式:

from django.conf.urls import url

urlpatterns = [
     url(正则表达式, views视图函数,参数,别名),
]

  

参数格式:

  • 正则表达式:一个正则表达式字符串
  • views视图函数:一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串
  • 参数:可选的要传递给视图函数的默认参数(字典形式)
  • 别名:一个可选的name参数

 

from django.urls import path

urlpatterns = [
    path('articles/2003/', views.special_case_2003),
    path('articles/<int:year>/', views.year_archive),
    path('articles/<int:year>/<int:month>/', views.month_archive),
    path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail),
]
Django 2.0版本格式

 

 

2.正则表达式详解---分组匹配

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/([0-9]{4})/$', views.year_archive),
    url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
    url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
]

  

注意事项:

  • urlpatterns中的元素按照书写顺序从上往下逐一匹配正则表达式,一旦匹配成功则不再继续。
  • 若要从URL中捕获一个值,只需要在它周围放置一对圆括号(分组匹配)。
  • 不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles。
  • 每个正则表达式前面的'r' 是可选的但是建议加上。

补充说明:

# 是否开启URL访问地址后面不为/跳转至带有/的路径的配置项
APPEND_SLASH=True

 Django settings.py配置文件中默认没有 APPEND_SLASH 这个参数,但 Django 默认这个参数为 APPEND_SLASH = True。 其作用就是自动在网址结尾加'/'。

 

分组匹配  --->   相当于给视图函数传递位置参数

分组命名匹配 --->  相当于给视图函数传递关键字参数

 

3.分组命名匹配

(1)分组命名匹配示例

上面的示例使用简单的正则表达式分组匹配(通过圆括号)来捕获URL中的值并以位置参数形式传递给视图。

在更高级的用法中,可以使用分组命名匹配的正则表达式组来捕获URL中的值并以关键字参数形式传递给视图。

在Python的正则表达式中,分组命名正则表达式组的语法是(?P<name>pattern),其中name是组的名称,pattern是要匹配的模式。

 

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),

  

这个实现与前面的示例完全相同,只有一个细微的差别:捕获的值作为关键字参数而不是位置参数传递给视图函数。

例如,针对url /articles/2017/12/相当于按以下方式调用视图函数:

views.month_archive(request, year="2017", month="12")

 

(2)分组命名匹配的位置

  URLconf 在请求的URL 上查找,将它当做一个普通的Python 字符串。不包括GET和POST参数以及域名。

例如,http://www.example.com/myapp/ 请求中,URLconf 将查找myapp/。

在http://www.example.com/myapp/?page=3 请求中,URLconf 仍将查找myapp/

URLconf 不检查请求的方法。换句话讲,所有的请求方法 —— 同一个URL的POSTGETHEAD等等 —— 都将路由到相同的函数。

 

(3)捕获的参数永远都是字符串

每个在URLconf中捕获的参数都作为一个普通的Python字符串传递给视图,无论正则表达式使用的是什么匹配方式。例如,下面这行URLconf 中:

url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),

传递到视图函数views.year_archive() 中的year 参数永远是一个字符串类型。

 

(4)视图函数中指定默认值

# urls.py中
from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^blog/$', views.page),
    url(r'^blog/page(?P<num>[0-9]+)/$', views.page),
]

# views.py中,可以为num指定默认值
def page(request, num="1"):
    pass

在上面的例子中,两个URL模式指向相同的view - views.page - 但是第一个模式并没有从URL中捕获任何东西。

如果第一个模式匹配上了,page()函数将使用其默认参数num=“1”,如果第二个模式匹配,page()将使用正则表达式捕获到的num值。

 

(5)include其他的URLconfs

a.主项目和子项目的urls分别在不同的urls.py文件中,主项目include子项目中的urls

from django.conf.urls import include, url

urlpatterns = [
   url(r'^admin/', admin.site.urls),
   url(r'^blog/', include('blog.urls')),  # 可以包含其他的URLconfs文件
]

  

 

请注意,此示例中的正则表达式没有$ (字符串结尾匹配字符),但包含尾部斜杠。每当Django遇到include()django.conf.urls.include())时,

它都会删除与该点匹配的URL的任何部分,并将剩余的字符串发送到包含的URLconf以进行进一步处理。

 

b.主项目和子项目URLS分组写,主项目include子项目中的URLS

 另一种可能性是通过使用url()实例列表来包含其他URL模式 例如,考虑这个URLconf:

from django.conf.urls import include, url

from apps.main import views as main_views
from credit import views as credit_views

extra_patterns = [
    url(r'^reports/$', credit_views.report),
    url(r'^reports/(?P<id>[0-9]+)/$', credit_views.report),
    url(r'^charge/$', credit_views.charge),
]

urlpatterns = [
    url(r'^$', main_views.homepage),
    url(r'^help/', include('apps.help.urls')),
    url(r'^credit/', include(extra_patterns)),
]

在此示例中,/credit/reports/URL将由credit_views.report()Django视图处理 

 

c.删除冗余url

这可用于从重复使用单个模式前缀的URLconf中删除冗余。例如,考虑这个URLconf:

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

urlpatterns = [
    url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/history/$', views.history),
    url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/edit/$', views.edit),
    url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/discuss/$', views.discuss),
    url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/permissions/$', views.permissions),
]

  

我们可以通过仅指定公共路径前缀一次并对不同的后缀进行分组来改进这一点:

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

urlpatterns = [
    url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/', include([
        url(r'^history/$', views.history),
        url(r'^edit/$', views.edit),
        url(r'^discuss/$', views.discuss),
        url(r'^permissions/$', views.permissions),
    ])),
]

  

 

五、Django模型(model)系统

1.ORM介绍

  对象-关系映射(Object/Relation Mapping,简称ORM),是随着面向对象的软件开发方法发展而产生的。面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,
关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系数据是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。
内存中的对象之间存在关联和继承关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系。因此,对象-关系映射(ORM)系统一般以中间件的形式存在,
主要实现程序对象到关系数据库数据的映射。简单的说:ORM相当于中继数据,操作数据、

2.Django中的ORM

Django项目中使用MySQL数据库

 (1)在Django项目的settings.py文件中,配置数据库连接信息:

DATABASES = {
    'default': {
        # 连接的数据库类型
        'ENGINE': 'django.db.backends.mysql',
        # 数据库的地址
        'HOST': '127.0.0.1',
        # 数据库使用的端口
        'PORT': '3306',
        # 所连接的数据库名字
        'NAME': 'book',
        # 连接数据库的用户名
        'USER': 'root',
        # 连接数据库的密码
        'PASSWORD': ''
    }
}

  

(2)在Django项目的__init__.py文件中写如下代码,告诉Django使用pymysql模块连接MySQL数据库:

import pymysql

pymysql.install_as_MySQLdb()

  

 Model

在Django中model是你数据的单一、明确的信息来源。它包含了你存储的数据的重要字段和行为。通常,一个模型(model)映射到一个数据库表,

基本情况:

  • 每个模型都是一个Python类,它是django.db.models.Model的子类。
  • 模型的每个属性都代表一个数据库字段。
  • 综上所述,Django为您提供了一个自动生成的数据库访问API,详询官方文档链接

 

 

下面这个例子定义了一个 Person 模型,包含 first_name 和 last_name。

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
first_name 和 last_name 是模型的字段。每个字段被指定为一个类属性,每个属性映射到一个数据库列。

上面的 Person 模型将会像这样创建一个数据库表:

CREATE TABLE myapp_person (
    "id" serial NOT NULL PRIMARY KEY,
    "first_name" varchar(30) NOT NULL,
    "last_name" varchar(30) NOT NULL
);
一些说明:

表myapp_person的名称是自动生成的,如果你要自定义表名,需要在model的Meta类中指定 db_table 参数,强烈建议使用小写表名,特别是使用MySQL作为后端数据库时。
id字段是自动添加的,如果你想要指定自定义主键,只需在其中一个字段中指定 primary_key=True 即可。如果Django发现你已经明确地设置了Field.primary_key,它将不会添加自动ID列。
本示例中的CREATE TABLE SQL使用PostgreSQL语法进行格式化,但值得注意的是,Django会根据配置文件中指定的数据库后端类型来生成相应的SQL语句。
Django支持MySQL5.5及更高版本。
示例

 

在app里面的models.py文件中要先导入模块:

from django.db import models 

class Userinfo(models.Model):
    id = models.AutoField(primary_key=True)  # 创建一个自增的主键字段 id
    name = models.CharField(null=False, max_length=32)  # 创建一个varchar类型的不能为空的字段 name

 

然后在Pycharm的Terminal里面执行以下命令,创建数据表和字段。

python manage.py makemigrations

python manage.py makemigrate

  

 

 注意:若在models.py文件中对数据表(类)有任何的改动,都需要执行以上两条命令,Django会把相应的操作翻译为SQL语句并执行到MySQL数据库中,而且也会保存相应的记录到app01/migrations下的文件中。

 

 3.常用字段(官网字段介绍)

AutoField
自增 int自增列,必须填入参数 primary_key=True。当model中如果没有自增列,则自动会创建一个列名为id的列。

IntegerField
一个整数类型,范围在 -2147483648 to 2147483647

CharField
varchar 字符类型,必须提供max_length参数, max_length表示字符长度。

DateField
日期字段,日期格式  YYYY-MM-DD,相当于Python中的datetime.date()实例。

DateTimeField
日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例。

    DatetimeField、DateField、TimeField这个三个时间字段,都可以设置如下属性。
    
    auto_now_add
    配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
    
    auto_now
    配置上auto_now=True,每次更新数据记录的时候会更新该字段。

  

   AutoField(Field)
        - int自增列,必须填入参数 primary_key=True

    BigAutoField(AutoField)
        - bigint自增列,必须填入参数 primary_key=True

        注:当model中如果没有自增列,则自动会创建一个列名为id的列
        from django.db import models

        class UserInfo(models.Model):
            # 自动创建一个列名为id的且为自增的整数列
            username = models.CharField(max_length=32)

        class Group(models.Model):
            # 自定义自增列
            nid = models.AutoField(primary_key=True)
            name = models.CharField(max_length=32)

    SmallIntegerField(IntegerField):
        - 小整数 -32768 ~ 32767

    PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正小整数 0 ~ 32767
    IntegerField(Field)
        - 整数列(有符号的) -2147483648 ~ 2147483647

    PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正整数 0 ~ 2147483647

    BigIntegerField(IntegerField):
        - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807

    BooleanField(Field)
        - 布尔值类型

    NullBooleanField(Field):
        - 可以为空的布尔值

    CharField(Field)
        - 字符类型
        - 必须提供max_length参数, max_length表示字符长度

    TextField(Field)
        - 文本类型

    EmailField(CharField):
        - 字符串类型,Django Admin以及ModelForm中提供验证机制

    IPAddressField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制

    GenericIPAddressField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
        - 参数:
            protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
            unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both"

    URLField(CharField)
        - 字符串类型,Django Admin以及ModelForm中提供验证 URL

    SlugField(CharField)
        - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)

    CommaSeparatedIntegerField(CharField)
        - 字符串类型,格式必须为逗号分割的数字

    UUIDField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证

    FilePathField(Field)
        - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
        - 参数:
                path,                      文件夹路径
                match=None,                正则匹配
                recursive=False,           递归下面的文件夹
                allow_files=True,          允许文件
                allow_folders=False,       允许文件夹

    FileField(Field)
        - 字符串,路径保存在数据库,文件上传到指定目录
        - 参数:
            upload_to = ""      上传文件的保存路径
            storage = None      存储组件,默认django.core.files.storage.FileSystemStorage

    ImageField(FileField)
        - 字符串,路径保存在数据库,文件上传到指定目录
        - 参数:
            upload_to = ""      上传文件的保存路径
            storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
            width_field=None,   上传图片的高度保存的数据库字段名(字符串)
            height_field=None   上传图片的宽度保存的数据库字段名(字符串)

    DateTimeField(DateField)
        - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]

    DateField(DateTimeCheckMixin, Field)
        - 日期格式      YYYY-MM-DD

    TimeField(DateTimeCheckMixin, Field)
        - 时间格式      HH:MM[:ss[.uuuuuu]]

    DurationField(Field)
        - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型

    FloatField(Field)
        - 浮点型

    DecimalField(Field)
        - 10进制小数
        - 参数:
            max_digits,小数总长度
            decimal_places,小数位长度

    BinaryField(Field)
        - 二进制类型
字段集合

 

 

4.自定义字段

class UnsignedIntegerField(models.IntegerField):
    def db_type(self, connection):
        return 'integer UNSIGNED'

自定义char类型字段:

复制代码
class FixedCharField(models.Field):
    """
    自定义的char类型的字段类
    """
    def __init__(self, max_length, *args, **kwargs):
        super().__init__(max_length=max_length, *args, **kwargs)
        self.length = max_length

    def db_type(self, connection):
        """
        限定生成数据库表的字段类型为char,长度为length指定的值
        """
        return 'char(%s)' % self.length


class Class(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=25)
    # 使用上面自定义的char类型的字段
    cname = FixedCharField(max_length=25)
复制代码

创建的表结构:

 

对应关系:
    'AutoField': 'integer AUTO_INCREMENT',
    'BigAutoField': 'bigint AUTO_INCREMENT',
    'BinaryField': 'longblob',
    'BooleanField': 'bool',
    'CharField': 'varchar(%(max_length)s)',
    'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
    'DateField': 'date',
    'DateTimeField': 'datetime',
    'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
    'DurationField': 'bigint',
    'FileField': 'varchar(%(max_length)s)',
    'FilePathField': 'varchar(%(max_length)s)',
    'FloatField': 'double precision',
    'IntegerField': 'integer',
    'BigIntegerField': 'bigint',
    'IPAddressField': 'char(15)',
    'GenericIPAddressField': 'char(39)',
    'NullBooleanField': 'bool',
    'OneToOneField': 'integer',
    'PositiveIntegerField': 'integer UNSIGNED',
    'PositiveSmallIntegerField': 'smallint UNSIGNED',
    'SlugField': 'varchar(%(max_length)s)',
    'SmallIntegerField': 'smallint',
    'TextField': 'longtext',
    'TimeField': 'time',
    'UUIDField': 'char(32)',
ORM字段与数据库实际字段的对应关系

 

 

5.字段参数

null
用于表示某个字段可以为空。

unique
如果设置为unique=True 则该字段在此表中必须是唯一的 。

db_index
如果db_index=True 则代表着为此字段设置数据库索引。

default
为该字段设置默认值。

DatetimeField、DateField、TimeField这个三个时间字段,都可以设置如下属性。

auto_now_add
配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。

auto_now
配置上auto_now=True,每次更新数据记录的时候会更新该字段。

  

6.关系字段

(1)ForeignKey

  外键类型在ORM中用来表示外键关联关系,一般把ForeignKey字段设置在 '一对多'中'多'的一方。ForeignKey可以和其他表做关联关系同时也可以和自身做关联关系。

 

字段参数:

1.to
设置要关联的表

2.to_field
设置要关联的表的字段

3.related_name
反向操作时,使用的字段名,用于代替原反向查询时的'表名_set'。

例如:
class Classes(models.Model):
    name = models.CharField(max_length=32)

class Student(models.Model):
    name = models.CharField(max_length=32)
    theclass = models.ForeignKey(to="Classes")
当我们要查询某个班级关联的所有学生(反向查询)时,我们会这么写:

models.Classes.objects.first().student_set.all()
当我们在ForeignKey字段中添加了参数 related_name 后,

class Student(models.Model):
    name = models.CharField(max_length=32)
    theclass = models.ForeignKey(to="Classes", related_name="students")
当我们要查询某个班级关联的所有学生(反向查询)时,我们会这么写:

models.Classes.objects.first().students.all()

4.related_query_name
反向查询操作时,使用的连接前缀,用于替换表名。

5.on_delete
当删除关联表中的数据时,当前表与其关联的行的行为。

models.CASCADE
删除关联数据,与之关联也删除


models.DO_NOTHING
删除关联数据,引发错误IntegrityError


models.PROTECT
删除关联数据,引发错误ProtectedError


models.SET_NULL
删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)


models.SET_DEFAULT
删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)


models.SET

删除关联数据,
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)

def func():
    return 10

class MyModel(models.Model):
    user = models.ForeignKey(
        to="User",
        to_field="id",
        on_delete=models.SET(func)
    )

6.db_constraint
是否在数据库中创建外键约束,默认为True。

  

(2)OneToOneField

   一对一字段。通常一对一字段用来扩展已有字段。一对一的关联关系多用在当一张表的不同字段查询频次差距过大的情况下,将本可以存储在一张表的字段拆开放置在两张表中,然后将两张表建立一对一的关联关系。

class Author(models.Model):
    name = models.CharField(max_length=32)
    info = models.OneToOneField(to='AuthorInfo')
    

class AuthorInfo(models.Model):
    phone = models.CharField(max_length=11)
    email = models.EmailField()

  

字段参数:

to
设置要关联的表。

to_field
设置要关联的字段。

on_delete
同ForeignKey字段。

  

(3)ManyToManyField

  用于表示多对多的关联关系。在数据库中通过第三张表来建立关联关系。

字段参数:
1.to
设置要关联的表

2.related_name
同ForeignKey字段。

3.related_query_name
同ForeignKey字段。

4.symmetrical
仅用于多对多自关联时,指定内部是否创建反向操作的字段。默认为True。
    
class Person(models.Model):
    name = models.CharField(max_length=16)
    friends = models.ManyToManyField("self")
此时,person对象就没有person_set属性。

class Person(models.Model):
    name = models.CharField(max_length=16)
    friends = models.ManyToManyField("self", symmetrical=False)
此时,person对象现在就可以使用person_set属性进行反向查询。

5.through
在使用ManyToManyField字段时,Django将自动生成一张表来管理多对多的关联关系。

但我们也可以手动创建第三张表来管理多对多关系,此时就需要通过through来指定第三张表的表名。

6.through_fields
设置关联的字段。

7.db_table
默认创建第三张表时,数据库中表的名称。

  

(4)元信息

ORM对应的类里面包含另一个Meta类,而Meta类封装了一些数据库的信息。主要字段如下:

db_table
ORM在数据库中的表名默认是 app_类名,可以通过db_table可以重写表名。

index_together
联合索引。

unique_together
联合唯一索引。

ordering
指定默认按什么字段排序。

只有设置了该属性,我们查询到的结果才可以被reverse()。

  

  

 

posted @ 2019-02-01 13:03  %华&仔%  阅读(131)  评论(0编辑  收藏