web框架django第二天

###URL配置 settings.py
* ROOT_URLCONF = 'mysite.urls'


###url转换(静态路由)
* from django.urls import path, re_path
* re_path(r'^add_book/$', views.add_book)
    * 浏览器访问 http://127.0.0.1:8000/add_book/
* re_path(r'^book/add/$', views.add_book)
    * 浏览器访问 http://127.0.0.1:8000/book/add/
    
* r'原生字符串' 
    * 不需要转义
* 建议都写上$符号  
    * 防止url匹配问题
* ^以什么开头,不需要添加/,系统会自动重定向
    * settings.py 配置文件
        * APPEND_SLASH = True  # 默认
        
  
###动态路由(正则表达式)
* re_path(r'^blog/d{4}/$', views.blog)
    * 浏览器访问 http://127.0.0.1:8000/blog/1234/
        
      
###分组匹配
* re_path(r'^blog/(d{4})/$', views.blog)
* views.py 按照位置参数传递给函数
```python
def blog(request, year):
    print(year, type(year))
    return HttpResponse('ok')
```


###分组命名匹配
* urls.py
```python
re_path(r'^blog/(?P<year>d{4})/$', views.blog)
```

* views.py 按照关键字参数传递给函数
```python
def blog(request, year):
    print(year, type(year))
    return HttpResponse('ok')
```


###视图函数中指定默认值
* urls.py
```python
re_path(r'^blog/$', views.blog)
re_path(r'^blog/page(?P<num>d+)/$', views.blog)
```

* views.py
```python
def blog(request, num='1'):
    return HttpResponse('ok')
```


###include其他的URLconfs
* urls.py
```python
from django.urls import path, re_path, include
re_path(r'^app01/', include('app01.urls')),
```

* app01/urls.py
```python
re_path(r'^blog/$', views.blog)
```

* 浏览器访问 http://127.0.0.1:8000/app01/blog/


###使用最新版本的django2.2.3报错(使用HttpResponse时,添加encode('utf8'))
* 报错信息
```python
Traceback (most recent call last):
  File "G:python3.6libsocketserver.py", line 654, in process_request_thread
    self.finish_request(request, client_address)
  File "G:python3.6libsocketserver.py", line 364, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "G:python3.6libsocketserver.py", line 724, in __init__
    self.handle()
  File "G:python3.6libsite-packagesdjangocoreserversasehttp.py", line 171, in handle
    self.handle_one_request()
  File "G:python3.6libsite-packagesdjangocoreserversasehttp.py", line 194, in handle_one_request
    handler.run(self.server.get_app())
  File "G:python3.6libwsgirefhandlers.py", line 144, in run
    self.close()
  File "G:python3.6libsite-packagesdjangocoreserversasehttp.py", line 111, in close
    super().close()
  File "G:python3.6libwsgirefsimple_server.py", line 35, in close
    self.status.split(' ',1)[0], self.bytes_sent
AttributeError: 'NoneType' object has no attribute 'split'
```
    * return HttpResponse('ok'.encode('utf8'))  # 就不会报错了


###传递额外的参数给视图函数
* urls.py
```python
re_path(r'^blog/(?P<year>d{4})/$', views.blog, {'month': '12'})
```

* views.py
```python
def blog(request, *args, **kwargs):
    print(args)  # ()
    print(kwargs)  # {'year': '1234', 'month': '12'}
    return HttpResponse('ok'.encode('utf8')) 
```

* 浏览器访问 http://127.0.0.1:8000/blog/1234/
* 如果位置参数或关键字参数和后面额外传递的字典里的参数同名,则会被后面的字典里的参数替换


###URL命名和URL反向解析
* urls.py
```python
re_path(r'^book/add/$', views.add_book, name='add_book')
```

* 模板文件
```html
<a href="{% url 'add_book' %}">添加书名</a>
```

* views.py视图函数
```python
from django.shortcuts import render, redirect, HttpResponse, reverse
return redirect(reverse('app01:add_book'))
```


###URL命名和URL反向解析(分组)
* urls.py
```python
re_path(r'^blog/(d{4})/$', views.blog, name='blog')
```

* 模板文件
```html
<a href="{% url 'blog' 2014 %}">添加书名</a>
```

* views.py视图函数
```python
from django.shortcuts import render, redirect, HttpResponse, reverse
return redirect(reverse('blog', args=('2019',)))
```


###URL命名和URL反向解析(分组命名)
* urls.py
```python
re_path(r'^blog/(?P<year>d{4})/$', views.blog, name='blog')
```

* 模板文件
```html
<a href="{% url 'blog' year=2016 %}">添加书名</a>
```

* views.py视图函数
```python
from django.shortcuts import render, redirect, HttpResponse, reverse
return redirect(reverse('blog', kwargs={'year': '2019'}))
```


###命名空间模式
* urls.py
```python
re_path(r'^app01/', include('app01.urls', namespace='app01')),
re_path(r'^app02/', include('app02.urls', namespace='app02')),
```

* 报错
```python
Watching for file changes with StatReloader
Exception in thread django-main-thread:
Traceback (most recent call last):
  File "G:python3.6lib	hreading.py", line 916, in _bootstrap_inner
    self.run()
  File "G:python3.6lib	hreading.py", line 864, in run
    self._target(*self._args, **self._kwargs)
  File "G:python3.6libsite-packagesdjangoutilsautoreload.py", line 54, in wrapper
    fn(*args, **kwargs)
  File "G:python3.6libsite-packagesdjangocoremanagementcommands
unserver.py", line 117, in inner_run
    self.check(display_num_errors=True)
  File "G:python3.6libsite-packagesdjangocoremanagementase.py", line 390, in check
    include_deployment_checks=include_deployment_checks,
  File "G:python3.6libsite-packagesdjangocoremanagementase.py", line 377, in _run_checks
    return checks.run_checks(**kwargs)
  File "G:python3.6libsite-packagesdjangocorechecks
egistry.py", line 72, in run_checks
    new_errors = check(app_configs=app_configs)
  File "G:python3.6libsite-packagesdjangocorechecksurls.py", line 40, in check_url_namespaces_unique
    all_namespaces = _load_all_namespaces(resolver)
  File "G:python3.6libsite-packagesdjangocorechecksurls.py", line 57, in _load_all_namespaces
    url_patterns = getattr(resolver, 'url_patterns', [])
  File "G:python3.6libsite-packagesdjangoutilsfunctional.py", line 80, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "G:python3.6libsite-packagesdjangourls
esolvers.py", line 579, in url_patterns
    patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
  File "G:python3.6libsite-packagesdjangoutilsfunctional.py", line 80, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "G:python3.6libsite-packagesdjangourls
esolvers.py", line 572, in urlconf_module
    return import_module(self.urlconf_name)
  File "G:python3.6libimportlib\__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "G:2019老男孩周末26期day15课下练习mysite_day15mysite_day15urls.py", line 22, in <module>
    re_path(r'^app01/', include('app01.urls', namespace='app01')),
  File "G:python3.6libsite-packagesdjangourlsconf.py", line 39, in include
    'Specifying a namespace in include() without providing an app_name '
django.core.exceptions.ImproperlyConfigured: Specifying a namespace in include() without providing an app_name is not supported. Set the app_name attribute in the included module, or pass a 2-tuple containing the list of patterns and app_name instead.
```

* 解决方法 urls.py
```python
from django.urls import path, re_path, include
re_path(r'^app01/', include(('app01.urls', 'app01'), namespace='app01')),
re_path(r'^app02/', include(('app02.urls', 'app02'), namespace='app02')),
```

* app01/urls.py
```python
from django.urls import path, re_path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'^home/$', views.home, name='home'),
]
```

* app02/urls.py
```python
from django.urls import path, re_path
from app02 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'^home/$', views.home, name='home'),
]
```

* app01/views.py
```python
def home(request):
    return render(request, 'app01home.html')
```

* app02/views.py
```python
def home(request):
    return render(request, 'app02home.html')
```

* app01home.html
```html
<a href="{% url 'app01:home' %}">app01家目录</a>
<a href="{% url 'app02:home' %}">app02家目录</a>
```

-----------------------------------------------------------------------------------
###返回当前日期和时间的视图
```python
import datetime

def current_datetime(request):
    current_time = datetime.datetime.now()
    return HttpResponse('<html><body>current time: %s</body></html>' % current_time)
```


###FBV基于函数的视图(Function Base View)
* mysite/urls.py 开始路由文件
```python
from django.urls import path, re_path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'^app02/', include(('app02.urls', 'app02'), namespace='app02')),
]
```

* app02/urls.py 命名空间app02路由文件
```python
from app02 import viewsre_path(r'^class/add/$', views.add_class, name='add_class'),
```

* app02/views.py 视图文件
```python
from django.shortcuts import render, HttpResponse, redirect, reverse

def add_class(request):
    if request.method == "POST":
        # 接收请求参数,添加到数据库
        return redirect(reverse('app01:home'))
    return render(request, 'add_class.html')
```


###CBV基于类的视图(Class Base View)
* mysite/urls.py 开始路由文件
```python
from django.urls import path, re_path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'^app01/', include(('app01.urls', 'app01'), namespace='app01')),
]
```

* app01/urls.py 命名空间app01路由文件
```python
urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'^class/add/$', views.AddClasses.as_view(), name='add_class'),
]
```

* app01/views.py 视图文件
```python
from django.shortcuts import render, redirect, HttpResponse, reverse
from django.views import View

class AddClasses(View):
    def get(self, request):
        return render(request, 'add_class.html')

    def post(self, request):
        # 接收请求参数,添加到数据库
        return redirect(reverse('app01:home'))
```


###给视图加装饰器
* 使用装饰器装饰FBV app02/views.py
```python
from django.shortcuts import render, HttpResponse, redirect, reverse
import time

def wrapper(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        # 执行被装饰函数前增加的逻辑
        ret = func(*args, **kwargs)
        # 执行被装饰函数后增加的逻辑
        print('used: %s' % (time.time() - start_time))
        return ret
    return inner
    

@wrapper
def add_class(request):
    if request.method == "POST":
        # 接收请求参数,添加到数据库
        return redirect(reverse('app02:home'))
    return render(request, 'add_class.html')
```

* 使用装饰器装饰CBV app01/views.py 方法一(分别加在每个请求方法上)
```python
from django.shortcuts import render, redirect, HttpResponse, reverse
from django.utils.decorators import method_decorator
from django.views import View
import time


def wrapper(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        # 执行被装饰函数前增加的逻辑
        ret = func(*args, **kwargs)
        # 执行被装饰函数后增加的逻辑
        print('used: %s' % (time.time() - start_time))
        return ret
    return inner
    

class AddClasses(View):
    @method_decorator(wrapper)
    def get(self, request):
        return render(request, 'add_class.html')

    @method_decorator(wrapper)
    def post(self, request):
        # 接收请求参数,添加到数据库
        return redirect(reverse('app01:home'))
```

* 使用装饰器装饰CBV app01/views.py 方法二(加在dispatch方法上)
```python
from django.shortcuts import render, redirect, HttpResponse, reverse
from django.utils.decorators import method_decorator
from django.views import View
import time


def wrapper(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        # 执行被装饰函数前增加的逻辑
        ret = func(*args, **kwargs)
        # 执行被装饰函数后增加的逻辑
        print('used: %s' % (time.time() - start_time))
        return ret
    return inner
    

class AddClasses(View):
    @method_decorator(wrapper)
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    def get(self, request):
        return render(request, 'add_class.html')

    def post(self, request):
        # 接收请求参数,添加到数据库
        return redirect(reverse('app01:home'))
```


* 使用装饰器装饰CBV app01/views.py 方法三(加在类上[请求方法])
```python
from django.shortcuts import render, redirect, HttpResponse, reverse
from django.utils.decorators import method_decorator
from django.views import View
import time


def wrapper(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        # 执行被装饰函数前增加的逻辑
        ret = func(*args, **kwargs)
        # 执行被装饰函数后增加的逻辑
        print('used: %s' % (time.time() - start_time))
        return ret
    return inner
    

@method_decorator(wrapper, name='post')
@method_decorator(wrapper, name='get')
class AddClasses(View):
    def get(self, request):
        return render(request, 'add_class.html')

    def post(self, request):
        # 接收请求参数,添加到数据库
        return redirect(reverse('app01:home'))
```

* 使用装饰器装饰CBV app01/views.py 方法二(加在dispatch方法上)
```python
from django.shortcuts import render, redirect, HttpResponse, reverse
from django.utils.decorators import method_decorator
from django.views import View
import time


def wrapper(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        # 执行被装饰函数前增加的逻辑
        ret = func(*args, **kwargs)
        # 执行被装饰函数后增加的逻辑
        print('used: %s' % (time.time() - start_time))
        return ret
    return inner
    

class AddClasses(View):
    @method_decorator(wrapper)
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    def get(self, request):
        return render(request, 'add_class.html')

    def post(self, request):
        # 接收请求参数,添加到数据库
        return redirect(reverse('app01:home'))
```


* 使用装饰器装饰CBV app01/views.py 方法四(加在类上[dispatch方法])
```python
from django.shortcuts import render, redirect, HttpResponse, reverse
from django.utils.decorators import method_decorator
from django.views import View
import time


def wrapper(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        # 执行被装饰函数前增加的逻辑
        ret = func(*args, **kwargs)
        # 执行被装饰函数后增加的逻辑
        print('used: %s' % (time.time() - start_time))
        return ret
    return inner
    

@method_decorator(wrapper, name='dispatch')
class AddClasses(View):
    def get(self, request):
        return render(request, 'add_class.html')

    def post(self, request):
        # 接收请求参数,添加到数据库
        return redirect(reverse('app01:home'))
```


###框架
* MVC框架(Model模型 View视图 Controller控制器)
    * 耦合性低,重用性高,生命周期成本低等优点,重视结果
 
* MTV框架(Model模型 Template模板 View视图)
    * 重视过程

* ORM对象关系映射(Object Relational Mapping)


###request对象
* app02/views.py
```python
from django.shortcuts import render, HttpResponse, redirect, reverse
import datetime

def current_datetime(request):
    print("path_info:", request.path_info)  # /app02/now/ 返回用户访问url,不包括域名
    print("method:", request.method)  # GET
    print("POST:", request.POST)  # <QueryDict: {}>
    print("GET:", request.GET)     # <QueryDict: {}>
    print("body:", request.body)  # b''
    print("scheme:", request.scheme)  # http或https
    print("path:", request.path)  # 同path_info
    print("session:", request.session)
    print("COOKIES:", request.COOKIES)
    print("FILES:", request.FILES)  # 文件
    print("META:", request.META)  # 请求头
    print("get_host:", request.get_host())
    print("get_full_path:", request.get_full_path())
    print("is_ajax:", request.is_ajax())
    current_time = datetime.datetime.now()
    return HttpResponse('<html><body>current time: %s</body></html>' % current_time)
```


###JsonResponse对象
```python
from django.http import JsonResponse
def func(request):
    dit = {"name": "lily"}
    return JsonResponse(dit)  # Content-Type: application/json
```

* 对比json序列化
```python
import json

def func(request):
    dit = {"name": "lily"}
    return HttpResponse(json.dumps(dit))  # Content-Type: text/html; charset=utf-8
```

* 通过修改响应值,返回json对象
```python
import json

def func(request):
    dit = {"name": "lily"}
    res = HttpResponse(json.dumps(dit))
    res['Content-Type'] = 'application/json'
    return res  # Content-Type: application/json
```

* 指定响应内容类型
```python
import json

def func(request):
    dit = {"name": "lily"}
    res = HttpResponse(json.dumps(dit), content_type='application/json')
    return res  # Content-Type: application/json
```

* In order to allow non-dict objects to be serialized set the safe parameter to False.
    * 为了允许非字典对象序列号设置safe参数为False
```python
from django.http import JsonResponse

def func(request):   
    return JsonResponse(['a', 8, 'z'], safe=False)  # Content-Type: application/json
```

---------------------------------------------------------------------------

###模板常用语法
* {{ 变量 }} 
    * .索引
    * .key
    * .属性
    * .方法
    * views.py
    ```python
    def template_test(request):
        lst = ['abc', '123', 'xyz']
        dit = {'name': 'lily', 'age': 18}
    
        class Person(object):
            def __init__(self, name, age):
                self.name = name
                self.age = age
    
            def eating(self):
                return '%s is eating.' % self.name
    
        Zhangsan = Person('zhangsan', 20)
        Lisi = Person('lisi', 21)
        WangWu = Person('wangwu', 22)
        person_lst = [Zhangsan, Lisi, WangWu]
    
        return render(request, 'template_test.html', {'lst': lst, 'dit': dit, 'person_lst': person_lst,
                                                      'file': 100 * 1024 * 1024, 'value': 4, 'v1': '这是什么鬼,到底什么情况啊?',
                                                      'v2': datetime.datetime.now(),
                                                      'atag': '<a  href="http://www.baidu.com">百度</a>'})
    ```
    
    * template_test.html
    ```html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>模板测试</title>
    </head>
    <body>
    列表:{{ lst }}
    <br>
    字典:{{ dit }}
    <br>
    对象列表:{{ person_lst }}
    <br>
    <br>
    列表中的第一个元素:{{ lst.0 }}
    <br>
    列表中的第二个元素:{{ lst.1 }}
    <br>
    列表中的第三个元素:{{ lst.2 }}
    <br>
    <br>
    字典的name值:{{ dit.name }}
    <br>
    字典的age值:{{ dit.age }}
    <br>
    字典的所有键值:{{ dit.keys }}
    <br>
    字典的所有元素:{{ dit.items }}
    <br>
    <br>
    对象列表的第一个对象:{{ person_lst.0 }}
    <br>
    对象列表的第二个对象:{{ person_lst.1 }}
    <br>
    对象列表的第三个对象:{{ person_lst.2 }}
    <br>
    <br>
    对象列表的第一个对象的name属性值:{{ person_lst.0.name }}
    <br>
    对象列表的第一个对象的age属性值:{{ person_lst.0.age }}
    <br>
    对象列表的第一个对象的eating方法:{{ person_lst.0.eating }}
    <br>
    <br>
    过滤器default: {{ aaa|default:"不存在或为空" }}
    <br>
    filesizeformat: {{ file|filesizeformat }}
    <br>
    add: {{ value|add:"2" }}
    <br>
    add: {{ value|add:"a" }}
    <br>
    add: {{ value|add:"2abc" }}
    <br>
    add: {{ lst|add:person_lst }}
    <br>
    upper: {{ lst.0|upper }}
    <br>
    lower: {{ lst.0|lower }}
    <br>
    title: {{ lst.0|title }}
    <br>
    length: {{ lst.0|length }}
    <br>
    ljust: {{ lst.0|ljust:"10" }}
    <br>
    rjust: {{ lst.0|rjust:"10" }}
    <br>
    center: {{ lst.0|center:"15" }}
    <br>
    first: {{ lst.0|first }}
    <br>
    last: {{ lst.0|last }}
    <br>
    slice: {{ lst|slice:"1:-1" }}
    <br>
    join: {{ lst|join:"-" }}
    <br>
    join: {{ lst.0|join:"-" }}
    <br>
    truncatechars: {{ v1|truncatechars:6 }}
    <br>
    date: {{ v2|date:"Y-m-d H:i:s" }}
    <br>
    默认:{{ atag }}
    <br>
    safe: {{ atag|safe }}
    </body>
    </html>
    ```
    
    * 浏览器访问效果
    ```html
    列表:['abc', '123', 'xyz'] 
    字典:{'name': 'lily', 'age': 18} 
    对象列表:[<app02.views.template_test.<locals>.Person object at 0x00000000047045C0>, <app02.views.template_test.<locals>.Person object at 0x0000000004704400>, <app02.views.template_test.<locals>.Person object at 0x000000000478FC18>] 
    
    列表中的第一个元素:abc 
    列表中的第二个元素:123 
    列表中的第三个元素:xyz 
    
    字典的name值:lily 
    字典的age值:18 
    字典的所有键值:dict_keys(['name', 'age']) 
    字典的所有元素:dict_items([('name', 'lily'), ('age', 18)]) 
    
    对象列表的第一个对象:<app02.views.template_test.<locals>.Person object at 0x00000000047045C0> 
    对象列表的第二个对象:<app02.views.template_test.<locals>.Person object at 0x0000000004704400> 
    对象列表的第三个对象:<app02.views.template_test.<locals>.Person object at 0x000000000478FC18> 
    
    对象列表的第一个对象的name属性值:zhangsan 
    对象列表的第一个对象的age属性值:20 
    对象列表的第一个对象的eating方法:zhangsan is eating. 
    
    过滤器default: 不存在或为空 
    filesizeformat: 100.0 MB 
    add: 6 
    add: 
    add: 
    add: ['abc', '123', 'xyz', <app02.views.template_test.<locals>.Person object at 0x00000000047045C0>, <app02.views.template_test.<locals>.Person object at 0x0000000004704400>, <app02.views.template_test.<locals>.Person object at 0x000000000478FC18>] 
    upper: ABC 
    lower: abc 
    title: Abc 
    length: 3 
    ljust: abc 
    rjust: abc 
    center: abc 
    first: a 
    last: c 
    slice: ['123'] 
    join: abc-123-xyz 
    join: a-b-c 
    truncatechars: 这是什么鬼… 
    date: 2019-07-22 21:42:33 
    默认:<a href="http://www.baidu.com">百度</a> 
    safe: 百度 
    ```
    
    * 当模板系统遇到一个(.)时,会按照如下的顺序去查询:
        * 在字典中查询
        * 属性或者方法
        * 数字索引
    
* Tags{% %} 逻辑相关操作 
    * for循环
    ```html
    <table border="1">
    <tr>
        <th>forloop.counter</th>
        <th>forloop.counter0</th>
        <th>forloop.revcounter</th>
        <th>forloop.revcounter0</th>
        <th>forloop.first</th>
        <th>forloop.last</th>
        <th>p_obj.name</th>
        <th>p_obj.age</th>
    </tr>
    {% for p_obj in person_lst %}
        <tr>
        <td>{{ forloop.counter }}</td>
        <td>{{ forloop.counter0 }}</td>
        <td>{{ forloop.revcounter }}</td>
        <td>{{ forloop.revcounter0 }}</td>
        <td>{{ forloop.first }}</td>
        <td>{{ forloop.last }}</td>
        <td>{{ p_obj.name }}</td>
        <td>{{ p_obj.age }}</td>
        </tr>
    {% endfor %}
    {% for p_obj in person_lst0 %}
        <tr>
        <td>{{ forloop.counter }}</td>
        <td>{{ forloop.counter0 }}</td>
        <td>{{ forloop.revcounter }}</td>
        <td>{{ forloop.revcounter0 }}</td>
        <td>{{ forloop.first }}</td>
        <td>{{ forloop.last }}</td>
        <td>{{ p_obj.name }}</td>
        <td>{{ p_obj.age }}</td>
        </tr>
        {% empty %}
        <tr>
        <td colspan="8">空了</td>
        </tr>
    {% endfor %}
    </table>  
    ```
    
    * if判断
    ```html
    {% if person_lst.0.age > 40 %}
        40岁以上
    {% elif person_lst.0.age > 30 %}
        30岁以上
    {% elif person_lst.0.age > 20 %}
        20岁以上
    {% else %}
        20岁以下
    {% endif %}
    ```
    
    * with
    ```html
    {% with person_lst.0 as zhangsan %}
        zhangsan.name = {{ zhangsan.name }}
        <br>
        zhangsan.age = {{ zhangsan.age }}
    {% endwith %}
    ```
    
    * csrf_token csrf跨站请求伪造(Cross Site Request Forgery) token令牌
        * 这个标签用于跨站请求伪造保护。
        * 在页面的form表单里面写上{% csrf_token %}


老师博客地址: https://www.cnblogs.com/maple-shaw/articles/9282718.html
        https://www.cnblogs.com/maple-shaw/articles/9285269.html
        https://www.cnblogs.com/maple-shaw/articles/9333821.html
原文地址:https://www.cnblogs.com/lilyxiaoyy/p/11253431.html