Django路由层与视图层

目录

Django路由层

表关系的创建

  • 表与表的三种关系:
    • 一对多
    • 多对多
    • 一对一

以图书管理系统为例

  • 图书表
    • 图书和出版社是一对多的外键关系
    • 一对多外键关系,外键字段建在多的一方
  • 作者表
    • 图书与作者是多对多外键关系
    • 多对多关系外键关系,外键字段无论建在哪张表里都可以(推荐建在查询频率较高的那张表中)
    • 作者与作者详情表是一对一的外键关系
    • 一对一外键关系,外键字段无论建在哪张表里都可以(推荐建在查询频率较高的那张表中)

ForeignKey字段以及OneToOneField字段,无论自己加没有加,在创建表的时候orm都会自动给字段加_id的后缀

在书写表关系的时候,要先把基表全部写出来,之后再考虑外键字段

from django.db import models


# Create your models here.
class Book(models.Model):
    # id 是自动创建的,我们可以不写
    title = models.CharField(max_length=64)
    # price 为小数字段,总共8位小数位占两位
    price = models.DecimalField(max_digits=8, decimal_places=2)
    # 书籍与出版社 是一对多外键关系
    publish = models.ForeignKey(to='Publish')  # 默认关联字段就是出版社表的主键字段
    # publish = models.ForeignKey(to='Publish')  # to后面也可以直接写表的变量名,但是需要保证该变量名在当前位置的上方出现
    # 书籍与作者是多对多关系
    author = models.ManyToManyField(to='Author')
    # author字段是一个虚拟字段,不会真正的在表中创建出来,
    # 只是用来告诉Django orm需要创建书籍与作者的第三张关系表


class Publish(models.Model):
    name = models.CharField(max_length=64)
    addr = models.CharField(max_length=64)


class Author(models.Model):
    name = models.CharField(max_length=32)
    phone = models.BigIntegerField()
    # 一对一关键关系建立
    author_detail = models.OneToOneField(to='AuthorDetail')


class AuthorDetail(models.Model):
    age = models.IntegerField()
    addr = models.CharField(max_length=255)

Django请求生命周期流程图

Django路由层与视图层


路由层

路由匹配
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/', views.login),
    url(r'^reg/', views.register)
]

url方式第一个参数是一个正则表达式

路由匹配按照正则匹配,一旦正则能够匹配到内容,会立刻执行对应的视图函数,不会再继续匹配了

用户输入url不加最后的斜杠,Django会默认自动加上,可以在配置文件中指定是否开启该功能

settings.py

APPEND_SLASH = True/False
无名分组

分组:分组就是给一段正则表达式加括号

在路由匹配的时候给某段正则表达式加了括号,匹配的时候会将括号内正则表达式匹配到的内容当做位置参数传递给对应的视图函数

无名分组

url(r'^test/([0-9]{4})', views.test)

视图函数

def test(request, args): # 函数就必须有个形参来接收([0-9]{4})
    return HttpResponse('test')
有名分组

在路由匹配的时候给某段正则表达式起一个别名,匹配的时候会将括号内正则表达式匹配到的内容当做关键字参数传递给对应的视图函数

有名分组

url(r'^test/(?P<year>\d+)', views.test)

视图函数

def test(request, year): # 函数必须有个相对应的关键字来接收year
    return HttpResponse('test')

注意:

无名分组和有名分组是不可以混合使用的,但是

同一种命名方式,可以使用多个

url(r'^test/(\d+)/(\d+)/',views.test)   # 无名分组
url(r'^test/(?P<xxx>\d+)/(?P<year>\d+)/',views.test)#有名分组
反向解析

通过一些方法,能够得到一个结果,该结果可以访问到对应的url

使用方法:

  • 先给路由和视图函数对应关系,起一个名字
url(r'^testadd/', views.testadd, name='add')
  • 前端解析
<a href="{% url 'add' %}">我是一个a标签</a>
  • 后端解析
from django.shortcuts import reverse
reverse('add')
- 无名分组反向解析

无名分组反向解析

url(r'^testadd/(\d+)', views.testadd, name='add'),
  • 前端解析
{% url 'add' 1 %}   # 因为Django不知道你写的数字是多少,所以需要个参数接收一下
  • 后端解析
reverse('add', args=(12,))
- 有名分组反向解析

有名分组反向解析

url(r'^testadd/(?P<year>\d+)', views.testadd, name='add')
  • 前端解析
{% url 'add' 1 %}           <!--推荐使用-->
{% url 'add' year=1 %}      <!--标准的-->
  • 后端解析
reverse('add', args=(12,))
reverse('add', kwargs={'year': 12})

伪代码诠释

"""
url(r'^edit_user/(\d+)/',views.edit_user,names='edit')

{% for user_obj in user_queryset %}
    <a href="edit_user/{{ user_obj.id }}/">编辑</a>
    <a href="{% url 'edit' user_obj.id %}">编辑</a>
{% endfor %}

def edit_user(request,edit_id):
    reverse('edit',args=(edit_id,))
"""
路由分发

概念:

当Django项目比较庞大的时候,路由与视图函数对应关系较多,总路由代码太多冗长,考虑到总路由代码不好维护,Django支持每个app都可以有自己的urls,并且总路由不再做路由与视图函数的对应关系,而仅仅只做一个分发任务的操作(类似于前台一样)
根据请求不同,识别出当前请求需要访问的功能属于哪个app然后自动下发到对应app里面的urls中,然后由app中的urls做路由与视图函数的匹配(类似于有人去前台找8215房,然后前台告诉你在8层,但是不再管你是去8215干什么)

拓展:

不仅如此,每个app除了可以有自己的urls.py之外,还可以有自己的static文件夹,templates模板文件,根据上面的特点,基于Django分小组开发,会变的格外的方便,简单每个人只需要开发自己的app即可,之后只需要创建一个空的Django项目,将多个人的app全部拷贝至目录下,配置文件注册,总路由分发即可

需要一个分发的模块

  1. 总路由
from django.conf.urls import url, include
# 路由分发,注意应用名后面千万不能加$符号,因为路径到此还未结束
from app01 import urls as app01_urls
from app02 import urls as app02_urls
urlpatterns = [
    url(r'^app01/', include(app01_urls)),
    url(r'^app02/', include(app02_urls)),
]
"""
显而易见这种还是不太方便,如果很多app的话,要一个一个的导过来
所以这里可以简写
"""
url(r'^app01/', include('app01.urls')),
url(r'^app02/', include('app02.urls')),
  1. 子路由

对应自身app中的视图函数

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

urlpatterns = [
    url(r'^index/', views.index)
]
from django.conf.urls import url
from app02 import views

urlpatterns = [
    url(r'^index/', views.index)
]
名称空间

为了防止多个app中有相同的网址后缀,可以给每个app都自定义一个名称空间

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

前端解析

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

后端解析

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

针对这种情况,在给路由与视图函数起别名的时候,只需要保证永远不出现冲突的情况即可,通常情况下推荐起别名的时候加上当前应用的应用名前缀即可

url(r'^index/',views.index,name='app01_index')
url(r'^index/',views.index,name='app02_index')

虚拟环境

我们想做到针对不同的项目,只安装项目所需要的功能模块,项目用不到的一概不装,来避免加载资源时的消耗,虚拟环境就类似于一个纯净的Python解释器环境

PS:每创建一个虚拟环境就类似于重新下载安装了一个Python解释器

虚拟环境不推荐使用,根据应用场景而定,学习阶段用本机环境即可,将所有的模块全部装到本机环境下


Django版本区别

路由层中

  • 1.X 版本用的是url
    • url第一个参数是一个正则表达式
  • 2.X 版本用的是path
    • path第一个参数不支持正则表达式,写什么就匹配什么
    • 如果不喜欢用path, 2.X以后的版本,还提供了一个和url一样功能的re_path,它就等同于1.X版本中的url功能

2.X之后版本path不支持正则表达式,但是它提供了五种默认的转换器

str---> 匹配除了路径分隔符(/)之外的非空字符串,这是默认的格式
int---> 匹配正整数,包含0
slug---> 匹配字母、数字以及横杠、下划线组成的字符串
uuid---> 匹配格式化的uuid 如:075194d3-6885-417e-a8a8-6c931e
path---> 匹配任意非空字符串,包含了路径分隔符(/)

例如

path('articles/<int:year>/<int:month>/<slug:other>/', views.article_detail) 
# 针对路径http://127.0.0.1:8000/articles/2009/123/hello/,path会匹配出参数year=2009,month=123,other='hello'传递给函数article_detail

很明显针对月份month,转换器int是无法精准匹配的,如果我们只想匹配两个字符,那么转换器slug也无法满足需求,针对等等这一系列复杂的需要,我们可以定义自己的转化器。转化器是一个类或接口,它的要求有三点:

  • regex 类属性,字符串类型
  • to_python(self, value) 方法,value是由类属性 regex 所匹配到的字符串,返回具体的Python变量值,以供Django传递到对应的视图函数中。
  • to_url(self, value) 方法,和 to_python 相反,value是一个具体的Python变量值,返回其字符串,通常用于url反向引用。

自定义转换器示例:

  1. 在app01下新建文件path_ converters.py,文件名可以随意命名
class MonthConverter:
    regex='\d{2}' # 属性名必须为regex

    def to_python(self, value):
        return int(value)

    def to_url(self, value):
        return value 
    # 匹配的regex是两个数字,返回的结果也必须是两个数字
  1. 在urls.py中,使用register_converter 将其注册到URL配置中:
from django.urls import path,register_converter
from app01.path_converts import MonthConverter

register_converter(MonthConverter,'mon')

from app01 import views


urlpatterns = [
    path('articles/<int:year>/<mon:month>/<slug:other>/', views.article_detail, name='aaa'),
]
  1. views.py中的视图函数article_detail
from django.shortcuts import render,HttpResponse,reverse

def article_detail(request,year,month,other):
    print(year,type(year))
    print(month,type(month))
    print(other,type(other))
    print(reverse('xxx',args=(1988,12,'hello'))) 
    # 反向解析结果/articles/1988/12/hello/
    return HttpResponse('xxxx')
  1. 测试
# 1、在浏览器输入http://127.0.0.1:8000/articles/2009/12/hello/,path会成功匹配出参数year=2009,month=12,other='hello'传递给函数article_detail
# 2、在浏览器输入http://127.0.0.1:8000/articles/2009/123/hello/,path会匹配失败,因为我们自定义的转换器mon只匹配两位数字,而对应位置的123超过了2位

伪静态

url以.html结尾 给人的感觉好像是这个文件是写死的 内容不会轻易的改变

伪静态作用:为了提高你的网站被搜索引擎收藏的力度 提供网站的SEO查询效率

上一篇:Django教程(2)


下一篇:css实现多行超出显示省略号?