第三篇:Django创建表关系及部分讲解
目录一、Django中orm创建表关系
一、数据库中的表关系
我们可以通过换位思考来理解数据表之间的关系,表关系如下所示。
"""
表与表之间的关系
一对多
多对多
一对一
没有关系
判断表关系的方法:换位思考
"""
我们先简单建立几张表,然后判断各个表之间的关系。
分别为图书表、作者表、出版社表、作者详情表【未画出】。我们逐个分析个表之间的关系,我们发现。
图书和出版社是一对多的关系 外键字段建在多的那一方 book
图书和作者是多对多的关系 需要创建第三张表来专门存储
作者与作者详情表是一对一
二、创建表
创建表关系,先将基表创建出来,然后再添加外键字段,且不用考虑先创建被关联表。
-
图书表
# class Book(models.Model): # 书名 title = models.CharField(max_length=32) # 价格:总共八位 小数点后面占两位 price = models.DecimalField(max_digits=8,decimal_places=2) # 外键:【一对多】图书和出版社是一对多 并且书是多的一方 所以外键字段放在书表里面 publish = models.ForeignKey(to='Publish') # 默认就是与出版社表的主键字段做外键关联 # 外键:【多对多】图书和作者是多对多的关系 外键字段建在任意一方均可 但是推荐你建在查询频率较高的一方 authors = models.ManyToManyField(to='Author') """ 注意:authors是一个虚拟字段 主要是用来告诉orm 书籍表和作者表是多对多关系,让orm自动帮你创建第三张关系表。 """
注意:
""" 1、如果字段对应的是ForeignKey那么会orm会自动在字段的后面加_id 2、如果你自作聪明的加了_id那么orm还是会在后面继续加_id """ 所以,后面在定义ForeignKey的时候就不要自己加_id
-
出版社表
class Publish(models.Model): name = models.CharField(max_length=32) addr = models.CharField(max_length=32)
-
作者表
class Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() # 外键:【一对一】作者与作者详情是一对一的关系 外键字段建在任意一方都可以 但是推荐你建在查询频率较高的表中 detail = models.OneToOneField(to='AuthorDetail') """ OneToOneField也会自动给字段加_id后缀,所以你也不要自作聪明的自己加_id """
-
作者详情表
class AuthorDetail(models.Model): # 注意BigIntegerField phone = models.BigIntegerField() # 或者直接字符类型 addr = models.CharField(max_length=32)
-
创建效果如下
我们进一步分析,打开app01_book,我们发现自动创建了主键字段id,同时给关联外键的字段publish自动添加了后缀,变成了publish_id。【此关系为多对一】
打开app01_author,我们发现给detail添加了后缀,变成了detail_id。【此关系为一对一】
打开app01_book_author,我们发现也自动添加了后缀。【此关系为多对多】
总结:
"""orm中如何定义三种关系?"""
# 默认就是与出版社表的主键字段做外键关联
publish = models.ForeignKey(to='Publish')
# 默认就是id之间建立外键关系
authors = models.ManyToManyField(to='Author')
# 默认就是id之间建立外键关系
author_detail = models.OneToOneField(to='AuthorDetail')
"""ForeignKey、OneToOneField、ManyToManyField会自动在字段后面加_id后缀"""
补充:
# 在django1.X版本中外键默认都是级联更新删除的
# 多对多的表关系可以有好几种创建方式 这里仅为其中一种
# 针对外键字段里面的其他参数 暂时不要考虑
二、Django请求生命周期流程图
补充:
缓存数据库:提前已经将你想要的数据准备好了 你来直接拿就可以,提高效率和响应时间。
案例:当你在修改你的数据的时候 你会发现数据并不是立刻修改完成的而是需要经过一段时间才会修改【博客园】
三、Django分布讲解
一、路由层(urls.py)
1、路由匹配
为了测试路由,我们先设置这样的路由路径。
"""urls.py"""
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'test/', views.test),
url(r'testadd/', views.testadd)
]
"""views.py"""
def test(request):
return HttpResponse('date from test')
def testadd(request):
return HttpResponse('date from testadd')
从浏览器中访问127.0.0.1:8000/test
,得到以下效果。
看着很正确,但是我们又访问127.0.0.1:8000/testadd
,我们会发现出现了错误,仍然得到了以前的页面。
原因如下:
url方法第一个参数是正则表达式,只要第一个参数正则表达式能够匹配到内容那么就会立刻停止往下匹配,直接执行对应的视图函数。
你在输入url的时候会默认加斜杠,django内部帮你做到重定向,一次匹配不行,url后面加斜杠再来一次。
总结:
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 首页【^以...开头,$以...结尾】
url(r'^$',views.home),
# 路由匹配
url(r'^test/$',views.test),
url(r'^testadd/$',views.testadd),
# 尾页(了解,不完善,有小bug)
url(r'',views.error),
]
补充:
# 取消自动加斜杠
APPEND_SLASH = False/True # 默认是自动加斜杠的
2、无名分组
分组:就是给某一段正则表达式用小括号扩起来
无名分组就是将括号内正则表达式匹配到的内容当作位置参数传递给后面的视图函数。
url(r'^test/(\d+)/',views.test) # \d匹配数字 +匹配一个或多个
def test(request,xx):
print(xx) # 使用xx作为形参,可以得到(\d+)匹配到的数字
return HttpResponse('test')
3、有名分组
可以给正则表达式起一个别名
有名分组就是将括号内正则表达式匹配到的内容当作关键字参数传递给后面的视图函数。
url(r'^testadd/(?P<year>\d+)',views.testadd) # (?P<year>\d+)
def testadd(request,year):
print(year) # 关键字实参year,必须是year
return HttpResponse('testadd')
4、无名分组和有名分组是否可以混用?
不能混用,但是同一个分组可以使用N多次
url(r'^index/(\d+)/(\d+)/(\d+)/',views.index),
def index(request, x, y, z): # 位置形参
pass
url(r'^index/(?P<year>\d+)/(?P<age>\d+)/(?P<month>\d+)/',views.index),
def index(request, year, age, month) # 关键字参数传参
5、反向解析
# 我们可以给路由与视图函数起一个别名
url(r'^func/',views.func,name='ooo')
# 访问方式如下即可
# 前端反向解析
<a href="{% url 'ooo' %}">111</a> # a链接访问的路径为urls.py下 路由关系名为'ooo'的路径
# 后端方向解析
"""urls.py"""
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^func/', views.func, name='ooo')
]
"""views.py"""
def func(request):
print(reverse('ooo')) # /func/
return HttpResponse('date from func')
访问127.0.0.1:8000/func/
服务端效果如下。