1 2 3 4

Django框架的使用

 

1 Django安装与配置

1:安装注意事项

安装之前注意看下自己的计算机名  是否是英文  如果是中文  安装后启动Django时极有可能报"utf-8"解码错误

2:安装方式以及启动

终端安装:

pip install django==1.11.22 -i https://pypi.tuna.tsinghua.edu.cn/simple

启动Django:

①终端启动  (麻烦)

第一种:
django-admin  startproject mysite
启动:
python manage.py runserver 127.0.0.1:8000 

  

②pycharm创建(推荐)

File---new project---Django 创建Django

### Django默认端口是8000   可以手动修改   点击下拉DJango的Edit下拉框更改port

关于Pycharm激活 可以看我的另一篇博客 :https://www.cnblogs.com/wakee/p/10952062.html

3:Django项目目录介绍和手动配置

'''
目录介绍

py目录:   
settings.py : 用户自定义的各种配置
urls.py  :  路由文件
wsgi.py  : 启动socket服务端的 文件					
mange.py:  管理文件 python mange.py 各种命令
__init__.py:  一般用来指定连接数据的格式问题(需要导入pymysql)
    
文件:    
templates:    存放html的地方
static:  静态文件 js(可以包含jQuery) css img    bootstrap等文件 
models : ORM链接数据库py   自创的连接数据库的py文件 
其他手动创建的路由分发文件
'''

  

4   Django创建之后的配置工作

1 settings中:
配置模板文件路径:'DIRS': [os.path.join(BASE_DIR, 'templates')]  (一般系统自动配置好)

2:配置静态资源的文件路径:
STATIC_URL = '/static/'
### 在下面配置静态资源文件
STATICFILES_DIRS =  (
os.path.join(BASE_DIR, 'static'), (逗号不能少)
)

3:注释中间件
MIDDLEWARE = [

'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
#'django.middleware.csrf.CsrfViewMiddleware', (注释中间件,这是取消防止csrf攻击,后期可开启csrf攻击)
                                    'django.contrib.auth.middleware.AuthenticationMiddleware',
                                    'django.contrib.messages.middleware.MessageMiddleware',
						'django.middleware.clickjacking.XFrameOptionsMiddleware',
					]	

4:在app中__init__.py文件中
    # 连接mysql数据库时指定连接
    import pymysql
pymysql.install_as_MySQLdb()

  

2 Django的生命周期:

 

 

 

	wsgiref模块		帮你处理http数据  这是本地环境使用的  上线使用uwsgi        
    中间件			   处理连接的的过程和 处理一些安全问题 比如CSRF攻击 				
    URL系统			处理函数分发
           
	jinja2模板语言     django用的是自己写的模板语法
	flask框架         用的jinja2

  

3 Django中的中间件

1 中间件介绍

官方的说法:
中间件是一个用来处理Django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出。每个中间件组件都负责做一些特定的功能。
本质上:
本质上就是一个自定义类 ,是介于request与response处理之间的一道处理过程类中,定义了几个方法,Django框架会在请求的特定的时间去执行这些方法。

注意

但是由于其影响的是全局,所以需要谨慎使用,使用不当会影响性能。

2 中间件的作用

如果你想修改请求,例如被传送到view中的HttpRequest对象。 或者你想修改view返回的HttpResponse对象,这些都可以通过中间件来实现。

可能你还想在view执行之前做一些操作,这种情况就可以用 middleware来实现。

默认中间件

 

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',
]

  

3 中间件的执行流程

从浏览器发出一个请求 Request,得到一个响应后的内容 HttpResponse ,这个请求传递到 Django的过程如下:

请求到达中间件之后,先按照正序执行每个注册中间件的process_request方法,如果process_request方法返回的值是None,就依次执行;如果返回的值是HttpResponse对象,则不再执行后面的process_request方法,而是执行当前对象中间件的process_response方法,并将HttpResponse对象返回给浏览器。

也就是说,如果MIDDLEWARE注册了6个中间件,执行过程中,第3个中间件返回了一个HttpResponse对象,那么第4, 5, 6中间件的process_request和process_response方法都不执行,而是顺序执行3, 2, 1中间件的process_response方法。

 

 

 

 process_request方法都执行完后,匹配路由,找到要执行的视图函数,先不执行视图函数,先执行中间件中的process_view方法,process_view方法返回None,继续按顺序执行,所有process_view方法执行完后执行视图函数。假如中间件3 的process_view方法返回了HttpResponse对象,则4,5,6的process_view以及视图函数都不执行,直接从最后一个中间件,也就是中间件6的process_response方法开始倒序执行。

 

 

 process_template_response和process_exception两个方法的触发是有条件的,执行顺序也是倒序。总结所有的执行流程如下:

 

 

 

 

 

4 自定义中间件

可以自定义中间件的五种方法

process_request(self,request) 发送请求

process_view(self, request, callback, callback_args, callback_kwargs) 执行完 request 预处理函数并确定待执行的 view 之后,但在 view 函数实际执行之前。

process_template_response(self,request,response) response参数应该是一个由view或者中间件返回的TemplateResponse对像(或等价的对象)。如果响应的实例有render()方法,process_template_response()会在view刚好执行完毕之后被调用。这个方法必须返回一个实现了render方法的响应对象。

process_exception(self, request, exception) 收集错误信息,当一个view引发异常时,Django会调用
process_exception()来处理。返回一个None或一个HttpResponse对象。如果返回HttpResponse对象,会将响应交给处理响应的中间件处理。由于处理响应时是从下到上的,此层以上的process_exception()是不会被调用的。

process_response(self, request, response) 必须返回 HttpResponse 对象。这个 response 对象可以是传入函数的那一个原始对象(通常已被修改),也可以是全新生成的。
执行顺序也是按照以上顺序执行的。

  

process_request

执行时间:
在视图函数之前,在路由匹配之前

参数:
request:请求对象,与视图中用到的request参数是同一个对象

返回值:
None:按照正常的流程走
HttpResponse:接着倒序执行当前中间件的以及之前执行过的中间件的process_response方法,不再执行其它的所有方法

执行顺序:
按照MIDDLEWARE中的注册的顺序执行,也就是此列表的索引值

  

process_response

执行时间:
最后执行

参数:
request:请求对象,与视图中用到的request参数是同一个对象
response:响应对象,与视图中返回的response是同一个对象

返回值:
response:必须返回此对象,按照正常的流程走

执行顺序:
按照注册的顺序倒序执行

  

process_view

执行时间:
在process_request方法及路由匹配之后,视图之前

参数:
request:请求对象,与视图中用到的request参数是同一个对象
view_func:将要执行的视图函数(它是实际的函数对象,而不是函数的名称作为字符串)
view_args:url路径中将传递给视图的位置参数的元组
view_kwargs:url路径中将传递给视图的关键值参数的字典

返回值:
None:按照正常的流程走
HttpResponse:它之后的中间件的process_view,及视图不执行,执行所有中间件的process_response方法

执行顺序:
按照注册的顺序执行

  

process_exception

执行时间:
视图之后,process_response之前

参数:
request:请求对象,与视图中用到的request参数是同一个对象
exception:视图函数异常产生的Exception对象

返回值:
None:按照正常的流程走
HttpResponse对象:不再执行后面的process_exception方法

执行顺序:
按照注册的顺序倒序执行

  

process_template_response

此方法必须在视图函数返回的对象有一个render()方法(或者表明该对象是一个TemplateResponse对象或等价方法)时,才被执行.

render()方法用于返回一个HTML页面.

执行时间:
视图之后,process_exception之前

参数:
request:请求对象,与视图中用到的request参数是同一个对象
response:是TemplateResponse对象(由视图函数或者中间件产生)

返回值:
response:必须返回此对象,按照正常的流程走

执行顺序:
按照注册的顺序倒序执行

  

案例

第一步导入

from django.utils.deprecation import MiddlewareMixin

第二步自定义

from django.utils.deprecation import MiddlewareMixin
# from django.http import HttpResponse
from django.shortcuts import HttpResponse, redirect


# 方式一:
class MyMiddleware(MiddlewareMixin):
    def process_request(self, request):
        next_url = request.path_info
        if not request.path_info.startswith("/login/"):
            # 做登录验证
            login_flag = request.session.get("login", "")
            if not login_flag:
                return redirect("/login/?next={}".format(next_url))

    def process_view(self, request, view_func, view_args, view_kwargs):
        pass

    def process_exception(self, request, exception):
        if isinstance(exception, ValueError):
            return HttpResponse("404")

    def process_response(self, request, response):
        return response


# 方式二:
class SimpleMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response
        # 一次性配置和初始化。

    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.
        # 这里写的代码会在视图被调用前执行来处理请求
        response = self.get_response(request)
        # 这里写的代码会在视图调用后执行来处理响应
        # Code to be executed for each request/response after
        # the view is called.

        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        pass

    def process_exception(self, request, exception):
        pass

    def process_template_response(self, request, response):
        pass

  第三步 中间件注册

# 在settings.py里的下面列表中添加自定义的中间件来激活该中间件
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',
    'mymiddlewares.middlewares.MyMiddleware',
    'mymiddlewares.middlewares.SimpleMiddleware',
]

 

 

 

4 Django之路由层

路由定义

路由即请求地址与视图函数的映射关系,如果把网站比喻为一本书,那路由就好比是这本书的目录,在Django中路由默认配置在urls.py中,如下图:

 

 

 

 

简单路由配置

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

# 由一条条映射关系组成的urlpatterns这个列表称之为路由表
urlpatterns = [
     url(regex, view, kwargs=None, name=None), # url本质就是一个函数
]
#函数url关键参数介绍
# regex:正则表达式,用来匹配url地址的路径部分,
        # 例如url地址为:http://127.0.0.1:8001/index/,正则表达式要匹配的部分是index/
# view:通常为一个视图函数,用来处理业务逻辑
# kwargs:略(用法详见有名分组)
# name:略(用法详见反向解析)

注意

'''
当APPEND_SLASH=True(如果配置文件中没有该配置,APPEND_SLASH的默认值为True),并且用户请求的url地址的路径部分不是以 / 结尾,第一次请求之后没有发现路径会在请求路径后面加/ 再去匹配一次
当APPEND_SLASH=False时  默认就匹配一次  不成功不会加/再次匹配
'''

  

路由分组

无名分组

在普通的正则匹配中加上()就是无名分组

urls.py文件

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    
    # 该正则会匹配url地址的路径部分为:article/数字/,匹配成功的分组部分会以关键字参数(article_id=匹配成功的数字)的形式传给视图函数,有几个有名分组就会传几个关键字参数
    url(r'^aritcle/(?P<article_id>\d+)/$',views.article), 
]

views.py文件

from django.shortcuts import render
from django.shortcuts import HttpResponse

# 需要额外增加一个形参,形参名必须为article_id
def article(request,article_id):
    return HttpResponse('id为 %s 的文章内容...' %article_id)

测试:

python manage.py runserver 8001 # 在浏览器输入:http://127.0.0.1:8001/article/3/ 会看到: id为 3 的文章内容...

  

有名分组

有名分组其实就是在无名的分组的基础上加上了名字

语法为:(?P<名字> 正则表达式),就是在无名分组的括号里面加上了?P<名字>,注意其中P为大写

urls.py文件

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    
    # 该正则会匹配url地址的路径部分为:article/数字/,匹配成功的分组部分会以关键字参数(article_id=匹配成功的数字)的形式传给视图函数,有几个有名分组就会传几个关键字参数
    url(r'^aritcle/(?P<article_id>\d+)/$',views.article), 
]

区别

区别在于无名分组是以位置参数的形式传递,有名分组是以关键字参数的形式传递

  

路由分发

随着项目功能的增加,app会越来越多,路由也越来越多,每个app都会有属于自己的路由,如果再将所有的路由都放到一张路由表中,会导致结构不清晰,不便于管理,所以我们应该将app自己的路由交由自己管理,然后在总路由表中做分发,具体做法如下

1 创建两个app

# 新建项目mystie2
E:\git>django-admin startproject mysite2
# 切换到项目目录下
E:\git>cd mysite2
# 创建app01和app02
E:\git\mysite2>python3 manage.py startapp app01
E:\git\mysite2>python3 manage.py startapp app02

 

2 在每个app下手动创建urls.py来存放自己的路由,如下:

app01下的urls.py文件

from django.conf.urls import url
# 导入app01的views
from app01 import views

urlpatterns = [
    url(r'^index/$',views.index), 
]

app01下的views.py

from django.shortcuts import render
from django.shortcuts import HttpResponse

def index(request):
    return HttpResponse('我是app01的index页面...')

app02下的urls.py文件

from django.conf.urls import url
# 导入app02的views
from app02 import views

urlpatterns = [
    url(r'^index/$',views.index), 
]

app02下的views.py

from django.shortcuts import render
from django.shortcuts import HttpResponse

def index(request):
    return HttpResponse('我是app02的index页面...')

3 在总的urls.py文件中(mysite2文件夹下的urls.py)

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

# 总路由表
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    
    # 新增两条路由,注意不能以$结尾
    # include函数就是做分发操作的,当在浏览器输入http://127.0.0.1:8001/app01/index/时,会先进入到总路由表中进行匹配,正则表达式r'^app01/'会先匹配成功路径app01/,然后include功能会去app01下的urls.py中继续匹配剩余的路径部分
    url(r'^app01/', include('app01.urls')),
    url(r'^app02/', include('app02.urls')),
]

测试

python manage.py runserver 8001  
# 在浏览器输入:http://127.0.0.1:8001/app01/index/ 会看到:我是app01的index页面...
# 在浏览器输入:http://127.0.0.1:8001/app02/index/ 会看到:我是app02的index页面...

  

反向解析

路由设置时通过别名来获取路径

案例:登录成功跳转到index.html页面

在urls.py文件中

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    
    url(r'^login/$', views.login,name='login_page'), # 路径login/的别名为login_page
    url(r'^index/$', views.index,name='index_page'), # 路径index/的别名为index_page
]

  

在views.py

from django.shortcuts import render 
from django.shortcuts import reverse # 用于反向解析
from django.shortcuts import redirect #用于重定向页面
from django.shortcuts import HttpResponse

def login(request):
    if request.method == 'GET':
        # 当为get请求时,返回login.html页面,页面中的{% url 'login_page' %}会被反向解析成路径:/login/
        return render(request, 'login.html')
    
    # 当为post请求时,可以从request.POST中取出请求体的数据
    name = request.POST.get('name')
    pwd = request.POST.get('pwd')
    if name == 'kevin' and pwd == '123':
        url = reverse('index_page')  # reverse会将别名'index_page'反向解析成路径:/index/       
        return redirect(url) # 重定向到/index/
    else:
        return HttpResponse('用户名或密码错误')


def index(request):
    return render(request, 'index.html')

  

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>
<!--强调:login_page必须加引号-->
<form action="{% url 'login_page' %}" method="post">
    {% csrf_token %} <!--强调:必须加上这一行,如果中间件csrf没有注释掉-->
    <p>用户名:<input type="text" name="name"></p>
    <p>密码:<input type="password" name="pwd"></p>
    <p><input type="submit" value="提交"></p>

</form>
</body>
</html>

  

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<h3>我是index页面...</h3>
</body>
</html>

测试

python manage.py runserver 8001  
# 在浏览器输入:http://127.0.0.1:8001/login/ 会看到登录页面,输入正确的用户名密码会跳转到index.html
# 当我们修改路由表中匹配路径的正则表达式时,程序其余部分均无需修改

总结:

在views.py中,反向解析的使用:
    url = reverse('index_page')
在模版login.html文件中,反向解析的使用
    {% url 'login_page' %}

  

5 视图函数Views

1 视图层作用

1:获取数据

2:对数据进行处理

3:连接数据库(ORM)对数据库操作)

4:对模板进行渲染(模板系统)

2 视图函数

视图函数,简称视图,属于Django的视图层,默认定义在views.py文件中,是用来处理web请求信息以及返回响应信息的函数,所以研究视图函数只需熟练掌握两个对象即可:请求对象(HttpRequest)和响应对象(HttpResponse)

请求对象(HttpRequest)

 

django将http协议请求报文中的请求行、首部信息、内容主体封装到了HttpRequest对象中(类似于我们自定义框架的environ参数)。 django会将HttpRequest对象当做参数传给视图函数的第一个参数request,在视图函数中,通过访问该对象的属性便可以提取http协议的请求数据

1、HttpRequest对象常用属性part1

一.HttpRequest.method
  获取请求使用的方法(值为纯大写的字符串格式)。例如:"GET"、"POST"
   应该通过该属性的值来判断请求方法

二.HttpRequest.GET
  值为一个类似于字典的QueryDict对象,封装了GET请求的所有参数,可通过HttpRequest.GET.get('键')获取相对应的值
  
三.HttpRequest.POST
   值为一个类似于字典的QueryDict对象,封装了POST请求所包含的表单数据,可通过HttpRequest.POST.get('键')获取相对应的值
   
   针对表单中checkbox类型的input标签、select标签提交的数据,键对应的值为多个,需要用:HttpRequest.POST.getlist("hobbies")获取存有多个值的列表,
  同理也有HttpRequest.GET.getlist("键")

  

案例:

urls.py

from django.urls import re_path
from app01 import views

urlpatterns = [
    re_path(r'^login/$',views.login),
]

Views.py

from django.shortcuts import render,HttpResponse

def login(request):
    if request.method == 'GET':
        # 当请求url为:http://127.0.0.1:8001/login/?a=1&b=2&c=3&c=4&c=5
        # 请求方法是GET,?后的请求参数都存放于request.GET中
        print(request.GET)
        # 输出<QueryDict: {'a': ['1'], 'b': ['2'], 'c': ['3', '4', '5']}>

        # 获取?后参数的方式为
        a=request.GET.get('a') # 1
        b=request.GET.get('b') # 2
        c=request.GET.getlist('c') # ['3', '4', '5']

        return render(request,'login.html')
    elif request.method == 'POST':
        # 在输入框内输入用户名egon、年龄18,选择爱好,点击提交
        # 请求方法为POST,表单内的数据都会存放于request.POST中
        print(request.POST) 
        # 输出<QueryDict: {..., 'name': ['egon'], 'age': ['18'], 'hobbies': ['music', 'read']}>

        # 获取表单中数据的方式为
        name=request.POST.get('name') # egon
        age=request.POST.get('age') # 18
        hobbies=request.POST.getlist('hobbies') # ['music', 'read']

        return HttpResponse('提交成功')

  

在templates目录下新建login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>

<!--
method="post"代表在提交表单时会以POST方法提交表单数据
action="/login/" 代表表单数据的提交地址为http://127.0.0.1:8001/login/,可以简写为action="/login/",或者action=""
-->
<form action="http://127.0.0.1:8001/login/" method="post">
    {% csrf_token %} <!--强调:必须加上这一行,后续我们会详细介绍-->
    <p>用户名:<input type="text" name="name"></p>
    <p>年龄:<input type="text" name="age"></p>
    <p>
        爱好:
        <input type="checkbox" name="hobbies" value="music">音乐
        <input type="checkbox" name="hobbies" value="read">阅读
        <input type="checkbox" name="hobbies" value="dancing">跳舞
    </p>
    <p><input type="submit" value="提交"></p>

</form>
</body>
</html>  

2、HttpRequest对象常用属性part2

一.HttpRequest.body
   当浏览器基于http协议的POST方法提交数据时,数据会被放到请求体中发送给django,django会将接收到的请求体数据存放于HttpRequest.body属性中,因为该属性的值为Bytes类型,所以通常情况下直接处理Bytes、并从中提取有用数据的操作是复杂而繁琐的,好在django会对它做进一步的处理与封装以便我们更为方便地提取数据,比如
   对于form表单来说,提交数据的常用方法为GET与POST
   1:如果表单属性method='GET',那么在提交表单时,表单内数据不会存放于请求体中,而是会将表单数据按照k1=v1&k2=v2&k3=v3的格式放到url中,然后发送给django,django会将这些数据封装到request.GET中,注意此时的request.body为空、无用
   2:如果表单属性method='POST',那么在提交表单时,表单内的所有数据都会存放于请求体中,在发送给django后会封装到request.body里,此时django为了方便我们提取数据,会request.body的数据进行进一步的处理,具体如何处理呢,需要从form表单提交数据的编码格式说起:
    form表单对提交的表单数据有两种常用的编码格式,可以通过属性enctype进行设置,如下
    编码格式1(默认的编码格式):enctype="application/x-www-form-urlencoded"
    编码格式2(使用form表单上传文件时只能用该编码):enctype="multipart/form-data"
    如果form表单提交数据是按照编码格式1,那么request.body中数据的格式类似于GET方法的数据格式,如k1=v1&k2=v2,此时django会将request.body中的数据提取出来封装到request.POST中方便我们提取
    如果form表单提交数据是按照编码格式2,那么request.body中数据的格式为b'------WebKitFormBoundaryKtcwuksQltpNprep\r\nContent-Disposition: form-data;......',,此时django会将request.body中的数据提取出来封装到request.POST中,将上传的文件数据专门提取出来封装到request.FILES属性中
    强调:毫无疑问,编码格式2的数据量要大于编码格式1,如果无需上传文件,还是推荐使用更为精简的编码格式1
   
    我们除了可以采用form表单向django提交数据外,还可以采用ajax技术,ajax可以提交的数据格式有:1、编码格式1 2、编码格式2 3、json,当ajax采用POST方法提交前两种格式的数据时,django的处理方案同上,但是当ajax采用POST方法提交json格式的数据时,django会将接收到的数据存放于HttpRequest.body,此时需要我们自己对HttpRequest.body属性值做反序列化操作,
具体的,我们在讲解ajax时再做具体介绍
   
二.HttpRequest.FILES
   如果使用form表单POST上传文件的话,文件数据将包含在HttpRequest.FILES属性中。
  该属性值为一个类似于字典的对象,可以包含多组key:value(对应多个上传的文件),其中每个key为<input type="file" name="" /> 中name属性的值,而value则为对应的文件数据
  强调:HttpRequest.FILES 只有在请求的方法为POST 且提交的<form> 带有enctype="multipart/form-data" 的情况下才会包含数据。否则,FILES 将为一个空的类似于字典的对象。

 

案例:form表单上传文件

 

from django.urls import path,register_converter,re_path
from app01 import views

urlpatterns = [
    re_path(r'^register/$',views.register),
]

views.py

from django.shortcuts import render,HttpResponse

def register(request):
    if request.method == 'GET':
        return render(request,'register.html')
    elif request.method == 'POST':
        print(request.body)

        # 从request.POST中获取用户名
        name=request.POST.get('name') 
        # 从request.FILES获取文件对象
        file_obj=request.FILES.get('header_img') 
        
        # 上传的文件存放于templates文件夹下
        with open('templates/header.png','wb') as f: 
            for line in file_obj:
                f.write(line)

        return HttpResponse('注册成功')

  

 

在templates目录下新建register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
</head>
<body>

<form action="" method="POST" enctype="multipart/form-data" >
    {% csrf_token %}
    <p>
        用户名:<input type="text" name="name">
    </p>
    <p>
        头像:<input type="file" name="header_img">
    </p>
    <p>
        <input type="submit" value="提交">
    </p>
</form>
</body>
</html>

  

3、HttpRequest对象常用属性part3

一.HttpRequest.path
  获取url地址的路径部分,只包含路径部分

二.HttpRequest.get_full_path()
  获取url地址的完整path,既包含路径又包含参数部分

如果请求地址是http://127.0.0.1:8001/order/?name=egon&age=10#_label3,
HttpRequest.path的值为"/order/"
HttpRequest.get_full_path()的值为"/order/?name=egon&age=10"

  

案例:

urls.py

from django.urls import path,register_converter,re_path
from app01 import views

urlpatterns = [
    re_path(r'^order',views.order),
]

  

views.py

from django.shortcuts import render,HttpResponse

# 针对请求的url地址:http://127.0.0.1:8001/order/?name=egon&age=10#_label3
# 从域名后的最后一个“/”开始到“?”为止是路径部分,即/order/
# 从“?”开始到“#”为止之间的部分为参数部分,即name=egon&age=10
def order(request):
    print(request.path) # 结果为“/order/”
    print(request.get_full_path()) # 结果为"/order/?name=egon&age=10"

    return HttpResponse('order page') 

4、HttpRequest对象常用属性part4

一.HttpRequest.META
   值为包含了HTTP协议的请求头数据的Python字典,字典中的key及期对应值的解释如下
    CONTENT_LENGTH —— 请求的正文的长度(是一个字符串)。
    CONTENT_TYPE —— 请求的正文的MIME类型。
    HTTP_ACCEPT —— 响应可接收的Content-Type。
    HTTP_ACCEPT_ENCODING —— 响应可接收的编码。
    HTTP_ACCEPT_LANGUAGE —— 响应可接收的语言。
    HTTP_HOST —— 客服端发送数据的目标主机与端口
    HTTP_REFERER —— Referring 页面。
    HTTP_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 的键时,
    都会
    1、将所有字母大写
    2、将单词的连接符替换为下划线
    3、加上前缀HTTP_。
    所以,一个叫做 X-Bender 的头部将转换成 META 中的 HTTP_X_BENDER 键。
   
注意:下述常用属性暂且了解即可,待我们讲到专门的知识点时再专门详细讲解

二.HttpRequest.COOKIES
  一个标准的Python 字典,包含所有的cookie。键和值都为字符串。

三.HttpRequest.session
  一个既可读又可写的类似于字典的对象,表示当前的会话。只有当Django 启用会话的支持时才可用。

11.HttpRequest.user(用户认证组件下使用)

  一个 AUTH_USER_MODEL 类型的对象,表示当前登录的用户。

2.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') 装饰你的视图以让响应能够正确地缓存

  

响应对象(HttpResponse)

响应对象主要有三种形式:HttpResponse, render, redirect, JsonResponse

from django.shortcuts import HttpResponse,render,redirect  # 导入相关包

 

HttpResponse()

括号内直接跟一个具体的字符串作为响应体,比较直接很简单,所以这里主要介绍后面两种形式。 

render()

render(request, template_name[, context])
参数:
    1、request:用于生成响应的请求对象,固定必须传入的第一个参数

    2、template_name:要使用的模板的完整名称,必须传入,render默认会去templates目录下查找模板文件

    3、context:可选参数,可以传入一个字典用来替换模块文件中的变量

综上,render的功能可以总结为:根据给定字典渲染模板文件,并返回一个渲染后的 HttpResponse对象。

 

redirect()

# 返回重定向信息
def my_view(request):
    ...
    return redirect('/some/url/')

# 重定向的地址也可以是一个完整的URL:
def my_view(request):
    ...
    return redirect('http://www.baidu.com/')  

JsonResponse

向前端返回一个json格式字符串的两种方式

方式一

import json

def my_view(request):
    data=['egon','kevin']
    return HttpResponse(json.dumps(data) )

方式二

from django.http import JsonResponse

def my_view(request):
    data=['egon','kevin']
    return JsonResponse(data,safe=False)
    #默认safe=True代表只能序列化字典对象,safe=False代表可以序列化字典以外的对象

  

3 FBV和CBV

django的视图层由两种形式构成:FBV和CBV

1、FBV基于函数的视图(Function base view),我们之前一直介绍的都是FBV

2、CBV基于类的视图(Class base view)

案例:

urls.py

from django.urls import path,register_converter,re_path
from app01 import views

urlpatterns = [
    re_path(r'^login/',views.LoginView.as_view()), # 必须调用类下的方法as_view
]

  

views.py

from django.shortcuts import render,HttpResponse,redirect
from django.views import View

class LoginView(View):
    def dispatch(self, request, *args, **kwargs): # 可在该方法内做一些预处理操作
        # 当请求url为:http://127.0.0.1:8008/login/会先触发dispatch的执行
        # 如果http协议的请求方法为GET,则调用下述get方法
        # 如果http协议的请求方法为POST,则调用下述post方法
        obj=super().dispatch(request, *args, **kwargs) # 必须继承父类的dispatch功能
        return obj # 必须返回obj

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

    def post(self,request):
        name=request.POST.get('name')
        pwd=request.POST.get('pwd')
        if name  == 'egon' and pwd == '123':
            res='登录成功'
        else:
            res='用户名或密码错误'
        return HttpResponse(res)

测试

python manage.py runserver 8001 
# 验证GET请求:在浏览器输入:http://127.0.0.1:8001/login/
# 验证POST请求:在表单内输入数据然后提交

  

6 Django之模板层

django的模板=HTML代码+模板语法

存放于templates目录下的html文件称之为模板文件,如果我们想要返回的html页面中的数据是动态的,那么必须在html页面中嵌入变量,这便用到了django的模板语法,具体来说,django的模板语法有以下重点

一、变量:{{ 变量名 }}
    1.1 深度查询:句点符的应用
    1.2 过滤器
二、标签:{% 标签名 %}
三、自定义标签和过滤器
四、模板的导入和继承

  

二 模板语法之变量

2.1 变量的基本使用

如果html代码中的数据不是固定死的,而是动态变化的,则必须在html中嵌入变量,为此,模板语法提供了变量的概念,允许我们在html代码中嵌入变量,我们只需要在视图函数中用render方法为html文件中指定的变量赋值即可,具体用法如下

test.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<p>{{ msg }}</p>
<p>{{ dic }}</p>
<p>{{ obj }}</p>
<p>{{ li }}</p>
</body>
</html>

 

我们需要在视图函数中为模板test.html的变量名msg、li、dic、obj、obj_li赋值,views.py内容如下

 
from django.shortcuts import render

def test(request):
    # 传给模板的变量值可以是任意python类型,如下
    msg='hello world'
    dic={'k1':1,'k2':2}
    class Person(object):
        def __init__(self,name,age):
            self.name=name
            self.age=age

    obj=Person('egon',18)
    li = [1,'aaa',obj]

    return render(request,'test.html',{'msg':msg,'dic':dic,'obj':obj,'li':li})
    # 注意:
    # 1、render函数的第三个参数包含了要传给模板的变量值,是一个字典类型,该字典中的key必须与模板文件中的变量名相对应,render函数会去templates目录下找到模板文件,
    然后根据字典中的key对应到模板文件中的变量名进行赋值操作,最后将赋值后的模板文件内容返回给浏览器 # 2、可以将render函数的第三个参数简写为locals(),如下 return render(request,'test.html',locals()) #locals()会将函数test内定义的名字与值转换为字典中的k与v

2.2 深度查询之句点符的使用

当视图函数传给模板的值中包含多个元素时,若想取出其中的单个元素,就必须使用句点符了。

句点符既可以引用容器类型的元素,也可以引用对象的方法,如下

test.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<!--调用字符串对象的upper方法,注意不要加括号-->
<p>{{ msg.upper }}</p>

<!--取字典中k1对应的值-->
<p>{{ dic.k1 }}</p>

<!--取对象的name属性-->
<p>{{ obj.name }}</p>

<!--取列表的第2个元素,然后变成大写-->
<p>{{ li.1.upper }}</p>

<!--取列表的第3个元素,并取该元素的age属性-->
<p>{{ li.2.age }}</p>


</body>
</html>

 

2.3 过滤器

过滤器类似于python的内置函数,用来把视图传入的变量值加以修饰后再显示,具体语法如下

 

{{ 变量名|过滤器名:传给过滤器的参数 }}

常用内置过滤器

#1、default
#作用:如果一个变量值是False或者为空,使用default后指定的默认值,否则,使用变量本身的值,如果value=’‘则输出“nothing”
{{ value|default:"nothing" }}

#2、length
#作用:返回值的长度。它对字符串、列表、字典等容器类型都起作用,如果value是 ['a', 'b', 'c', 'd'],那么输出是4
{{ value|length }}

#3、filesizeformat
#作用:将值的格式化为一个"人类可读的"文件尺寸(如13KB、4.1 MB、102bytes等等),如果 value 是 12312312321,输出将会是 11.5 GB
{{ value|filesizeformat }}

#4、date
#作用:将日期按照指定的格式输出,如果value=datetime.datetime.now(),按照格式Y-m-d则输出2019-02-02
{{ value|date:"Y-m-d" }}  

#5、slice
#作用:对输出的字符串进行切片操作,顾头不顾尾,如果value=“egon“,则输出"eg"
{{ value|slice:"0:2" }} 

#6、truncatechars
#作用:如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾,如果value=”hello world egon 嘎嘎“,则输出"hello...",注意8个字符也包含末尾的3个点
{{ value|truncatechars:8 }}

#7、truncatewords
#作用:同truncatechars,但truncatewords是按照单词截断,注意末尾的3个点不算作单词,如果value=”hello world egon 嘎嘎“,则输出"hello world ..."
{{ value|truncatewords:2 }}

#8、safe
#作用:出于安全考虑,Django的模板会对HTML标签、JS等语法标签进行自动转义,例如value="<script>alert(123)</script>",模板变量{{ value }}会被渲染成<script>alert(123)</script>交给浏览器后会被解析成普通字符”<script>alert(123)</script>“,失去了js代码的语法意义,但如果我们就想让模板变量{{ value }}被渲染的结果又语法意义,那么就用到了过滤器safe,比如value='<a href="https://www.baidu.com">点我啊</a>',在被safe过滤器处理后就成为了真正的超链接,不加safe过滤器则会当做普通字符显示’<a href="https://www.baidu.com">点我啊</a>‘
{{ value|safe }}

  

三 模板语法之标签

标签是为了在模板中完成一些特殊功能,语法为{% 标签名 %},一些标签还需要搭配结束标签 {% endtag %}

3.1 常用标签之for标签

#1、遍历每一个元素:
{% for person in person_list %}
    <p>{{ person.name }}</p>
{% endfor %}

#2、可以利用{% for obj in list reversed %}反向循环。

#3、遍历一个字典:
{% for key,val in dic.items %}
    <p>{{ key }}:{{ val }}</p>
{% endfor %}

#4、循环序号可以通过{{ forloop }}显示 
forloop.counter            当前循环的索引值(从1开始)
forloop.counter0           当前循环的索引值(从0开始)
forloop.revcounter         当前循环的倒序索引值(从1开始)
forloop.revcounter0        当前循环的倒序索引值(从0开始)
forloop.first              当前循环是第一次循环则返回True,否则返回False
forloop.last               当前循环是最后一次循环则返回True,否则返回False
forloop.parentloop         本层循环的外层循环

#5、for标签可以带有一个可选的{% empty %} 从句,在变量person_list为空或者没有被找到时,则执行empty子句
{% for person in person_list %}
    <p>{{ person.name }}</p>

{% empty %}
    <p>sorry,no person here</p>
{% endfor %}

  

案例

url.py

from django.urls import re_path
from app01 import views

urlpatterns = [
    re_path(r'^test/',views.test)
]

  

view.py

def test(request):
    names=['egon','kevin']
    dic={'name':'egon','age':18,'sex':'male'}

    list1=[]

    return render(request,'test.html',locals())

test.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<hr>
{% for name in names %}
    <p>{{ forloop.counter0 }} {{ name }}</p>
{% endfor %}
<!--
输出结果为:
0 egon
1 kevin
-->

<hr>
{% for name in names reversed %}
    <p>{{ forloop.revcounter0 }} {{ name }}</p>
{% endfor %}
<!--
输出结果为:
1 kevin
0 egon
-->

<hr>
{% for k,v in dic.items %}
    <p>{{ forloop.counter }} {{ k }} {{ v }}</p>
{% endfor %}
<!--
输出结果为:
1 name egon
2 age 18
3 sex male
-->

<hr>
{% for item in list1 %}
    <p>{{ item }}</p>
    {% empty %}
        <p>sorry,no value here</p>
{% endfor %}
<!--
输出结果为:
sorry,no value here
-->

</body>
</html>

 

3.2 常用标签之if标签

# 1、注意:
{% if 条件 %}条件为真时if的子句才会生效,条件也可以是一个变量,if会对变量进行求值,在变量值为空、或者视图没有为其传值的情况下均为False

# 2、具体语法
{% if num > 100 or num < 0 %}
    <p>无效</p>
{% elif num > 80 and num < 100 %}
    <p>优秀</p>
{% else %}
    <p>凑活吧</p>
{% endif %}

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

  

案例

urls.py

from django.urls import path,register_converter,re_path
from app01 import views

urlpatterns = [
    # 输入http://127.0.0.1:8008/或者http://127.0.0.1:8008/index/都会转发给视图函数index
    re_path(r'^$',views.index),
    re_path(r'^index/$',views.index),
    
    re_path(r'^login/',views.login),

]

  

views.py

from django.shortcuts import render

def index(request):
    return render(request,'index.html')

def login(request):
    if request.method == 'GET':
        return render(request,'login.html')

    name=request.POST.get('name')
    pwd=request.POST.get('pwd')
    if name == 'egon' and pwd == '123':
        current_user=name
        return render(request,'index.html',locals())
    else:
        msg='账号或密码错误'
        return render(request,'login.html',locals())

  

在templates目录下新建模板文件index.html与login.html

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>

<h3>首页</h3>
<!--
如果用户已经登录,则current_user变量有值,if判断结果为真,会打印变量current_user的值,为当前登录的用户名
如果用户没有登录,则current_user变量无值,if判断结果为假,会打印a标签要求用户先登录
-->
{% if current_user %}
    <p>当前登录用户为:{{ current_user }}</p>
{% else %}
    <p><a href="/login/">请先登录</a></p>
{% endif %}

</body>
</html>

  

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>

<form action="" method="POST">
    {% csrf_token %}
    <p>用户名:<input type="text" name="name"></p>
    <p>密码:<input type="password" name="pwd"></p>
    <p><input type="submit" value="提交"></p>
</form>
<!--输错账号密码时的提示信息-->
<p style="color: red">{{ msg }}</p>
</body>
</html>

  

3.3 常用标签之with标签

# with标签用来为一个复杂的变量名起别名,如果变量的值来自于数据库,在起别名后只需要使用别名即可,无需每次都向数据库发送请求来重新获取变量的值
{% with li.1.upper as v %}
    {{ v }}
{% endwith %

  

3.4 常用标签之csrf_token标签

# 当用form表单提交POST请求时必须加上标签{% csrf_token%},该标签用于防止跨站伪造请求
<form action="" method="POST">
    {% csrf_token %}
    <p>用户名:<input type="text" name="name"></p>
    <p>密码:<input type="password" name="pwd"></p>
    <p><input type="submit" value="提交"></p>
</form>
# 具体工作原理为:
# 1、在GET请求到form表单时,标签{% csrf_token%}会被渲染成一个隐藏的input标签,该标签包含了由服务端生成的一串随机字符串,如<input type="hidden" name="csrfmiddlewaretoken" value="dmje28mFo...OvnZ5">
# 2、在使用form表单提交POST请求时,会提交上述随机字符串,服务端在接收到该POST请求时会对比该随机字符串,对比成功则处理该POST请求,否则拒绝,以此来确定客户端的身份

  

四 自定义过滤器和标签

当内置的过滤器或标签无法满足我们需求时,我们可以自定义,具体操作步骤如下

1、在settings中的INSTALLED_APPS添加当前app的名字,不然django无法找到自定义的过滤器或标签

settings.py

# 在settings.py中找到该列表,然后加以配置
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config',
    'app01', # 添加当前app的名字
]

  

2、在文件夹app01中创建子文件夹templatetags(文件夹名只能是templatetags)

3、在templatetags新建任意.py文件,如my_tags.py,在该文件中自定义过滤器或标签,文件内容如下

from django import template
register = template.Library() # 注意变量名必须为register,不可改变

#1、自定义过滤器
@register.filter
def my_multi_filter(v1 ,v2): # 自定义的过滤器只能定义最多两个参数,针对{{ value1 | filter_multi:value2 }},参数传递为v1=value1,v2=value2
    return  v1 * v2

#2、自定义标签
@register.simple_tag
def my_multi_tag(v1, v2): # 自定义的标签可以定义多个参数
    return v1 * v2


#3、自定义标签扩展之mark_safe
# 注释:我们可以用内置的标签safe来让标签内容有语法意义,如果我们想让自定义标签处理的结果也有语法意义,则不能使用内置标签safe了,需要使用mark_safe,可以实现与内置标签safe同样的功能
from django.utils.safestring import mark_safe

@register.simple_tag
def my_input_tag(id, name):
    res = "<input type='text' id='%s' name='%s' />" % (id, name)
    return mark_safe(res)

  

4、自定义过滤器或标签必须重新启动django方可生效

5、自定义过滤器或标签的使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<!--必须先加载存有自定义过滤器和标签的文件-->
{% load my_tags %}

<!--salary的值为10,经过滤器my_multi_filter的处理结果为120-->
{{ salary|my_multi_filter:12 }}

<!--结果为2-->
{% my_multi_tag 1 2 %}

<!--
结果为一个input标签,该表的属性id="inp1" name="username"
注意:input的属性值均为字符串类型,所以my_input_tag后的两个值均为字符串类型
-->
{% my_input_tag "inp1" "username" %} 

</body>
</html>

  

对比自定义标签与自定义过滤器

#1、自定义过滤器只能传两个参数,而自定义标签却可以传多个参数

#2、过滤器可以用于if判断,而标签不能
{% if salary|my_multi_filter:12 > 200 %}
    <p>优秀</p>
{% else %}
    <p>垃圾</p>
{% endif %}

  

五 模板的导入和继承

在实际开发中,模板文件彼此之间可能会有大量冗余代码,为此django提供了专门的语法来解决这个问题,主要围绕三种标签的使用:include标签、extends标签、block标签,详解如下

5.1、模板的导入之include标签

#作用:在一个模板文件中,引入/重用另外一个模板文件的内容,
{% include '模版名称' %}

  

案例:

可以把广告栏写到专门的文件里advertise.html

<div class="adv">
    <div class="panel panel-default">
        <div class="panel-heading">
            <h3 class="panel-title">Panel title</h3>
        </div>
        <div class="panel-body">
            Panel content
        </div>
    </div>
    <div class="panel panel-danger">
        <div class="panel-heading">
            <h3 class="panel-title">Panel title</h3>
        </div>
        <div class="panel-body">
            Panel content
        </div>
    </div>
    <div class="panel panel-warning">
        <div class="panel-heading">
            <h3 class="panel-title">Panel title</h3>
        </div>
        <div class="panel-body">
            Panel content
        </div>
    </div>
</div>

  

然后在base.html文件中用include标签引入advertise.html文件的内容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .header {
            height: 50px;
            width: 100%;
            background-color: black;
        }
        
    </style>
</head>
<body>
<div class="header"></div>
<div class="container">
    <div class="row">
        <div class="col-md-3">
            <!--在base.html引入advertise.html文件的内容-->
            {% include "advertise.html" %}
        </div>
        <div class="col-md-9"></div>
    </div>
</div>
</body>
</html>

  

5.2、模板的继承\派生之extends标签、block标签

#作用:在一个模板文件中,引入/重用另外一个模板文件的内容
{% extends "模版名称" %}
#  也就是说include有的功能extends全都有,但是extends可以搭配一个block标签,用于在继承的基础上定制新的内容

  

案例

Django模版引擎中最复杂且最强大的部分就是模版继承了。我们以先创建一个基本的“骨架”模版,它包含我们站点中的全部元素,并且可以定义多处blocks ,例如我们创建base.html内容如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>
        {% block title %}自定义title名{% endblock %}
    </title>

    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .header {
            height: 50px;
            width: 100%;
            background-color: #919191;
            margin-bottom: 20px;
        }

    </style>

</head>
<body>
<div class="header"></div>

<div class="container">
    <div class="row">
        <div class="col-md-3">
            <div class="list-group">
                {% block sidebar %}
                    <a href="#" class="list-group-item active">服装城</a>
                    <a href="#" class="list-group-item">美妆馆</a>
                    <a href="#" class="list-group-item">超市</a>
                    <a href="#" class="list-group-item">全球购</a>
                    <a href="#" class="list-group-item">闪购</a>
                    <a href="#" class="list-group-item">团购</a>
                {% endblock %}

            </div>
        </div>

        <div class="col-md-9">
            {% block content %}
                base.html页面内容
            {% endblock %}
        </div>
    </div>

</div>

</body>
</html>

  

模板base.html 定义了一个可以用于两列排版页面的简单HTML骨架。我们新建子模板index.html的主要工作就是继承base.html然后填充/覆盖其内部的blocks。

{% extends "base.html" %}

<!--用新内容完全覆盖了父模板内容-->
{% block title %}
    index页面
{% endblock %}


{% block sidebar %}
    <!--该变量会将父模板中sidebar中原来的内容继承过来,然后我们可以在此基础上新增,否则就是纯粹地覆盖-->
    {{ block.super }}

    <!--在继承父模板内容的基础上新增的标签-->
    <a href="#" class="list-group-item">拍卖</a>
    <a href="#" class="list-group-item">金融</a>
{% endblock %}

{% block content %}
    <!--用新内容完全覆盖了父模板内容-->
    <p>index页面内容</p>
{% endblock %}

  

我们通过django访问index.html看到内容如下(block标签的内容都完成了替换或更新)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>
        index页面
    </title>

    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .header {
            height: 50px;
            width: 100%;
            background-color: #919191;
            margin-bottom: 20px;
        }

    </style>

</head>
<body>
<div class="header"></div>

<div class="container">
    <div class="row">
        <div class="col-md-3">
            <div class="list-group">
                <!--该变量会将父模板中sidebar中原来的内容继承过来,然后我们可以在此基础上新增,否则就是纯粹地覆盖-->
                <a href="#" class="list-group-item active">服装城</a>
                <a href="#" class="list-group-item">美妆馆</a>
                <a href="#" class="list-group-item">超市</a>
                <a href="#" class="list-group-item">全球购</a>
                <a href="#" class="list-group-item">闪购</a>
                <a href="#" class="list-group-item">团购</a>


                <!--在继承父模板内容的基础上新增的标签-->
                <a href="#" class="list-group-item">拍卖</a>
                <a href="#" class="list-group-item">金融</a>
            </div>
        </div>

        <div class="col-md-9">
            <!--用新内容完全覆盖了父模板内容-->
            <p>index页面内容</p>
        </div>
    </div>

</div>

</body>
</html>

  

总结和注意

#1、标签extends必须放在首行,base.html中block越多可定制性越强

#2、include仅仅只是完全引用其他模板文件,而extends却可以搭配block在引用的基础上进行扩写

#3、变量{{ block.super }} 可以重用父类的内容,然后在父类基础上增加新内容,而不是完全覆盖

#4、为了提升可读性,我们可以给标签{% endblock %} 起一个名字 。例如:
    {% block content %}
    ...
    {% endblock content %}  
#5、在一个模版中不能出现重名的block标签。

  

7 静态文件配置和前后台交互

我们在编写模板文件时,需要大量引用css、js、图片等静态文件,如果我们将这些文件在服务端存放的路径都固定写死那么将非常不利于后期的扩展,我们可以这么做

1、settings.py

STATIC_URL = '/static/' # 找到这一行,然后新增下述代码
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'statics'),# 获取静态文件在服务端的绝对路径
]
#STATIC_URL = '/static/'就是为静态文件的绝对路径起了一个别名,以后我们只需要用路径/static/即可

  

2、在项目根目录下新增文件夹statics,为了更便于管理,可以在statics下新建子文件夹css、js、img等

 

 

 

3、新建模板文件index.html,在该文件中对静态文件的引用如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/css/my.css">
</head>
<body>
<h4>我是红色的,点我就绿</h4>
<img src="/static/img/rb.jpeg" alt="">


<script src="/static/js/jquery-3.3.1.min.js"></script>
<script src="/static/js/my.js"></script>

</body>
</html>

  

前台发给后台的方式

 

request: 用户所有的请求信息,视图函数参数必须要这个
前后台交互的数据必须使用json格式数据

### 前台发送给后台的方式
第一种方式:
前台HTML发送给后台的方式:(常见的是这两种)
    get(html不指定发送方式,默认是get)  post (常见的是这两种) 

    后台获取html请求数据  :   if request.method == 'GET':
                                    pass
                            if request.method == 'POST':
                                    pass

                    
第二种方式:
    前台ajax发送数据给后台
    前提是:有js和jQuery
            {#  这是模态框(弹出框)添加班级 #}
    $('#tijiao').click(function () {
        console.log($('#classid').val());
        $.ajax({
            {# 这三步是客户在弹出框输入内容之后 将输入的内容向后台提交  #}
            type: "POST",
            url : "/classesapp/modal_add_class/",
            data:  {"classname":$('#classname').val()},
            {# 这三步是客户在弹出框输入内容之后 将输入的内容向后台提交  #}

            success: function (data) {
                {#这是后台收到前台输入的信息之后返货给HTML的数据 也就会data #}
                if(data == 'ok'){
                    alert('添加成功!');
                    window.location.href = '/classesapp/classes/';   // 让浏览器啊界面返回这个classesapp/classes
                }else{
                    alert('添加失败!');
                    $('#error').text(data)
                }
            }
        })
    });

 
后台接收HTML的ajax的内容 以及传回给html的内容
 def modal_add_class(request):
    classname = request.POST.get('classname') # 接收html的值 

    if not classname:
        return HttpResponse('false') # 返回的信息
    else:
        import pymysql
        conn = pymysql.connect(host='127.0.0.1', user='root', password='', db='s858', charset='utf8')
        cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
        sql = 'insert into classes (name) values (%s)'
        cursor.execute(sql, (classname,))
        conn.commit()
        return HttpResponse('ok')  # 返回的信息            
 
 
第三种方式:                
前台HTML与后台交互的方式:  a标签或form表单  
                            
<a href="/classesapp/add_class/" type="button" class="btn btn-success" >增加班级</a>   # 代表点击增加班级的时候  会运转classesapp/add_class函数
 
<form action="/teacherapp/update_teacher/" method="post">
{#  update_teacher 走这update_teacher函数  #}
    <input type="hidden" name="teacherid" value="{{ teacherid }}">
    更新后的名称 <input type="text" name="teachername" value="{{ teachername }}">
    <input type="submit" value="更新">
</form>
 ##  表示 页面点击更新按钮时运行/teacherapp/update_teacher这个函数 
  
    
 第四种方式:
    widnow.location.href = '/classes/';  # 跳转到classes函数
	window.location.reload();  # 刷新当前页面
 
    
 后台正常接收数据
                

  

Django后台回应前台

                            
后台发送给html的方式:
    第一种:render:渲染页面  
	return  render(request,'classes.html',{"classes":classes})
	代表返回给渲染的是这个HTML: classes.html 和传递的数据是{"classes":classes},要注意的
{"classes":classes}这个数据是传给classes.html这个html页面的  前端可以在html页面中获取这个内容

如果仅仅是传递数据 return  render(request,{"classes":classes}),这个是报错的的 因为没有页面渲染
        
前台获取的后台发送的内容
	{% for item in classes %}
                <tr>
                    <td>{{ item.id }}</td>
                    <td>{{ item.name }}</td>
                    <td>
                        <a href="/classesapp/del_class/?id={{ item.id }}"  onclick="return xxxxx()" type="button" class="btn btn-danger">删除</a>
                        <a href="/classesapp/update_class/?id={{ item.id }}" type="button" class="btn btn-primary">更新</a>
                        <button  type="button" class="btn btn-success  ajax_class_modal">弹出框更新</button>
                    </td>
                </tr>
            {% endfor %}
            
            
    第二种:redirect()  重指向的意思
        return redirect('/classesapp/classes/')
    代表重新指向函数运行 由/classesapp/classes/函数去执行向前台输入信息等
   
    
    第三种方式: HttpResponse("xxx") 和 JsonResponse(")
        '''
        HttpResponse                                             
        后台返回的格式:
        res["code"] = 10000
        res['data'] = "success"
        return HttpResponse(json.dumps(res))
                                              
        前台js反序列化:
        // js反序列化:
            console.log(data);
            res = JSON.parse(data);
            
        前台js的序列化:
		JSON.stringify(res)
        '''
        
                                              
        JsonResponse(")
                                                     
        后台返回的格式:
        res["code"] = 10000
        res['data'] = "success"
        return JsonResponse((res))  ### 相当于使用了json.dumps()

        前台:
        console.log(data) ## data 已经反序列化好的字典
                     


 区别:
 HTTPResponse:
是由Django创造的,
他的返回格式为
HTTPResponse(content=响应体,content_type=响应体数据类型,status=状态码),
可以修改返回的数据类型,适用于返回图片,视频,音频等二进制文件。


 JsonResponse:
是HTTPResponse的子类,适用于处理json格式的数据,但是不能返回模板。
帮助我们将数据转换为json字符串
设置响应头Content-Type为application/json

  

即(HTML与py的数据格式)

前台:(html中的显示)
{{name}}

列表:
{% for item in mylist%}
{{item}}
{% endfor %}

{{mylist.0}}  # 取得列表的首位  直接点语法
{{mylist.1}}  # 取得列表的第二位


字典:
{% for key, val in mydict.items %}
{{key}} --- {{val}}
{% endfor %}

  

Django中HTML补充

1. css

如果想要绑定多个相同的标签的时候, 不要用id选择器, 要用class类别选择器

2. DOM  ===》 Document Object Modal

DOM0:
html:
<!--如果onclick是true的话,会执行跳转, 如果是False的 话, 阻止跳转-->
<a href="https://www.baidu.com" onclick="return del();">点击</a>
js:
function del(){
res = window.confirm('是否确认删除?');
return res; // true / false
}


DOM1:
html:
<a href="https://www.baidu.com" id="mya">点击</a>
js:
原生js:
var a = document.getElementById('mya') ;
// console.log(a);
a.onclick = function () {
res = window.confirm('是否确认删除?');
return res;
}

jquery:
$('#mya').click(fucntion(){
res = window.confirm('是否确认删除?');
return res;
})

  

 

  

posted @ 2019-03-10 18:08  犹豫就会拜佛  阅读(234)  评论(0编辑  收藏  举报