django建立表关系
- 以图书表, 作者表, 出版社表为例
-
一对多
给图书表增加出版社外键
publish = models.ForeignKey(to="Publish")
-
一对一
给作者表增加作者详情外键
author_detail = models.OneToOneField(to="AuthorDetail")
-
多对多
图书和作者之间, 在图书表上添加
author = models.ManyToManyField(to="Author")
-
注意
- django会给外键字段自动加上"_id"后缀, 不需要我们手动增加, 否则会导致重复
- 建立多对多表关系时的字段只是一个虚拟字段, 并不会出现在表中. 会生成一个新的表
请求生命周期图
路由匹配
我们在 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提供的反向解析方法
- 在url中添加一个一个别名
# urls.py
urlpatterns = [
url(r'^index/', views.index),
# 添加一个name参数
url(r'^home/', views.home, name='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' %}">跳转到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"))
以为反向解析就这么结束了吗? 再看看下面两种情况:
- 当需要跳转的连接中包含无名分组时
# 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, )))
- 当需要跳转的连接中包含有名分组时
# 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.py
中 url()
方法中都传入相同的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第一个参数不是正则也不支持正则 写什么就匹配什么
-
虽然path不支持正则 感觉也bu好用 django2.x还有一个
re_path
的方法 该方法就是你django1.x里面url -
path提供了五种转换器 能够将匹配到的数据自动转化成对应的类型
urlpatterns = [
path('index/<int:age>', views.index),
]