djiango建立表关系
- 以图书表, 作者表, 出版社表为例
一对多
给图书表增加出版社外键
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里面urlpath提供了五种转换器 能够将匹配到的数据自动转化成对应的类型
urlpatterns = [ path('index/<int:age>', views.index), ]