• 【1022 | Day 50】Django之路由层(urls.py)


    今日内容

    1. 图书管理系统表设计

    2. django请求生命周期

    3. 路由层(urls.py)

      • 路由分发
      • 有/无分组
      • 反向解析
      • 名称空间
      • 伪静态
      • django版本区别

    1. 图书管理系统表设计

    • Book表
    • Author表
    • Publish表
    • Author_detail表
    1. 表与表之间的关系
    • Author和Author_detail是一对一
    • Author和Book是多对多
    • Publish和Book是一对多

    注意:建议外键字段应放在多的那一方,并且在建立关系时变量名不要自己加上_id,系统会自己重命名加上,不要多此一举!

    model.py

    from django.db import models
    
    #book表
    class Book(models.Model):
        title = models.CharField(max_length=32)
        # 总共八位 小数占两位
        price = models.DecimalField(max_digits=8,decimal_places=2)
        # 书和出版社是一对多的关系  外键字段键在多的一方
        publish_id = models.ForeignKey(to='Publish')  # to指定跟谁是外键关联的  默认关联的是表的主键字段
        
        """
        ForeignKey字段  django orm在创建表的时候 会自动给该字段添加_id后缀
        """
        
        # 书和作者是多对多的关系  外键字段建在任何一方都可以  但是 推荐你建在查询频率比较高的一方
        authors = models.ManyToManyField(to='Author')
        
        """
        authors字段仅仅是一个虚拟字段 不会再表中展示出来  仅仅是用来告诉django orm 书籍表和作者表示多对多的关系
        自动创建第三张表 
        
        """
    
        
    #author表
    class Author(models.Model):
        name = models.CharField(max_length=32)
        phone = models.BigIntegerField()
        # 一对一字段 建在哪张表都可以  但是推荐你建在 查询频率比较高的那张表
        author_detail = models.OneToOneField(to='AuthorDetail')
        
        """
        OneToOneField字段  django orm在创建表的时候 会自动给该字段添加_id后缀
        
        """
    
        
    #publish表
    class Publish(models.Model):
        name = models.CharField(max_length=32)
        addr = models.CharField(max_length=255)
    
        
        
    #AuthorDetail表
    class AuthorDetail(models.Model):
        addr = models.CharField(max_length=255)
        age = models.IntegerField()
    
    

    2. django请求生命周期

    3. 路由层(urls.py)

    原因:

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

    做法:

    1. 创建两个app

      # 新建项目np1
      D:fxyadela>django-admin startproject np1
      
      # 切换到项目目录下
      D:fxyadela>cd np1
      
      # 创建app01和app02
      D:fxyadela
      p1>python3 manage.py startapp app01
      D:fxyadela
      p1>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页面...')
    
    1. 在总的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页面...
      

    4. 有名/无名分组

    什么是分组?为何要分组呢?

    比如我们开发了一个博客系统,当我们需要根据文章的id查看指定文章时,浏览器在发送请求时需要向后台传递参数(文章的id号)。

    1. 可以使用

      http://127.0.0.1:8001/article/?id=3

    2. 也可以直接将参数放到路径中

      http://127.0.0.1:8001/article/3/

    针对后一种方式Django就需要直接从路径中取出参数,这就用到了正则表达式的分组功能了。

    分组分为两种:无名分组与有名分组

    4.1 无名分组

    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'^aritcle/(d+)/$',views.article),
     ] 
    	# 下述正则表达式会匹配url地址的路径部分为:article/数字/,匹配成功的分组部分会以位置参数的形式传给 视图函数,有几个分组就传几个位置参数 
    	 
    

    views.py文件

    from django.shortcuts import render
    from django.shortcuts import HttpResponse
    
    # 需要额外增加一个形参用于接收传递过来的分组数据
    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的文章内容...
    

    4.2 有名分组

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

    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的文章内容...
    

    总结:

    • 有名分组和无名分组都是为了获取路径中的参数,并传递给视图函数,区别在于无名分组是以位置参数的形式传递,有名分组是以关键字参数的形式传递。
    • 二者不能混合使用,可以单独多个使用

    5. 反向解析

    5.1 由来和定义

    在软件开发初期,url地址的路径设计可能并不完美,后期需要进行调整,如果项 目中很多地方使用了该路径,一旦该路径发生变化,就意味着所有使用该路径的 地方都需要进行修改,这是一个非常繁琐的操作。

    解决方案来了!!!

    就是在编写一条url(regex, view, kwargs=None, name=None)时,可以**通过参数nameurl地址的路径部分起一个别名****,项目中就可以通过别名来获取这个路径。

    以后无论路径如何变化别名与路径始终保持一致。

    上述方案中通过别名获取路径的过程称为反向解析

    5.2 案例

    登录成功跳转到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 %} <!--强调:必须加上这一行,后续我们会详细介绍-->
             <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.3 拓展(了解)

    如果路径存在分组的反向解析使用:

    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
    
        url(r'^aritcle/(d+)/$',views.article,name='article_page'), # 无名分组
        url(r'^user/(?P<uid>d+)/$',views.article,name='user_page'), # 有名分组
    ]
    
    
    1. 针对无名分组,比如我们要反向解析出:/aritcle/1/ 这种路径,写法如下:
    #在views.py中,反向解析的使用:
    
    	 url = reverse('article_page',args=(1,))
    
    #在模版login.html文件中,反向解析的使用
    
    	 {% url 'article_page' 1 %}
    
    1. 针对有名分组,比如我们要反向解析出:/user/1/ 这种路径,写法如下 :
    #在views.py中,反向解析的使用:
    
    	url = reverse('user_page',kwargs={'uid':1})
    
    #在模版login.html文件中,反向解析的使用
     
        {% url 'user_page' uid=1 %}
    

    6. 名称空间

    当我们的项目下创建了多个app,并且每个app下都针对匹配的路径起了别名,如果别名存在重复,那么在反向解析时则会出现覆盖。

    6.1 案例(覆盖)

    1. 创建两个app

      # 新建项目np2
      D:fxyadela>django-admin startproject np1
      
      # 切换到项目目录下
      D:fxyadela>cd np1
      
      # 创建app01和app02
      D:fxyadela
      p1>python3 manage.py startapp app01
      D:fxyadela
      p1>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 = [
       
          # 为匹配的路径app01/index/起别名'index_page'
       	
          url(r'^index/$',views.index,name='index_page'),
      
      ]
      
      

      app01下的views.py文件

      from django.shortcuts import render
      from django.shortcuts import HttpResponse
      from django.shortcuts import reverse
      
      def index(request):
       url=reverse('index_page')
       return HttpResponse('app01的index页面,反向解析结果为%s' %url)
      

      app02下的urls.py文件

      from django.conf.urls import url
      from app02 import views
      
      
      urlpatterns = [
       
          # 为匹配的路径app02/index/起别名'index_page',与app01中的别名相同
       	
          url(r'^index/$',views.index,name='index_page'),
      
      ]
      
      

      app02下的views.py文件

      from django.shortcuts import render
      from django.shortcuts import HttpResponse
      from django.shortcuts import reverse
      
      def index(request):
       url=reverse('index_page')
       return HttpResponse('app02的index页面,反向解析结果为%s' %url)
      
    3. 在总的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
       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页面...
    

    在测试时,无论在浏览器输入:

    http://127.0.0.1:8001/app01/index/还是输入http://127.0.0.1:8001/app02/index/

    针对别名'index_page'反向解析的结果都 是/app02/index/,覆盖了app01下别名的解析

    6.2 解决方案

    • 解决这个问题的方法之一:就是避免使用相同的别名。

    • 解决这个问题的方法之二:如果就想使用相同的别名, 那就需要用到django中名称空间的概念,将别名放到不同的名称空间中。这样即便是出现重复,彼此也不会冲突。

    具体做法如下:

    1. 总urls.py在路由分发时,指定名称空间

      from django.conf.urls import url, include
      from django.contrib import admin
      
      # 总路由表
      urlpatterns = [
       	url(r'^admin/', admin.site.urls),
      
       # 传给include功能一个元组,元组的第一个值是路由分发的地址,第二个值则是我们为名称空间起的名字
       	url(r'^app01/', include(('app01.urls','app01'))),
       	url(r'^app02/', include(('app02.urls','app02'))),
      ]
      
      
    2. 修改每个app下的view.py中视图函数,针对不同名称空间中的别名index_page做反向解析

      app01下的views.py

      from django.shortcuts import render
      from django.shortcuts import HttpResponse
      from django.shortcuts import reverse
      
      def index(request):
          url=reverse('app01:index_page') # 解析的是名称空间app01下的别名'index_page'
       	return HttpResponse('app01的index页面,反向解析结果为%s' %url)
      
      
    3. 测试

      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/

    6.3 总结

    1、在视图函数中基于名称空间的反向解析,用法如下
    
    	url=reverse('名称空间的名字:待解析的别名')
    
    2、在模版里基于名称空间的反向解析,用法如下
    
    	<a href="{% url '名称空间的名字:待解析的别名'%}">哈哈</a>
    
    

    7. 伪静态

    7.1 定义

    • 网站本身是动态网页如.php、.asp、.aspx等格式动态网页,有时这类动态网页还跟“ ? ”加参数来读取数据库内不同资料。
    • 很典型的案例即是discuz论坛系统,后台就有一个设置伪静态功能。开启伪静态后,动态网页即被转换重写成静态网页类型页面,通过浏览器访问地址和真的静态页面没区别。
    • 前提服务器支持伪静态重写URL Rewrite功能。

    需要伪静态功能地方:

    • 考虑搜索引擎优化SEO,将动态网页通过服务器处理成静态页面,如www.xxx.com/jk/fd.php?=12这样的动态网页处理成www.xxx.com/jk-fd-12.html这样格式静态页面。
    • 常见的论坛帖子页面,都是经过伪静态处理成静态页面格式http://www.divcss5.com/html页面。
    • 考虑网站所用的程序语言不易被发现,经过重写来伪静态来将动态网页的程序后缀变为html的静态页面格式。

    7.2 弊端

    当然犹如一篇文章的作者所说的:"如果流量稍大一些使用伪静态就出现CPU使用超负荷,我的同时在线300多人就挂了,而不使用伪静态的时候同时在线超500人都不挂,我的IIS数是1000。”

    确实是这样的,由于伪静态是用正则判断而不是真实地址,分辨到底显示哪个页面的责任也由直接指定转由CPU来判断了,所以CPU占有量的上升,确实是伪静态最大的弊病

    8. 虚拟环境

    • 通常针对不同的项目只会安装该项目所用的模块(用不到的一般不装)
    • 不同的项目由专门的解释器环境与之对应
    • 每创建一个虚拟环境就相当于重新下载了一个纯净的python解释器
    • 虚拟环境不要创建太多个, 会占硬盘资源的操作

    9. Django版本区别

    django1.x VS django2.x

    区别一:

    • urls.py中1.x用的是url,而2.x中用的是path
    • 2.x中的path第一个不支持正则表达式(就是不加^和/也不会瞎匹配),写什么匹配什么
    • 2.x里面的re_path就是1.x版本里的url

    区别二:

    • django2.x默认支持一下5种转换器(Path converters)
    str, 匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
    int, 匹配正整数,包含0。
    slug, 匹配字母、数字以及横杠、下划线组成的字符串。
    uuid, 匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
    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
    

    今天的内容不是一般多,但是其实也没什么大不了!!!正则表达式和数据库都需要复习一下,朋友们!!!别偷懒哦~

  • 相关阅读:
    【Java】java运行jar时,报 java.lang.UnsupportedClassVersionError
    【kafka】kafka.admin.AdminOperationException: replication factor: 1 larger than available brokers: 0
    【DB2】关闭表的日志功能
    python网络爬虫技术图谱
    对于网络通信的理解(图)
    对项目开发流程的思考和小结
    django框架--cookie/session
    django框架--中间件系统
    django框架--底层架构
    django框架--视图系统
  • 原文地址:https://www.cnblogs.com/fxyadela/p/11721935.html
Copyright © 2020-2023  润新知