Day53 Django的请求生命周期以及路由层和视图层

一.Django中数据库表与表之间建关系

以图书管理系统表为例

我们需要创建三张主要的表

1.书籍表

2.作者表

3.出版社表

4.书籍详细信息表

创建完成后思考这几张表之间的关系

书籍表 >>多对多>> 作者表

书籍表
>>多对一>> 出版社表

作者表 >>一对一>> 作者详细信息表

书籍表和作者表是多对多的关系,需要再创建一张关联他们的表

书籍表和出版社表是多对一,即出版社一对多书籍,我们需要在多的那一方创建一个外键

作者表和作者详细信息表是一对一的关系,我们需要在常用的那一方创建一个外键,并且标识唯一

在Django项目下的models.py文件中创建表,注意:库在Django中不能主动创建,需要我们先行创建

from django.db import models

# Create your models here.
# 书籍表
class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    # 小数,总共八位,小数部分占2位
    price = models.DecimalField(max_digits=8,decimal_places=2)
    # 书籍表和出版社表是多对一的关系,需要在书籍表中创建外键
    publish = models.ForeignKey(to='Publish')  # 外键中不用加_id,会自动帮你添加
    # 书籍表和作者表是多对多的关系,在这写这段代码后会,Django会自动帮我们创建这两张表的关联关系表
    author = models.ManyToManyField(to='Author')

# 作者表
class Author(models.Model):
    name = models.CharField(max_length=32)
    phone = models.IntegerField()
    # 作者表和详细信息表是一对一的外键关系
    author_detail = models.OneToOneField(to='AuthorDetail')

# 出版社表
class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)

# 作者详细信息表
class AuthorDetail(models.Model):
    phone = models.IntegerField()
    addr = models.CharField(max_length=32)
创建相关的表代码

Django中创建表的对应关系需要用到的代码

django orm中表与表之间建关系
一对多  ForeignKey(to='Publish')

一对一  OneToOneField(to='AuthorDetail')

多对多  ManyToManyField(to='Author')

注意:
    前面两个关键字会自动再字段后面加_id
    最后一个关键字 并不会产生实际字段 只是告诉django orm自动创建第三张表

二.Django请求生命周期

一张图说明Django的请求生命周期

三.Django之路由层(urls.py)

1.路由层url的正则表达式

路由层中的路由对应函数

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

url()方法第一个参数(^admin/)其实是一个正则表达式

一旦前面的正则匹配到了内容,就不会往下继续匹配,而是直接执行对应的视图函数

写一个小例子

# url层
url(r'^test', views.test),
url(r'^testadd', views.testadd),

# 对应的视图函数
def test(request):
    return HttpResponse('test')

def testadd(request):
    return HttpResponse('testadd')

这个小例子中当我们访问'testadd'页面,显示的还是test对应的视图函数

这是因为url层的第一个'^test'正则匹配也能匹配到testadd后缀,故先执行了对应的视图函数

由于上面的特性,当我们的项目特别庞大时,url的前后顺序也是我们需要考虑的问题,如果不考虑,极有可能出现url错乱的问题

# 我们可以在url层正则的末尾加一个斜杠解决
url(r'^test/', views.test),
url(r'^testadd/', views.testadd),

2.路由匹配自动加'/'的机制

Django在路由的匹配的时候,当我们在浏览器中没有敲斜杠,Django会先拿着没有敲斜杠的结果去匹配,

如果都没有匹配上,会让浏览器在末尾加斜杠再发一次请求,再匹配一次,如果还匹配不上才会报错

如果我们想取消该机制,不想做二次匹配可以在settings配置文件中指定

APPEND_SLASH = False  # 该参数默认是True

这时,就会取消自动加斜杠再匹配一次的机制

3.主页和404页面对应url正则表达式

当我们想生成一个主页,即什么后缀都不加的页面,或者是除了已经声明后缀的页面,其他页面都变成404页面

这时需要写的正则表达式

# 主页,即以空开头和以空结尾
url(r'^$', views.home),
# 404页面,匹配为空,即匹配不到的页面
url(r'', views.error),

注意,当我们声明空的404页面时,需要把默认加斜杠的配置去掉,即只能输指定的后缀

4.路由层的无名分组和有名分组

无名分组

url(r'^test/(\d+)/', views.test)

路由匹配的时候,会将括号内的正则表达式匹配到的内容,当做位置参数传递给视图函数,所以在视图函数中也需要一个参数来接收

def test(request,num):  # 需要添加一个位置参数接收
    print(num)
    return HttpResponse('test')

有名分组

url(r'^test/(?P<year>\d+/)', views.test),  # 给这个正则表达式起了一个别名

这时候我们再次访问又会报错

有名分组路由匹配的时候,会将括号内正则表达式匹配到的内容当做关键字参数传递给视图函数

def test(request,year):  # 需要添加一个位置参数接收
    print(year)
    return HttpResponse('test')

注意:无名和有名不能混合使用!!!

但是同一种分组下,可以使用多个

# 无名分组支持多个
# url(r'^test/(\d+)/(\d+)/', views.test),
# 有名分组支持多个
# url(r'^test/(?P<year>\d+)/(?P<xx>\d+)/', views.test),

5.路由层的反向解析

反向解析的本质就是返回一个能够返回对应url的地址

1.需要先给url和视图函数对应关系起别名

url(r'^index111/',views.index,name='qqq'),

2.反向解析

  前端反向解析

{% url 'qqq' %}

  后端反向解析

后端可以在任意位置通过reverse反向解析出对应的url

from django.shortcuts import render,HttpResponse,redirect,reverse
# 先导入reverse,再通过别名解析
reverse('qqq')

无名分组反向解析

先起别名,并且加入正则,此时正则不能被解析,我们需要传入参数

url(r'^index/(\d+)/$',views.index,name='kkk')

后端反向解析

reverse('kkk',args=(1,))  # 后面的数字通常都是数据的id值

前端反向解析

{% url 'kkk' 1%}   # 后面的数字通常都是数据的id值

有名分组反向解析

同无名分组反向解析的用法

url(r'^index/(?P<year>\d+)/$',views.index,name='kkk')

后端反向解析

print(reverse('kkk',args=(1,)))  # 推荐你使用上面这种  减少你的脑容量消耗
print(reverse('kkk',kwargs={'year':1}))

前端反向解析

<a href="{% url 'kkk' 1 %}">1</a>  # 推荐你使用上面这种  减少你的脑容量消耗
<a href="{% url 'kkk' year=1 %}">1</a>

注意:在同一个应用下,别名不能重复!!!

6.路由层的路由分发

当一个Django项目特别庞大的时候,路由与视图函数对应的关系特别多,这样总路由urls.py太过冗长,不易维护

在Django开发中每一个应用都可以有自己的urls.py,static文件夹,templates文件夹

这样就可以实现多人分组开发,等到每个人开发完成后,我们只需要创建一个空的Django项目,

然后将多人开发的app注册进来,在总路由实现一个路由分发,而不再做路由匹配

总路由进行分发

from django.conf.urls import url,include  # 导入include
# from app01 import urls as app01_urls  # 将两个app里的urls导入并起分别别名
# from app02 import urls as app02_urls

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # url(r'^app01/', include(app01_urls)),  # 导入到总路由中
    # url(r'^app02/', include(app02_urls)),  
    # 下面的简便写法,甚至都不用导入模块,直接字符串点的方法导入
    url(r'^app01/', include('app01.urls')),
    url(r'^app02/', include('app02.urls')),
]

子路由在写具体的视图函数对应关系

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', views.index),
]

注意:总路由中的一级路由后不能加$符号,否则会报错

当视图层的视图函数特别多的时候,也可以使用分发的方法,建一个views文件夹,里面根据功能的细分再建不同的py文件

7.路由层的名称空间

当多个app中的urls中视图对应函数起了相同的别名,这时候使用反向解析,并不会自动识别应用前缀

我们分别访问不同app中的后缀,使用反向解析返回这个别名时,发现返回的是同一个后缀,反向解析失败

如果想避免这种问题:

方式1:

  总路由中设置名称空间

url(r'^app01/',include('app01.urls',namespace='app01'))
url(r'^app02/',include('app02.urls',namespace='app02'))

  后端解析的时候

reverse('app01:index')
reverse('app02:index')

  前端解析的时候

{% url 'app01:index' %}
{% url 'app02:index' %}

方式2:

  当我们起别名的时候不要冲突,一般情况下起别名都是以应用名作为前缀

name = 'app01_index'
name = 'app02_index'

8.伪静态

静态网页是数据写死的,万年不变

伪静态网页就是伪造成后缀是.html的网页,这样设计可以增加百度等搜索引擎seo的查询力度

通过伪静态就可以实现

实现方法:在后缀最后加个.html

url(r'^login.html/',views.login)

9.虚拟环境(了解)

一般情况下 我们会给每一个项目 配备该项目所需要的模块 不需要的一概不装

虚拟环境 就类似于为每个项目量身定做的解释器环境

如何创建虚拟环境

每创建一个虚拟环境 就类似于你又下载了一个全新的python解释器

10.Django版本的区别

路由层1.x用的是url,2.x使用的是path

2.x中的path第一个参数不再是正则表达式,而是写什么就匹配什么,精准匹配

如果还想使用正则表达式,可以使用re_path,这个就是1.x中的url

虽然2.x中path不支持正则表达式,但是它提供了五种默认的转换器

1.0版本的url和2.0版本的re_path分组出来的数据都是字符串类型
默认有五个转换器,感兴趣的自己可以课下去试一下
str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
int,匹配正整数,包含0。
slug,匹配字母、数字以及横杠、下划线组成的字符串。
uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)

还支持自定义转换器

class FourDigitYearConverter:  
regex = '[0-9]{4}'  
def to_python(self, value):  
    return int(value)  
def to_url(self, value):  
    return '%04d' % value  占四位,不够用0填满,超了则就按超了的位数来!
register_converter(FourDigitYearConverter, 'yyyy')  

urlpatterns = [  
        path('articles/2003/', views.special_case_2003),  
        path('articles/<yyyy:year>/', views.year_archive),  
        ...  
    ]  

四.Django之视图层(view.py)

1.小白必会三板斧

  1.HttpResponse

  2.render

  3.redirect

Django视图函数必须返回一个HttpResponse对象(这三个对象其实直接或间接的继承了HttpResponse对象)

如果不返回,就会报错

2.前后端分离和JsonRespnse

前端一个人干(前端转成自定义对象)

  JSON.stringify()  >>>  json.dumps()

  JSON.parse()  >>>  json.loads()

后端另一个干(python后端用字典)

只要涉及到数据交互,一般情况下都是用的json格式

后端只负责产生接口,前端调用该接口能拿到一个大字典

后端只需要写一个接口文档 里面描述字典的详细信息以及参数的传递

JsonResponse

当传入中文字符时,需要传入json_dumps_params = {'ensure_ascii':False}

from django.http import JsonResponse
def index(request):
    data = {'name':'sxc沈晓晨', 'age':18}
    l = [1,2,3,4,5,6]
    # res = json.dumps(data,ensure_ascii=False)
    # return HttpResponse(res)
    # return JsonResponse(data,json_dumps_params = {'ensure_ascii':False})  # 可以通过json_dumps_params = {'ensure_ascii':False}进行转码
    return JsonResponse(l,safe=False)  # 传列表需要将safe设置为False

3.上传文件

form表单上传文件需要注意的事项

  1.enctype需要由默认的urlencoded变成formdata

  2.method需要由默认的get变成post

(目前还需要考虑的是 提交post请求需要将配置文件中的csrf中间件注释)

如果form表单上传文件 后端需要在request.FILES获取文件数据 而不再是POST里面

def upload_file(request):
    if request.method == 'POST':
        file_obj = request.FILES.get('myfile')
        print(file_obj.name)
        with open(file_obj.name,'wb') as f:
            for line in file_obj.chunks():
                print(line)
                f.write(line)
        return HttpResponse('收到了')
    return render(request,'file.html')

4.request方法

request.method  # 获取请求方式
request.GET  # 可以获取GET后面的跟的数据
request.POST  # 可以获取POST后面的跟的数据
request.FILES  # 获取文件数据
request.path  # 只获取url后缀 不获取?后面的参数
request.get_full_path()  # 后缀和参数全部获取

猜你喜欢

转载自www.cnblogs.com/sxchen/p/11538161.html