Django 03

django建立表关系

  • 以图书表, 作者表, 出版社表为例
  1. 一对多

    给图书表增加出版社外键 publish = models.ForeignKey(to="Publish")

  2. 一对一

    给作者表增加作者详情外键 author_detail = models.OneToOneField(to="AuthorDetail")

  3. 多对多

    图书和作者之间, 在图书表上添加 author = models.ManyToManyField(to="Author")

  4. 注意

    1. django会给外键字段自动加上"_id"后缀, 不需要我们手动增加, 否则会导致重复
    2. 建立多对多表关系时的字段只是一个虚拟字段, 并不会出现在表中. 会生成一个新的表

请求生命周期图

路由匹配

我们在 urls.py 添加如下路由与视图函数的匹配关系, 注意路由结尾没有 "/"

# urls.py
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^test', views.test),
    url(r'^test_add', views.test_add),
]

# views.py
def test(request):
    return HttpResponse("test view")


def test_add(request):
    return HttpResponse("test_add view")

现在我们启动项目并访问 127.0.0.1:8000/test 页面显示为 test view

然后我们访问 127.0.0.1:8000/test_add 页面显示依旧为 test view

**造成这种现象的原因是 url(r'^test', views.test) 中的第一个参数是一个正则表达式, 只要你输入的域名后缀, 比如 test_add 符合条件, 就会执行其对应的视图函数, 并结束匹配 **

我们可以修改路由为 url(r'^test_add/', views.test) 来解决这个问题 (结尾增加一个 "/" )

  • ^test/ 必须以test/开头
  • test/$ 必须以test/结尾

现在我们通过在结尾加"/"的方式解决了上述问题, 我们现在访问 127.0.0.1:8000/test_add 页面显示为 test_add view

心细的你肯定又发现了另一个问题, 我们设置的路由匹配明明结尾是带有 "/" url(r'^test_add/', views.test)

为什么我们访问时不加"/" 127.0.0.1:8000/test_add 也能成功进行访问呢?

这是因为django自动给我们做了处理:

  • 当我们结尾不加 "/" 时进行访问时 127.0.0.1:8000/test_add, 先以 test_add 在url.py中进行寻找, 如果没有找到, 就会重定向到 127.0.0.1:8000/test_add/ 再次进行访问, 这时候就可以匹配到了

如果我们不想让django帮我们添加 "/" 进行重定向访问, 可以在 setting.py 中加上如下一行代码

  • APPEND_SLASH = Flase

还有一点需要我们注意的是:

  • 路由匹配不会匹配url中 "?" 及其后面的内容

无名分组

  • 当路由中有无名分组的正则表达式时, 会把分组内匹配到的内容当做位置参数传递给视图函数

现在我们设置了如下路由和视图函数的匹配关系

# urls.py
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # ([0-9]{4}) 这是一个无名分组, 表示从0-9的4位数字
    url(r'^test/([0-9]{4})/', views.test),  
]

# views.py
def test(request, *args):
    return HttpResponse(f"test view received a positional argument: {args}")

然后我们来访问一下 127.0.0.1:8000/test/1234 页面显示如下

有名分组

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

现在我们设置了如下路由和视图函数的匹配关系

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^test_add/(?P<year>[0-9]{4})/', views.test_add),
]

def test_add(request, **kwargs):
    return HttpResponse(f"test_add view received a key-word argument: {kwargs}")

然后我们来访问一下 127.0.0.1:8000/test_add/2019/ 页面显示如下:

  • 利用无名分组和有名分组就可以给视图函数传递额外的参数
  • 可以有多个有名或者无名分组, 就是不能混合使用 url(r'^index/(?P<year1>[0-9]{4})/(?P<year2>[0-9]{4})/', views.test)

反向解析

  • 根据一个别名, 动态解析出一个结果, 这个结果可以直接访问对应的url

模板中的超链接

这是我们的路由和视图函数

# urls.py
urlpatterns = [
    url(r'^index/', views.index),
    url(r'^home/', views.home)
]


# views.py
def index(request):
    return render(request, "index.html")


def home(request):
    return HttpResponse("this is home view")

这是我们的 index.html 文件, 很明显, 我们访问 index/ 点击 跳转到home 就可以跳转到home页面了

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>反向解析</title>
</head>
<body>
普通连接: <a href="home/">跳转到home</a>
</body>
</html>

假设现在产品经理要求把 url(r'^home/', views.home) 改成 url(r'^homework/', views.home)

那我们也要把 index.html 的当中的 <a href="home/">跳转到home</a> 改成 <a href="homework/">跳转到home</a> 否则就跳转不到相应的页面啦

像上面这样, 一个a标签很好改, 万一有几百上千个a标签呢? 还不得改出人命么

为了挽救程序员的生命, 我们可以利用django提供的反向解析方法

  1. 在url中添加一个一个别名
# urls.py
urlpatterns = [
    url(r'^index/', views.index),
    # 添加一个name参数
    url(r'^home/', views.home, name='to_home')
]
  1. index.html 中修改跳转连接
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>反向解析</title>
</head>
<body>
普通连接: <a href="home/">跳转到home</a>
<hr>
反向解析: <a href="{% url 'to_home' %}">跳转到home</a>
</body>
</html>

这样django就可以通过这个 to_home 别名来找到相应的路由啦 (比如: /homework/)

现在不管我们如何修改 url(r'^home/', views.home, name='to_home') 中的路由, 都可以跳转成功了

视图中的重定向

显然我们在视图层也有可能用到超链接, 比如 redirect 重定向

同样的, 这是我们的路由和视图函数

# urls.py
urlpatterns = [
    url(r'^home/', views.home),
    url(r'^homepage/', views.homepage)
]

# views.py
def home(request):
    return HttpResponse("this is home view")


def homepage(request):
    return redirect("/home/")

和上面一样, 如果我们把 url(r'^home/', views.home) 改成 url(r'^homework/', views.home)

那我们也要把 views.py 中的 homepage()方法 下面的 /home/ 改为 /homework/

有什么办法可以让 django帮我们操作呢? 这里我们需要借助 reverse() 方法

from django.shortcuts import render, HttpResponse, redirect, reverse

def home(request):
    return HttpResponse("this is home view")


def homepage(request):
    return redirect(reverse("to_home"))

以为反向解析就这么结束了吗? 再看看下面两种情况:

  1. 当需要跳转的连接中包含无名分组时
# urls.py
urlpatterns = [
    url(r'^index/', views.index),
    url(r'^home/([0-9]{4})', views.home)
    url(r'^homepage/', views.homepage)
]

# views.py
def index(request):
    return render(request, "index.html")

# 分组匹配到的内容会传入视图函数, 因此我们用*args/**kwargs来接收一下, 否则会报错
def home(request, *args, **kwargs):
    return HttpResponse("this is home view")

def homepage(request):
    return redirect(reverse("to_home"))

index.html 中修改跳转连接, 需要传入一个参数, 只要这个参数满足无名分组的筛选条件即可

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>反向解析</title>
</head>
<body>
普通连接: <a href="home/">跳转到home</a>
<hr>
反向解析: <a href="{% url 'to_home' 1999 %}">跳转到home</a>
</body>
</html>

当然, 视图层里面的reverse内也需要传入一个参数, 只要这个参数满足无名分组的筛选条件即可

def homepage(request):
    # 以元祖的形式传参
    return redirect(reverse("to_home", args=(1998, )))
  1. 当需要跳转的连接中包含有名分组时
# urls.py
urlpatterns = [
    url(r'^index/', views.index),
    url(r'^home/(?P<year>[0-9]{4})', views.home)
    url(r'^homepage/', views.homepage)
]

# views.py
def index(request):
    return render(request, "index.html")

# 分组匹配到的内容会传入视图函数, 因此我们用*args/**kwargs来接收一下, 否则会报错
def home(request, *args, **kwargs):
    return HttpResponse("this is home view")

def homepage(request):
    return redirect(reverse("to_home"))

index.html 中修改跳转连接, 需要传入一个参数, 只要这个参数满足有名分组的筛选条件即可

<!--下面这两种都可以, 第二种更规范-->
反向解析: <a href="{% url 'to_home' 1999 %}">跳转到home</a>
    
反向解析: <a href="{% url 'to_home' year=1999 %}">跳转到home</a>

当然, 视图层里面的reverse内也需要传入一个参数, 只要这个参数满足有名分组的筛选条件即可

def homepage(request):
    # 以字典的形式传参
    return redirect(reverse("to_home", kwargs={year:1998}))

路由分发

在django中所有的app都可以有自己的独立的urls.py, templates文件夹, static文件夹

这样一来, django项目就能够完全做到多人分组开发, 互相不干扰

路由分发解决的是项目的总路由匹配关系过多的情况

使用路由分发, 总路由匹配的是不再是视图函数, 而是对应的app

# project/urls.py
# 需要先导入 include
from django.conf.urls import url, include
from django.contrib import admin

from app01 import urls as app01_urls
from app02 import urls as app02_urls


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 总路由这里匹配的是相应的app
    url(r'^app01/', include(app01_urls)),
    url(r'^app02/', include(app02_urls)),

]


# app01/urls.py
from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^reg/', views.reg),

]


# app02/urls.py
from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^reg/', views.reg),

]


# app01/views.py
from django.shortcuts import render, HttpResponse


def reg(request):
    return HttpResponse("app01 reg")


# app02/views.py
from django.shortcuts import render, HttpResponse


def reg(request):
    return HttpResponse("app02 reg")

现在我们来访问 127.0.0.1:8000/app01/reg 页面显示为 app01 reg

访问 127.0.0.1:8000/app02/reg 页面显示为 app02 reg

别着急还没完, 我们还可以写成如下格式, 这样就不用将各个app的 urls.py 一个个导入啦

# project/urls.py
# 需要先导入 include
from django.conf.urls import url, include
from django.contrib import admin


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 总路由这里匹配的是相应的app 
    url(r'^app01/', include(app01.urls)),  # 直接用 app名.urls 就可以, 不用导入了
    url(r'^app02/', include(app02.urls)),

]

名称空间

当多个app中 urls.pyurl() 方法中都传入相同的name参数, 那反向解析时可能会出错

# app01/urls.py
from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^reg/', views.reg, name='reg'),

]


# app02/urls.py
from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^reg/', views.reg, name='reg'),

]

怎样解决这个问题呢?

我们可以在项目 urls.py 中给各个app添加名称空间

# project/urls.py
from django.conf.urls import url, include
from django.contrib import admin

from app01 import urls as app01_urls
from app02 import urls as app02_urls


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 添加名称空间spacename
    url(r'^app01/', include(app01_urls), spacename='app01'),
    url(r'^app02/', include(app02_urls), spacename='app02'),

]

在前后端进行反向解析时也应该进行相应的修改

​ 模板层: <a href="{% url 'app01:reg' %}">跳转到reg</a>

​ 模型层: reverse("app01:reg")

现在我们再来想一下这个问题, 导致这个问题的根本原因就是不同的app中起别名起重复了

那只要不重复不就行了???

很简单, 其别名的时候用app名作为前缀就行了

# app01/urls.py
from django.conf.urls import url
from app01 import views

urlpatterns = [
    # 用app名作为别名前缀, 避免重复
    url(r'^reg/', views.reg, name='app01_reg'),

]


# app02/urls.py
from django.conf.urls import url
from app01 import views

urlpatterns = [
    # 用app名作为别名前缀, 避免重复
    url(r'^reg/', views.reg, name='app02_reg'),

]

伪静态

将一个动态网页伪装一个静态网页, 以此来提升搜索引擎SEO查询频率和收藏力度

如何伪装呢?

很简单, 修改路由以 .html 结尾即可

urlpatterns = [
    url(r'^reg.html/', views.reg),

]

虚拟环境

给每一个项目 装备该项目所需要的模块 不需要的模块一概不装

每创建一个虚拟环境就类似于你重新下载了一个纯净python解释器
之后该项目用到什么 你就装什么 (虚拟环境一台机器上可以有N多个)

注意, 不要在你的机器上无限制创建虚拟环境

django版本区别

我们知道现在django有两个大版本: django1.x 和 django2.x

这两个版本 urls.py 中路由匹配的方法有区别的:

# 1.x 用的还是url
urlpatterns = [
     url(r'^admin/', admin.site.urls)
]

# 2.x 用的是path
urlpatterns = [
		path('admin/', admin.site.urls),
	]

path第一个参数不是正则也不支持正则 写什么就匹配什么

  1. 虽然path不支持正则 感觉也bu好用 django2.x还有一个 re_path 的方法 该方法就是你django1.x里面url

  2. path提供了五种转换器 能够将匹配到的数据自动转化成对应的类型

urlpatterns = [
		path('index/<int:age>', views.index),
	]

posted @ 2019-11-26 01:54  MrBigB  阅读(131)  评论(0编辑  收藏  举报