web框架篇--基础

1. 什么是框架

框架,即framework, 特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以帮你快速开发特定的系统,简单地说,就是你用别人搭建好的舞台来做表演。

对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端

1.浏览器发送一个http请求
2.服务器接收到请求,生成一个html文档
3.服务器把http文档作为http响应的body发送给浏览器
4.浏览器接收到http响应后,从http body中取出html文档进行展示

最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。

如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。
正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口,让我们专心用Python编写Web业务。

这个接口就是WSGI:Web Server Gateway Interface。

自己开发一个简单的框架
step 1 服务端雏形:

from wsgiref.simple_server import make_server


def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return [b'

Hello, web!

'
] httpd = make_server('127.0.0.1', 8888, application) print('Serving HTTP on port 8888...') # 开始监听HTTP请求: httpd.serve_forever()

注意

整个application()函数本身没有涉及到任何解析HTTP的部分,也就是说,底层代码不需要我们自己编写,
我们只负责在更高层次上考虑如何响应请求就可以了。

application()函数必须由WSGI服务器来调用。有很多符合WSGI规范的服务器,我们可以挑选一个来用。

Python内置了一个WSGI服务器,这个模块叫wsgiref    
    
    
application()函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数:

        //environ:一个包含所有HTTP请求信息的dict对象;
        
        //start_response:一个发送HTTP响应的函数。

在application()函数中,调用:

start_response('200 OK', [('Content-Type', 'text/html')])

就发送了HTTP响应的Header,注意Header只能发送一次,也就是只能调用一次start_response()函数。
start_response()函数接收两个参数,一个是HTTP响应码,一个是一组list表示的HTTP Header,每
个Header用一个包含两个str的tuple表示。

通常情况下,都应该把Content-Type头发送给浏览器。其他很多常用的HTTP Header也应该发送。

然后,函数的返回值b'

Hello, web!

'将作为HTTP响应的Body发送给浏览器。 有了WSGI,我们关心的就是如何从environ这个dict对象拿到HTTP请求信息,然后构造HTML, 通过start_response()发送Header,最后返回Body。

step 2 添加功能:

from wsgiref.simple_server import make_server

def f1():
    f1=open("index1.html","rb")
    data1=f1.read()
    return [data1]

def f2():
    f2=open("index2.html","rb")
    data2=f2.read()
    return [data2]

def application(environ, start_response):

    print(environ['PATH_INFO'])
    path=environ['PATH_INFO']
    start_response('200 OK', [('Content-Type', 'text/html')])


    if path=="/frankshi":
        return f1()

    elif path=="/shitou":
        return f2()

    else:
        return ["

404

"
.encode("utf8")] httpd = make_server('127.0.0.1', 8888, application) print('Serving HTTP on port 8084...') # 开始监听HTTP请求: httpd.serve_forever()

step3 再次优化

from wsgiref.simple_server import make_server


def f1(req):
    print(req)
    print(req["QUERY_STRING"])

    f1=open("index1.html","rb")
    data1=f1.read()
    return [data1]

def f2(req):
    print(req)
    print(req["QUERY_STRING"])
    f2=open("index2.html","rb")
    data2=f2.read()
    return [data2]

import time

def f3(req):        #模版以及数据库

    f3=open("index3.html","rb")
    data3=f3.read()
    times=time.strftime("%Y-%m-%d %X", time.localtime())
    data3=str(data3,"utf8").replace("time",str(times))


    return [data3.encode("utf8")]


def routers():

    urlpatterns = (
        ('/frankshi',f1),
        ('/shitou',f2),
        ("/cur_time",f3)
    )
    return urlpatterns


def application(environ, start_response):

    print(environ['PATH_INFO'])
    path=environ['PATH_INFO']
    start_response('200 OK', [('Content-Type', 'text/html')])


    urlpatterns = routers()
    func = None
    for item in urlpatterns:
        if item[0] == path:
            func = item[1]
            break
    if func:
        return func(environ)
    else:
        return ["

404

"
.encode("utf8")] httpd = make_server('127.0.0.1', 8888, application) print('Serving HTTP on port 8888...') # 开始监听HTTP请求: httpd.serve_forever()

2.MVC和MTV模式

著名的MVC模式:所谓MVC就是把web应用分为模型(M),控制器©,视图(V)三层;他们之间以一种插件似的,松耦合的方式连接在一起。

模型负责业务对象与数据库的对象(ORM),视图负责与用户的交互(页面),控制器©接受用户的输入调用模型和视图完成用户的请求。

Django的MTV模式本质上与MVC模式没有什么差别,也是各组件之间为了保持松耦合关系,只是定义上有些许不同,Django的MTV分别代表:

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

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

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

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

3. django的流程和命令行工具

3.1 django实现流程

django
    #安装: pip3 install django

          添加环境变量

    #1  创建project
       django-admin startproject mysite

       ---mysite

          ---settings.py //包含一些默认的设置
          ---urls.py   //负责把ur映射到应用程序
          ---wsgi.py   //服务端

       ---- manage.py(启动文件)  

    #2  创建APP       
       python mannage.py startapp  app01

    #3  settings配置
    
       TEMPLATES

       STATICFILES_DIRS=(
            os.path.join(BASE_DIR,"statics"),
        )

       STATIC_URL = '/static/' 
       #  我们只能用 STATIC_URL,但STATIC_URL会按着你的STATICFILES_DIRS去找#4  根据需求设计代码
           url.py
           view.py

    #5  使用模版
       render(req,"index.html")   

    #6  启动项目
       python manage.py runserver  127.0.0.1:8090

    #7  连接数据库,操作数据
       model.py

3.2 django的命令行工具

django-admin.py 是Django的一个用于管理任务的命令行工具,manage.py是对django-admin.py的简单包装,每一个Django Project里都会有一个mannage.py。

<1> 创建一个django工程 : django-admin.py startproject mysite

当前目录下会生成mysite的工程,目录结构如下:
manage.py ----- Django项目里面的工具,通过它可以调用django shell和数据库等。
settings.py ---- 包含了项目的默认设置,包括数据库信息,调试标志以及其他一些工作的变量。
urls.py ----- 负责把URL模式映射到应用程序。

<2>在mysite目录下创建blog应用: python manage.py startapp blog

<3>启动django项目:python manage.py runserver 8080
这样我们的django就启动起来了!当我们访问:http://127.0.0.1:8080/时就可以看到:

<4>生成同步数据库的脚本:python manage.py makemigrations

同步数据库: python manage.py migrate

  注意:在开发过程中,数据库同步误操作之后,难免会遇到后面不能同步成功的情况,解决这个问题的一个简单粗暴方法是把migrations目录下 的脚本(除__init__.py之外)全部删掉,再把数据库删掉之后创建一个新的数据库,数据库同步操作再重新做一遍。            

<5>当我们访问http://127.0.0.1:8080/admin/时,会出现:

所以我们需要为进入这个项目的后台创建超级管理员:python manage.py createsuperuser,设置好用户名和密码后便可登录啦!

<6>清空数据库:python manage.py flush

<7>查询某个命令的详细信息: django-admin.py help startapp
admin 是Django 自带的一个后台数据库管理系统。

<8>启动交互界面 :python manage.py shell

这个命令和直接运行 python 进入 shell 的区别是:你可以在这个 shell 里面调用当前项目的 models.py 中的 API,对于操作数据,还有一些小测试非常方便。

<9> 终端上输入python manage.py
可以看到详细的列表,在忘记子名称的时候特别有用。

实例1:前端提交数据并展示

框架
web框架篇--基础_第1张图片

  1. settings
    TEMPLATES
    //添加模板路径
    ‘DIRS’: [os.path.join(BASE_DIR,“templates”)],

  2. views, 添加提交信息接口

from django.shortcuts import render, HttpResponse
import time
from django.views.decorators.csrf import csrf_exempt


def view_current_time(req):
    print(req)
    now_time = time.strftime("%Y-%m-%d %X")
#     return HttpResponse(f"

{now}

")
# 渲染 return render(req, "current_time.html", {"now":now_time}) user_list = [] def view_userinfo(req): print(req.POST) # {'username': ['zhishang.shi'], 'sex': ['男'], 'age': ['34'], 'email': ['[email protected]']} if req.method == "POST": username = req.POST.get("username", None) sex = req.POST.get("sex", None) age = req.POST.get("age", None) email = req.POST.get("email", None) user = {'username': username, 'sex':sex , 'age': age, 'email': email} user_list.append(user) return render(req, "userinfo.html", {"user_list":user_list})
  1. urls 配置url
from django.contrib import admin
from django.conf.urls import url
from myapp import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^cur_time/', views.view_current_time),
    url(r'^userinfo/', views.view_userinfo),
]

  1. current_time.html

<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>current timetitle>
<body>

<h1>当前时间:{{now}}h1>
body>
html>
  1. userinfo.html 添加用户信息并展示

<html>
<head lang="en">
<meta charset="UTF-8">
<title>userinfotitle>
<body>
	
	<form action="/userinfo/" method="post">
		<p>
			姓名<input type="text" name="username">
		p>
		<p>
			性别<input type="text" name="sex">
		p>
		<p>
			年龄<input type="text" name="age">
		p>
		<p>
			邮箱<input type="text" name="email">
		p>
		<p>
			<input type="submit" value="submit">
		p>
	form>

	
	<h1>user listh1>
	<table border="1">
		<tr>
			<td>姓名td>
			<td>性别td>
			<td>年龄td>
			<td>邮箱td>
		tr>
		
		
		{% for user in user_list %}
		<tr>
			<td>{{ user.username }}td>
			<td>{{ user.sex }}td>
			<td>{{ user.age }}td>
			<td>{{ user.email }}td>
		tr>
		{% endfor %}
	table>
body>
html>
  1. 测试
    访问http://127.0.0.1:8090/userinfo/
    web框架篇--基础_第2张图片

实例练习2-提交数据并展示(数据库)

  1. settings
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myapp',  #必须添加自己的app名字
]
  1. models 创建表
from django.db import models

# Create your models here.
class UserInfo(models.Model):
    id = models.AutoField(primary_key=True)
    username = models.CharField(max_length=50)
    sex = models.CharField(max_length=50)
    age = models.CharField(max_length=50)
    email = models.CharField(max_length=50)
    
    
  1. view.py
from django.shortcuts import render, HttpResponse
import time
from django.views.decorators.csrf import csrf_exempt
from myapp import models

def view_current_time(req):
    print(req)
    now_time = time.strftime("%Y-%m-%d %X")
#     return HttpResponse(f"

{now}

")
# 渲染 return render(req, "current_time.html", {"now":now_time}) def view_userinfo(req): print(req.POST) # {'username': ['zhishang.shi'], 'sex': ['男'], 'age': ['34'], 'email': ['[email protected]']} if req.method == "POST": u = req.POST.get("username", None) s = req.POST.get("sex", None) a = req.POST.get("age", None) e = req.POST.get("email", None) #创建用户,并插入到数据库的表中 models.UserInfo.objects.create( username=u, sex=s, age=a, email=e ) user_list = models.UserInfo.objects.all() return render(req, "userinfo.html", {"user_list":user_list})
  1. userinfo.html

<html>
<head lang="en">
<meta charset="UTF-8">
<title>userinfotitle>
<body>
	
	<form action="/userinfo/" method="post">
		<p>
			姓名<input type="text" name="username">
		p>
		<p>
			性别<input type="text" name="sex">
		p>
		<p>
			年龄<input type="text" name="age">
		p>
		<p>
			邮箱<input type="text" name="email">
		p>
		<p>
			<input type="submit" value="submit">
		p>
	form>

	
	<h1>user listh1>
	<table border="1">
		<tr>
			<td>IDtd>
			<td>姓名td>
			<td>性别td>
			<td>年龄td>
			<td>邮箱td>
		tr>
		
		
		{% for user in user_list %}
		<tr>
			<td>{{ user.id }}td>
			<td>{{ user.username }}td>
			<td>{{ user.sex }}td>
			<td>{{ user.age }}td>
			<td>{{ user.email }}td>
		tr>
		{% endfor %}
	table>
body>
html>
  1. 关闭django服务,然后重启
    生成同步数据库的脚本:python manage.py makemigrations
    同步数据库: python manage.py migrate
    重启:python manage.py runserver 8090

测试:
web框架篇--基础_第3张图片

3.3 Django的配置文件(settings)

静态文件设置

一、概述:

     #静态文件交由Web服务器处理,Django本身不处理静态文件。简单的处理逻辑如下(以nginx为例):

     #          URI请求-----> 按照Web服务器里面的配置规则先处理,以nginx为例,主要求配置在nginx.
                             #conf里的location

                         |---------->如果是静态文件,则由nginx直接处理

                         |---------->如果不是则交由Django处理,Django根据urls.py里面的规则进行匹配

    # 以上是部署到Web服务器后的处理方式,为了便于开发,Django提供了在开发环境的对静态文件的处理机制,方法是这样:

    #1、在INSTALLED_APPS里面加入'django.contrib.staticfiles',

    #2、在urls.py里面加入
       if settings.DEBUG:  
           urlpatterns += patterns('', url(r'^media/(?P.*)$', 
           'django.views.static.serve', {'document_root': settings.MEDIA_ROOT }),   
            url(r'^static/(?P.*)$',
          'django.views.static.serve',{'document_root':settings.STATIC_ROOT}), )  

    # 3、这样就可以在开发阶段直接使用静态文件了。

二、MEDIA_ROOT和MEDIA_URL

        #而静态文件的处理又包括STATIC和MEDIA两类,这往往容易混淆,在Django里面是这样定义的:

        #MEDIA:指用户上传的文件,比如在Model里面的FileFIeld,ImageField上传的文件。如果你定义

        #MEDIA_ROOT=c:\temp\media,那么File=models.FileField(upload_to="abc/")#,上传的文件就会被保存到c:\temp\media\abc  
        #eg:
            class blog(models.Model):  
                   Title=models.charField(max_length=64)  
                   Photo=models.ImageField(upload_to="photo") 
        #     上传的图片就上传到c:\temp\media\photo,而在模板中要显示该文件,则在这样写
        #在settings里面设置的MEDIA_ROOT必须是本地路径的绝对路径,一般是这样写:
                 BASE_DIR= os.path.abspath(os.path.dirname(__file__))  
                 MEDIA_ROOT=os.path.join(BASE_DIR,'media/').replace('\\','/') 

        #MEDIA_URL是指从浏览器访问时的地址前缀,举个例子:
            MEDIA_ROOT=c:\temp\media\photo  
            MEDIA_URL="/data/"
        #在开发阶段,media的处理由django处理:

        #    访问http://localhost/data/abc/a.png就是访问c:\temp\media\photo\abc\a.png

        #    在模板里面这样写

        #    在部署阶段最大的不同在于你必须让web服务器来处理media文件,因此你必须在web服务器中配置,
        #  以便能让web服务器能访问media文件
        #    以nginx为例,可以在nginx.conf里面这样:

                 location ~/media/{
                       root/temp/
                       break;
                    }

        #    具体可以参考如何在nginx部署django的资料。

三、STATIC_ROOT和STATIC_URL、
    STATIC主要指的是如css,js,images这样文件,在settings里面可以配置STATIC_ROOT和STATIC_URL,
    配置方式与MEDIA_ROOT是一样的,但是要注意

    #STATIC文件一般保存在以下位置:

    #1、STATIC_ROOT:在settings里面设置,一般用来放一些公共的js,css,images等。

    #2、app的static文件夹,在每个app所在文夹均可以建立一个static文件夹,然后当运行collectstatic时,
    #    Django会遍历INSTALL_APPS里面所有app的static文件夹,将里面所有的文件复制到STATIC_ROOT。因此,
    #   如果你要建立可复用的app,那么你要将该app所需要的静态文件放在static文件夹中。

    # 也就是说一个项目引用了很多app,那么这个项目所需要的css,images等静态文件是分散在各个app的static文件的,比
    #  较典型的是admin应用。当你要发布时,需要将这些分散的static文件收集到一个地方就是STATIC_ROOT。

    #3、STATIC文件还可以配置STATICFILES_DIRS,指定额外的静态文件存储位置。
    #  STATIC_URL的含义与MEDIA_URL类似。

    # ----------------------------------------------------------------------------
    #注意1:
        #为了后端的更改不会影响前端的引入,避免造成前端大量修改

        STATIC_URL = '/static/'               #引用名
        STATICFILES_DIRS = (
            os.path.join(BASE_DIR,"statics")  #实际名 ,即实际文件夹的名字
        )

        #django对引用名和实际名进行映射,引用时,只能按照引用名来,不能按实际名去找
        #
        #------error-----不能直接用,必须用STATIC_URL = '/static/':
        #

    #注意2(statics文件夹写在不同的app下,静态文件的调用):

        STATIC_URL = '/static/'

        STATICFILES_DIRS=(
            ('hello',os.path.join(BASE_DIR,"app01","statics")) ,
        )

        #

    #注意3:
        STATIC_URL = '/static/'
        {% load staticfiles %}
       # 

3.4 Django URL (路由系统)

URL配置(URLconf)就像Django 所支撑网站的目录。它的本质是URL模式以及要为该URL模式调用的视图函数之间的映射表;你就是以这种方式告诉Django,对于这个URL调用这段代码,对于那个URL调用那段代码。

3.4.1 一般正则匹配

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

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

没有name样例:
urls.py

from django.contrib import admin
from django.conf.urls import url
from myapp import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^cur_time/', views.view_current_time),
    url(r'^userinfo/', views.view_userinfo),
    #^:以字符串开头, $:以字符串结尾
    url(r'^articles/2003/$', views.view_current_time),
    #以articles开头,接下来是4个数字
    #url(r'^articles/[0-9]{4}/$', views.view_current_time),
    #以articles开头,接下来是4个数字,并将数字作为视图函数的参数
    #url(r'^articles/([0-9]{4})/$', views.view_params),
    #以articles开头,接下来是4个数字,和2个数字,并将数字作为视图函数的参数
    #url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.view_two_params),

    url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.view_thread_params),
]

view.py

from django.shortcuts import render, HttpResponse
import time
from django.views.decorators.csrf import csrf_exempt
from myapp import models

def view_current_time(req):
    now_time = time.strftime("%Y-%m-%d %X")
#     return HttpResponse(f"

{now}

")
# 渲染 return render(req, "current_time.html", {"now":now_time}) def view_userinfo(req): print(req.POST) # {'username': ['zhishang.shi'], 'sex': ['男'], 'age': ['34'], 'email': ['[email protected]']} if req.method == "POST": u = req.POST.get("username", None) s = req.POST.get("sex", None) a = req.POST.get("age", None) e = req.POST.get("email", None) models.UserInfo.objects.create( username=u, sex=s, age=a, email=e ) user_list = models.UserInfo.objects.all() return render(req, "userinfo.html", {"user_list":user_list}) def view_params(req, param): return HttpResponse(param) def view_two_params(req, param1, param2): return HttpResponse(param1+param2) def view_thread_params(req, param1, param2, param3): return HttpResponse(param1+param2+param3)

3.4.2 使用name

from django.test import TestCase

# Create your tests here.

import re

ret=re.search('(?P\d{3})/(?P\w{3})','weeew34ttt123/ooo')

print(ret.group())
print(ret.group('id'))
print(ret.group('name'))
# 123/ooo
# 123
# ooo

urls.py

from django.contrib import admin
from django.conf.urls import url
from myapp import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^cur_time/', views.view_current_time),
    url(r'^userinfo/', views.view_userinfo),

    url(r'^articles/2003/$', views.view_current_time),
    #url(r'^articles/(?P[0-9]{4})/$', views.view_params),
    #url(r'^articles/(?P[0-9]{4})/(?P[0-9]{2})/$', views.view_two_params),
    url(r'^articles/(?P[0-9]{4})/(?P[0-9]{2})/(?P[0-9]{2})/$', views.view_three_params),
]

view.py

from django.shortcuts import render, HttpResponse
import time
from django.views.decorators.csrf import csrf_exempt
from myapp import models

def view_current_time(req):
    now_time = time.strftime("%Y-%m-%d %X")
#     return HttpResponse(f"

{now}

")
# 渲染 return render(req, "current_time.html", {"now":now_time}) def view_userinfo(req): print(req.POST) # {'username': ['zhishang.shi'], 'sex': ['男'], 'age': ['34'], 'email': ['[email protected]']} if req.method == "POST": u = req.POST.get("username", None) s = req.POST.get("sex", None) a = req.POST.get("age", None) e = req.POST.get("email", None) models.UserInfo.objects.create( username=u, sex=s, age=a, email=e ) user_list = models.UserInfo.objects.all() return render(req, "userinfo.html", {"user_list":user_list}) def view_params(req, year): return HttpResponse(year) def view_two_params(req, month, year): return HttpResponse(month+year) def view_three_params(req, year, month, day): return HttpResponse(year+month+day)

3.4.3 传参

urls.py

from django.contrib import admin
from django.conf.urls import url
from myapp import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^cur_time/', views.view_current_time),
    url(r'^userinfo/', views.view_userinfo),

    #固定参数
    url(r'^articles', views.index, {"name":"frankshi"}),
    
]

3.4.4 url别名

urls.py

from django.contrib import admin
from django.conf.urls import url
from myapp import views
urlpatterns = [
    url(r'^index',views.index,name='bieming'),
    url(r'^admin/', admin.site.urls),
    
]

view.py

def index(req):
    if req.method=='POST':
        username=req.POST.get('username')
        password=req.POST.get('password')
        if username=='shitou' and password=='123':
            return HttpResponse("登陆成功")

    return render(req,'index.html')

index.html


<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
	<form action="{% url 'bieming' %}" method="post">
		<p>用户名:<input type="text" name="username">p>
		<p>密码:<input type="password" name="password">p>
		 <input type="submit" value="submit">
	form>
body>
html>

3.4.4 url分发(include)

如果存在多个应用,各自都有自己对应的url, 则需要分发管理

mysite下的urls.py

from django.contrib import admin
from django.conf.urls import url,include
from myapp import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^cur_time/', views.view_current_time),
    #路由分发
    url(r'^myapp/', include("myapp.urls")),
]

myapp下面自己的urls.py

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

urlpatterns = [
    url(r'^userinfo/', views.view_userinfo),
]

测试
web框架篇--基础_第4张图片

3.5 视图views(业务)

http请求中产生两个核心对象:

    http请求:HttpRequest对象

    http响应:HttpResponse对象

所在位置:django.http (from django.http import HttpRequest)

之前我们用到的参数request就是HttpRequest, 检测方法:isinstance(request,HttpRequest)

3.5.1 HttpRequest对象的属性和方法

# path:       请求页面的全路径,不包括域名
#
# method:     请求中使用的HTTP方法的字符串表示。全大写表示。例如
#
#                    if  req.method=="GET":
#
#                              do_something()
#
#                    elseif req.method=="POST":
#
#                              do_something_else()
#
# GET:         包含所有HTTP GET参数的类字典对象
#
# POST:       包含所有HTTP POST参数的类字典对象
#
#              服务器收到空的POST请求的情况也是可能发生的,也就是说,表单form通过
#              HTTP POST方法提交请求,但是表单中可能没有数据,因此不能使用
#              if req.POST来判断是否使用了HTTP POST 方法;应该使用  if req.method=="POST"
#
#
#
# COOKIES:     包含所有cookies的标准Python字典对象;keys和values都是字符串。
#
# FILES:      包含所有上传文件的类字典对象;FILES中的每一个Key都是标签中                     name属性的值,FILES中的每一个value同时也是一个标准的python字典对象,包含下面三个Keys:
#
#             filename:      上传文件名,用字符串表示
#             content_type:   上传文件的Content Type
#             content:       上传文件的原始内容
#
#
# user:       是一个django.contrib.auth.models.User对象,代表当前登陆的用户。如果访问用户当前
#              没有登陆,user将被初始化为django.contrib.auth.models.AnonymousUser的实例。你
#              可以通过user的is_authenticated()方法来辨别用户是否登陆:
#              if req.user.is_authenticated();只有激活Django中的AuthenticationMiddleware
#              时该属性才可用
#
# session:    唯一可读写的属性,代表当前会话的字典对象;自己有激活Django中的session支持时该属性才可用。

#方法
#http://127.0.0.1:8090/cur_time?city=xian
req.get_full_path()  得到的结果就是/cur_time?city=xian
req.path:/cur_time
#注意一个常用方法:request.POST.getlist('')

实例
viws.py

def view_userinfo(req):
    print(req.FILES)
    #获取文件名:
    filename = req.FILES["filename"].name
    print(filename)
    #获取文件内容:
    with open(filename, "wb"):
        for i in req.FILES["filename"].chunks():
            print(str(i, encoding = "utf-8")  )

    return render(req, "userinfo.html")

userinfo.html

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>userinfo</title>
<body>
	<!-- 提交表单 -->
	<form action="/userinfo/" method="post" enctype="multipart/form-data">
		<p>
			<input type="file" name="filename">
		</p>
		<p>
			<input type="submit" value="submit">
		</p>
	</form>
</body>
</html>

3.5.2 HttpResponse

对于HttpRequest对象来说,是由django自动创建的,但是,HttpResponse对象就必须我们自己创建。每个view请求处理方法必须返回一个HttpResponse对象。

HttpResponse类在django.http.HttpResponse

在HttpResponse对象上扩展的常用方法:

页面渲染:
render()(推荐
render_to_response(),
页面跳转(重定向):
redirect(“路径”)
locals(): 可以直接将函数中所有的变量传给模板

locals()实例
view.py

def view_userinfo(req):

    name = "frankshi"
    sex = "男"
    age = 18
    return render(req, "userinfo.html", locals())

userinfo.html


<html>
<head lang="en">
<meta charset="UTF-8">
<title>userinfotitle>
<body>
	<h1>user listh1>
	<table border="1">
		<tr>
			<td>姓名td>
			<td>性别td>
			<td>年龄td>
		tr>
		<tr>
			<td>{{ name }}td>
			<td>{{ sex }}td>
			<td>{{ age }}td>
		tr>

body>
html>

测试:
web框架篇--基础_第5张图片

页面跳转redirect实例1
view.py

def view_userinfo(req):
    print("重定向")
    
    return redirect("https://www.baidu.com/")

测试:
1.访问http://127.0.0.1:8090/userinfo/
2.页面会自动跳转到https://www.baidu.com/

页面跳转redirect实例登录跳转
urls.py

from django.contrib import admin
from django.conf.urls import url,include
from myapp import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r"^login/$",   views.login),
]

]

views.py

from django.shortcuts import render, HttpResponse, redirect
import time
from myapp import models

def login(req):
    if req.method=="POST":
        if ((req.POST["username"]=="frankshi") and (req.POST["password"]=="123456")):
            return render(req,"home.html",locals())
        else:
            return redirect("/login/")
    return render(req,"login.html",locals())

login.html


<html lang="en">
<head>
<meta charset="UTF-8">
<title>Logintitle>
head>
<body>
<form action="/login/" method="post">
    <p>username<input type="text" name="username">p>
    <p>password<input type="text" name="password">p>
    <p><input type="submit" value="submit">p>
form>
body>
html>

home.html


<html lang="en">
<head>
<meta charset="UTF-8">
<title>主页面title>
head>
<body>
<h1>你好,欢迎来到我的博客h1>
body>
html>

3.6 模板:Template基础

3.6.1 模板系统的介绍

基础视图
HTML被直接硬编码在 Python代码之中。

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

这种方式便于解释视图是如何工作的,但是并不是个好主意, 没有实现前后端分离:

对页面设计进行的任何改变都必须对 Python 代码进行相应的修改。 站点设计的修改往往比底层 Python 代码的修改要频繁得多,因此如果可以在不进行 Python 代码修改的情况下变更设计,那将会方便得多。

Python 代码编写和 HTML 设计是两项不同的工作,大多数专业的网站开发环境都将他们分配给不同的人员(甚至不同部门)来完成。 设计者和HTML/CSS的编码人员不应该被要求去编辑Python的代码来完成他们的工作。

程序员编写 Python代码和设计人员制作模板两项工作同时进行的效率是最高的,远胜于让一个人等待另一个人完成对某个既包含 Python又包含 HTML 的文件的编辑工作。

基于这些原因,将页面的设计和Python的代码分离开会更干净简洁更容易维护。 我们可以使用 Django的 模板系统 (Template System)来实现这种模式。

3.6.2 模板语法

3.6.2.1 模板组成

组成:HTML代码+逻辑控制代码

3.6.2.2 逻辑控制代码的组成
  1. 变量(使用双大括号来引用变量)
    语法格式: {{var_name}}

Template和Context对象

>>> python manange.py shell  (进入该django项目的环境)
>>> from django.template import Context, Template
>>> t = Template('My name is {{ name }}.')
>>> c = Context({'name': 'Stephane'})
>>> t.render(c)
'My name is Stephane.'


# 同一模板,多个上下文,一旦有了模板对象,你就可以通过它渲染多个context,无论何时我们都可以
# 像这样使用同一模板源渲染多个context,只进行 一次模板创建然后多次调用render()方法渲染会
# 更为高效:
# Low
for name in ('John', 'Julie', 'Pat'):
    t = Template('Hello, {{ name }}')
    print t.render(Context({'name': name}))

# Good
t = Template('Hello, {{ name }}')
for name in ('John', 'Julie', 'Pat'):
    print t.render(Context({'name': name}))

模板使用方式

from django.shortcuts import render,HttpResponse
from django.template.loader import get_template #记得导入
# Create your views here.


import datetime
from django.template import Template,Context

# def current_time(req):
    #原始的视图函数
    # now=datetime.datetime.now()
    # html="现在时刻:

%s.

" %now
# return HttpResponse(html) # def current_time(req): #django模板修改的视图函数 # now=datetime.datetime.now() # t=Template('现在时刻是:

{{current_date}}

')
#t=get_template('current_datetime.html') # c=Context({'current_date':now}) # html=t.render(c) # return HttpResponse(html) #另一种写法(推荐) def current_time(req): now=datetime.datetime.now() return render(req, 'current_datetime.html', {'current_date':now})

深度变量的查找
在到目前为止的例子中,我们通过 context 传递的简单参数值主要是字符串,然而,模板系统能够非常简洁地处理更加复杂的数据结构,例如list、dictionary和自定义的对象。

在 Django 模板中遍历复杂数据结构的关键是句点字符 (.)。

#最好是用几个例子来说明一下。
# 首先,句点可用于访问列表索引,例如:

>>> from django.template import Template, Context
>>> t = Template('Item 2 is {{ items.2 }}.')
>>> c = Context({'items': ['apples', 'bananas', 'carrots']})
>>> t.render(c)
'Item 2 is carrots.'

#假设你要向模板传递一个 Python 字典。 要通过字典键访问该字典的值,可使用一个句点:
>>> from django.template import Template, Context
>>> person = {'name': 'Sally', 'age': '43'}
>>> t = Template('{{ person.name }} is {{ person.age }} years old.')
>>> c = Context({'person': person})
>>> t.render(c)
'Sally is 43 years old.'

#同样,也可以通过句点来访问对象的属性。 比方说, Python 的 datetime.date 对象有
#year 、 month 和 day 几个属性,你同样可以在模板中使用句点来访问这些属性:

>>> from django.template import Template, Context
>>> import datetime
>>> d = datetime.date(1993, 5, 2)
>>> d.year
1993
>>> d.month
5
>>> d.day
2
>>> t = Template('The month is {{ date.month }} and the year is {{ date.year }}.')
>>> c = Context({'date': d})
>>> t.render(c)
'The month is 5 and the year is 1993.'

# 这个例子使用了一个自定义的类,演示了通过实例变量加一点(dots)来访问它的属性,这个方法适
# 用于任意的对象。
>>> from django.template import Template, Context
>>> class Person(object):
...     def __init__(self, first_name, last_name):
...         self.first_name, self.last_name = first_name, last_name
>>> t = Template('Hello, {{ person.first_name }} {{ person.last_name }}.')
>>> c = Context({'person': Person('John', 'Smith')})
>>> t.render(c)
'Hello, John Smith.'

# 点语法也可以用来引用对象的方法。 例如,每个 Python 字符串都有 upper() 和 isdigit()
# 方法,你在模板中可以使用同样的句点语法来调用它们:
>>> from django.template import Template, Context
>>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}')
>>> t.render(Context({'var': 'hello'}))
'hello -- HELLO -- False'
>>> t.render(Context({'var': '123'}))
'123 -- 123 -- True'

变量的过滤器(filter)的使用

语法格式: {{obj|filter:param}}
使用

# 1  add          :   给变量加上相应的值
   #
   # 2  addslashes   :    给变量中的引号前加上斜线
   #
   # 3  capfirst     :    首字母大写
   #
   # 4  cut          :   从字符串中移除指定的字符
   #
   # 5  date         :   格式化日期字符串
   #
   # 6  default      :   如果值是False,就替换成设置的默认值,否则就是用本来的值
   #
   # 7  default_if_none:  如果值是None,就替换成设置的默认值,否则就使用本来的值


#实例:

#value1="aBcDe"
{{ value1|upper }}<br>

#value2=5
{{ value2|add:3 }}<br>

#value3='he  llo wo r ld'
{{ value3|cut:' ' }}<br>

#import datetime
#value4=datetime.datetime.now()
{{ value4|date:'Y-m-d' }}<br>

#value5=[]
{{ value5|default:'空的' }}<br>

#value6='跳转'

{{ value6 }}

{% autoescape off %}
  {{ value6 }}
{% endautoescape %}

{{ value6|safe }}<br>

{{ value6|striptags }}

#value7='1234'
{{ value7|filesizeformat }}<br>
{{ value7|first }}<br>
{{ value7|length }}<br>
{{ value7|slice:":-1" }}<br>

#value8='http://www.baidu.com/?a=1&b=3'
{{ value8|urlencode }}<br>
    value9='hello I am yuan'

标签(tag)的使用
语法:{% tags %}

1.{% if %} 的使用

{% if num >= 100 and 8 %}

    {% if num > 200 %}
        <p>num大于200</p>
    {% else %}
        <p>num大于100小于200</p>
    {% endif %}

{% elif num < 100%}
    <p>num小于100</p>

{% else %}
    <p>num等于100</p>

{% endif %}



{% if %} 标签接受andor或者not来测试多个变量值或者否定一个给定的变量
{% if %} 标签不允许同一标签里同时出现andor,否则逻辑容易产生歧义,例如下面的标签是不合法的:

{% if obj1 and obj2 or obj3 %}

2.{% for %}的使用

<ul>
{% for obj in list %}
    <li>{{ obj.name }}</li>
{% endfor %}
</ul>


#在标签里添加reversed来反序循环列表:

    {% for obj in list reversed %}
    ...
    {% endfor %}

#{% for %}标签可以嵌套:

    {% for country in countries %}
        <h1>{{ country.name }}</h1>
        <ul>
         {% for city in country.city_list %}
            <li>{{ city }}</li>
         {% endfor %}
        </ul>
    {% endfor %}


#系统不支持中断循环,系统也不支持continue语句,{% for %}标签内置了一个forloop模板变量,
#这个变量含有一些属性可以提供给你一些关于循环的信息

1,forloop.counter表示循环的次数,它从1开始计数,第一次循环设为1:

    {% for item in todo_list %}
        <p>{{ forloop.counter }}: {{ item }}</p>
    {% endfor %}
2,forloop.counter0 类似于forloop.counter,但它是从0开始计数,第一次循环设为0
3,forloop.revcounter 统计剩余元素个数
4,forloop.revcounter0 
5,forloop.first当第一次循环时值为True,在特别情况下很有用:

    
    {% for object in objects %}   
         {% if forloop.first %}<li class="first">{% else %}<li>{% endif %}   
         {{ object }}   
        </li>  
    {% endfor %}  
    
# 富有魔力的forloop变量只能在循环中得到,当模板解析器到达{% endfor %}时forloop就消失了
# 如果你的模板context已经包含一个叫forloop的变量,Django会用{% for %}标签替代它
# Django会在for标签的块中覆盖你定义的forloop变量的值
# 在其他非循环的地方,你的forloop变量仍然可用


#{% empty %}

{{li }}
      {%  for i in li %}
          <li>{{ forloop.counter0 }}----{{ i }}</li>
      {% empty %}
          <li>this is empty!</li>
      {% endfor %}

#         [11, 22, 33, 44, 55]
#            0----11
#            1----22
#            2----33
#            3----44
#            4----55

3.{%csrf_token%}:csrf_token标签
用于生成csrf_token的标签,用于防治跨站攻击验证。注意如果你在view的index里用的是render_to_response方法,不会生效
其实,这里是会生成一个input标签,和其他表单标签一起提交给后台的。
login.html


<html lang="en">
<head>
<meta charset="UTF-8">
<title>Logintitle>
head>
<body>
	<form action="/login/" method="post">

		<p>
			username<input type="text" name="username">
		p>
		<p>
			password<input type="text" name="password">
		p>
		<p>
			<input type="submit" value="submit">
		p>
		{%csrf_token%}
	form>
body>
html>

web框架篇--基础_第6张图片
4.{% url %}: 引用路由配置的地址

<input type="text"> {%csrf_token%} form>

5.{% with %}:用更简单的变量名替代复杂的变量名

{% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %}

6.{% verbatim %}: 禁止render

{% verbatim %}
         {{ hello }}
{% endverbatim %}

7.自定义filter和simple_tag
a、在app中创建templatetags模块(必须的)
b、创建任意 .py 文件,如:my_tags.py

from django import template
from django.utils.safestring import mark_safe

register = template.Library()  # register的名字是固定的,不可改变

@register.simple_tag
def add100(num):
    return num + 100

@register.filter
def filter_multi(v1, v2):
    return  v1 * v2


@register.simple_tag
def simple_tag_multi(v1, v2):
    return  v1 * v2


@register.simple_tag
def my_input(id, arg):
    result = "" % (id, arg,)
    return mark_safe(result)

c、在使用自定义simple_tag和filter的html文件中导入之前创建的 my_tags.py :{% load my_tags %}

d、使用simple_tag和filter(如何调用)

-------------------------------.html
{% load xxx %}   #首行
    
    
    
    
 # num=12
{{ num|filter_multi:2 }} #24

{{ num|filter_multi:"[22,333,4444]" }}


{% simple_tag_multi 2 5 %}  参数不限,但不能放在if for语句中
{% simple_tag_multi num 5 %}

调用

{% load my_tags %}

<html lang="en">
<head>
<meta charset="UTF-8">
<title>Logintitle>
head>
<body>
	<form action="/login/" method="post">
	{% add100 a %}
	{% simple_tag_multi a b %}
	{{ a|filter_multi:b }}
	form>
body>
html>

e、在settings中的INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag.

注意:
filter可以用在if等语句后,simple_tag不可以

8.4 extend模板继承
include 模板标签

在讲解了模板加载机制之后,我们再介绍一个利用该机制的内建模板标签: {% include %} 。该标签允许在(模板中)包含其它的模板的内容。 标签的参数是所要包含的模板名称,可以是一个变量,也可以是用单/双引号硬编码的字符串。 每当在多个模板中出现相同的代码时,就应该考虑是否要使用 {% include %} 来减少重复。

extend(继承)模板标签

到目前为止,我们的模板范例都只是些零星的 HTML 片段,但在实际应用中,你将用 Django 模板系统来创建整个 HTML 页面。 这就带来一个常见的 Web 开发问题: 在整个网站中,如何减少共用页面区域(比如站点导航)所引起的重复和冗余代码?

解决该问题的传统做法是使用 服务器端的 includes ,你可以在 HTML 页面中使用该指令将一个网页嵌入到另一个中。 事实上, Django 通过刚才讲述的 {% include %} 支持了这种方法。 但是用 Django 解决此类问题的首选方法是使用更加优雅的策略—— 模板继承 。

本质上来说,模板继承就是先构造一个基础框架模板,而后在其子模板中对它所包含站点公用部分和定义块进行重载。

两个页面:

ordered.html


<html lang="en">
<head>
<meta charset="UTF-8">
<title>购物title>
<style>
.page-header {
	height: 100px;
	background-color: blue;
}

.page-body .menu {
	height: 600px;
	background-color: green;
	float: left;
	width: 20%;
}

.page-body .content {
	height: 600px;
	background-color: gray;
	float: left;
	width: 80%;
}

.page-footer {
	height: 80px;
	background-color: yellow;
	clear: both;
}
style>
head>
<body>
	<div>
		<div class="page-header">div>
		<div class="page-body">
			<div class="menu">
				<p>
					<a href="/ordered/">订单a>
				p>
				<p>
					<a href="/shopping_car/">购物车a>
				p>
			div>
			<div class="content">订单div>

		div>
		<div class="page-footer">div>
	div>

body>
html>

shopping_car.html


<html lang="en">
<head>
<meta charset="UTF-8">
<title>购物title>
<style>
.page-header {
	height: 100px;
	background-color: blue;
}

.page-body .menu {
	height: 600px;
	background-color: green;
	float: left;
	width: 20%;
}

.page-body .content {
	height: 600px;
	background-color: gray;
	float: left;
	width: 80%;
}

.page-footer {
	height: 80px;
	background-color: yellow;
	clear: both;
}
style>
head>
<body>
	<div>
		<div class="page-header">div>
		<div class="page-body">
			<div class="menu">
				<p>
					<a href="/ordered/">订单a>
				p>
				<p>
					<a href="/shopping_car/">购物车a>
				p>
			div>
			<div class="content">购物车div>

		div>
		<div class="page-footer">div>
	div>

body>
html>

很明显,我们刚才重复了大量的 HTML 代码。 想象一下,如果有一个更典型的网站,它有导航条、样式表,可能还有一些 JavaScript 代码,事情必将以向每个模板填充各种冗余的 HTML 而告终。

解决这个问题的服务器端 include 方案是找出两个模板中的共同部分,将其保存为不同的模板片段,然后在每个模板中进行 include。 也许你会把模板头部的一些代码保存为 header.html 文件:


<html lang="en">
<head>
<meta charset="UTF-8">

你可能会把底部保存到文件 footer.html :

body>
html>

对基于 include 的策略,头部和底部的包含很简单。 麻烦的是中间部分。
Django 的模板继承系统解决了这些问题。 你可以将其视为服务器端 include 的逆向思维版本。 你可以对那些不同 的代码段进行定义,而不是 共同 代码段。

第一步是定义 基础模板,该框架之后将由子模板所继承。 以下是我们目前所讲述范例的基础模板:
所以需要编写base.html:


<html lang="en">
<head>
<meta charset="UTF-8">
<title>购物title>
<style>
.page-header {
	height: 100px;
	background-color: blue;
}

.page-body .menu {
	height: 600px;
	background-color: green;
	float: left;
	width: 20%;
}

.page-body .content {
	height: 600px;
	background-color: gray;
	float: left;
	width: 80%;
}

.page-footer {
	height: 80px;
	background-color: yellow;
	clear: both;
}
style>
head>
<body>
	<div>
		<div class="page-header">div>
		<div class="page-body">
			<div class="menu">
				<p>
					<a href="/ordered/">订单a>
				p>
				<p>
					<a href="/shopping_car/">购物车a>
				p>
			div>
			
			{% block content %} 
			
			{% endblock %}
		div>
		<div class="page-footer">div>
	div>

body>
html>

优化各个模块的模板:
ordered.html

{% extends "base.html" %}

{% block content %}
<div class="content">订单div>
{% endblock %}
			

shopping_car.html

{% extends "base.html" %}

{% block content %}
<div class="content">购物车div>
{% endblock %}
			

说明

<1>如果在模板中使用 {% extends %} ,必须保证其为模板中的第一个模板标记。 否则,模板继承将不起作用。
 <2>一般来说,基础模板中的 {% block %} 标签越多越好。 记住,子模板不必定义父模板中所有的代码块,因此
    你可以用合理的缺省值对一些代码块进行填充,然后只对子模板所需的代码块进行(重)定义。 俗话说,钩子越
    多越好。
 <3>如果发觉自己在多个模板之间拷贝代码,你应该考虑将该代码段放置到父模板的某个 {% block %} 中。
    如果你需要访问父模板中的块的内容,使用 {{ block.super }}这个标签吧,这一个魔法变量将会表现出父模
    板中的内容。 如果只想在上级代码块基础上添加内容,而不是全部重载,该变量就显得非常有用了。

base.html

{% block content %} 
test
{% endblock %}

ordered.html

{% extends "base.html" %}

{% block content %}
//获取父类块中的内容
<div class="content">{{ block.super }}订单div>
{% endblock %}

web框架篇--基础_第7张图片

 <4>不允许在同一个模板中定义多个同名的 {% block %} 。 存在这样的限制是因为block 标签的工作方式是双向的。
    也就是说,block 标签不仅挖了一个要填的坑,也定义了在父模板中这个坑所填充的内容。如果模板中出现了两个
    相同名称的 {% block %} 标签,父模板将无从得知要使用哪个块的内容。

3.7 Models 操作数据库

3.7.1 数据库的配置

1 django默认支持sqlite,mysql, oracle,postgresql数据库。
<1> sqlite
django默认使用sqlite的数据库,默认自带sqlite的数据库驱动 , 引擎名称:django.db.backends.sqlite3
<2> mysql
引擎名称:django.db.backends.mysql

2 mysql驱动程序
MySQLdb(mysql python)
mysqlclient
MySQL
PyMySQL(纯python的mysql驱动程序)
3 在django的项目中会默认使用sqlite数据库,在settings里有如下设置:

如果我们想要更改数据库,需要修改如下:

DATABASES = {

    'default': {

        'ENGINE': 'django.db.backends.mysql', 

        'NAME': 'books',    #你的数据库名称

        'USER': 'root',   #你的数据库用户名

        'PASSWORD': '', #你的数据库密码

        'HOST': '', #你的数据库主机,留空默认为localhost

        'PORT': '3306', #你的数据库端口

    }

}

注意:

NAME即数据库的名字,在mysql连接前该数据库必须已经创建,而上面的sqlite数据库下的db.sqlite3则是项目自动创建

USER和PASSWORD分别是数据库的用户名和密码。

设置完后,再启动我们的Django项目前,我们需要激活我们的mysql。

然后,启动项目,会报错:no module named MySQLdb

这是因为django默认你导入的驱动是MySQLdb,可是MySQLdb对于py3有很大问题,所以我们需要的驱动是PyMySQL

所以,我们只需要找到项目名文件下的__init__,在里面写入:

import pymysql
pymysql.install_as_MySQLdb()

如果报错:  File "C:\Python36\lib\site-packages\django\db\backends\mysql\operations.py", line 147, in last_executed_query
    query = query.decode(errors='replace')
AttributeError: 'str' object has no attribute 'decode'

可修改文件“operations.py”,line 146: decode-->encode

3.7.2 ORM(对象关系映射)

3.7.2.1 介绍

用于实现面向对象编程语言里不同类型系统的数据之间的转换,换言之,就是用面向对象的方式去操作数据库的创建表以及增删改查等操作。

优点
1 ORM使得我们的通用数据库交互变得简单易行,而且完全不用考虑该死的SQL语句。快速开发,由此而来。
2 可以避免一些新手程序猿写sql语句带来的性能问题。
比如 我们查询User表中的所有字段:

新手可能会用select * from auth_user,这样会因为多了一个匹配动作而影响效率的。

缺点
1 性能有所牺牲,不过现在的各种ORM框架都在尝试各种方法,比如缓存,延迟加载登来减轻这个问题。效果很显著。

2 对于个别复杂查询,ORM仍然力不从心,为了解决这个问题,ORM一般也支持写raw sql。

3 通过QuerySet的query属性查询对应操作的sql语句

author_obj=models.Author.objects.filter(id=2)
print(author_obj.query)
3.7.2.2 练习

下面要开始学习Django ORM语法了,为了更好的理解,我们来做一个基本的 书籍/作者/出版商 数据库结构。

需求

实例:我们来假定下面这些概念,字段和关系

作者模型:一个作者有姓名。

作者详细模型:把作者的详情放到详情表,包含性别,email地址和出生日期,作者详情模型和作者模型之间是一对一的关系(one-to-one)(类似于每个人和他的身份证之间的关系),在大多数情况下我们没有必要将他们拆分成两张表,这里只是引出一对一的概念。

出版商模型:出版商有名称,地址,所在城市,省,国家和网站。

书籍模型:书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many),一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many),也被称作外键。

models.py

from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=30)
    def __str__(self):
        return self.name
    
class AuthorDetail(models.Model):
    sex = models.BooleanField(max_length=1, choices=((0, '男'),(1, '女'),))
    email = models.EmailField()
    address = models.CharField(max_length=50)
    birthday = models.DateField()
    #https://www.cnblogs.com/gerenboke/p/12091960.html
    author = models.OneToOneField(Author, on_delete=models.CASCADE)
    
class Publisher(models.Model):
    name = models.CharField(max_length=30, verbose_name="名称")
    address = models.CharField("地址", max_length=50)
    city = models.CharField('城市',max_length=60)
    state_province = models.CharField(max_length=30)
    country = models.CharField(max_length=50)
    website = models.URLField()
 
    class Meta:
        verbose_name = '出版商'
        verbose_name_plural = verbose_name
 
    def __str__(self):
        return self.name
    
class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher,on_delete=models.CASCADE)
    publication_date = models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2,default=10)
    def __str__(self):
        return self.title

注意1:记得在settings里的INSTALLED_APPS中加入’myapp’,然后再同步数据库。

python manage.py makemigrations
python manage.py migrate

注意2: models.ForeignKey(“Publish”) & models.ForeignKey(Publish)

分析代码:

   <1>  每个数据模型都是django.db.models.Model的子类,它的父类Model包含了所有必要的和数据库交互的方法。并提供了一个简介漂亮的定义数据库字段的语法。

   <2>  每个模型相当于单个数据库表(多对多关系例外,会多生成一张关系表),每个属性也是这个表中的字段。属性名就是字段名,它的类型(例如CharField)相当于数据库的字段类型(例如varchar)。大家可以留意下其它的类型都和数据库里的什么字段对应。

   <3>  模型之间的三种关系:一对一,一对多,多对多。

         一对一:实质就是在主外键(author_id就是foreign key)的关系基础上,给外键加了一个UNIQUE=True的属性;

         一对多:就是主外键关系;(foreign key)

         多对多:(ManyToManyField) 自动创建第三张表(当然我们也可以自己创建第三张表:两个foreign key)

模型常用的字段类型参数:

<1> CharField
        #字符串字段, 用于较短的字符串.
        #CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所允许的最大字符数.

<2> IntegerField
       #用于保存一个整数.

<3> FloatField
        # 一个浮点数. 必须 提供两个参数:
        #
        # 参数    描述
        # max_digits    总位数(不包括小数点和符号)
        # decimal_places    小数位数
                # 举例来说, 要保存最大值为 999 (小数点后保存2位),你要这样定义字段:
                #
                # models.FloatField(..., max_digits=5, decimal_places=2)
                # 要保存最大值一百万(小数点后保存10位)的话,你要这样定义:
                #
                # models.FloatField(..., max_digits=19, decimal_places=10)
                # admin 用一个文本框()表示该字段保存的数据.

<4> AutoField
        # 一个 IntegerField, 添加记录时它会自动增长. 你通常不需要直接使用这个字段; 
        # 自定义一个主键:my_id=models.AutoField(primary_key=True)
        # 如果你不指定主键的话,系统会自动添加一个主键字段到你的 model.

<5> BooleanField
        # A true/false field. admin 用 checkbox 来表示此类字段.

<6> TextField
        # 一个容量很大的文本字段.
        # admin 用一个