一.Django框架中rom的表关系创建
1).表与表的关系
-
一对一
-
-
具体sql语句及关系请查看数据库基础链接
2)以图书管理系统为例,创建几张关系表
注意:**在models.py中书写表关系的时候,要先写好基表,再写外键关系
1 # 先建立好三张基表 2 from django.db import models 3 4 # Create your models here. 5 #书表 6 class Book(models.Model): 7 # id 是自动创建的,除非id名自定义 8 title = models.CharField(max_length=255) 9 # price为小数字段,表示总共8位数,其中小数占2位 10 price = models.DecimalField(max_digits=8, decimal_places=2) 11 12 13 #出版社表 14 class Publsh(models.Model): 15 name = models.CharField(max_length=64) 16 addr = models.CharField(max_length=64) 17 18 19 # 作者表 20 class Author(models.Model): 21 name = models.CharField(max_length=64) 22 # 比Int类型长一些 23 phone = models.BigIntegerField()
-
图书表
图书和出版社是一对多的关系
外键字段应该建立在多的一方
1 #书表 2 class Book(models.Model): 3 # id 是自动创建的,除非id名自定义 4 title = models.CharField(max_length=255) 5 # price为小数字段,表示总共8位数,其中小数占2位 6 price = models.DecimalField(max_digits=8, decimal_places=2) 7 # 书籍表和出版社表是一对多关系,外键字段要建立在书籍表中 8 Publish = models.ForeignKey(to='Publish') # 并且默认关联字段就是出版社表的主键字段,里面有个to_field参数能够修改关联的字段 9 # 补充:to后面需要加引号,若Publish表在Book表上方,则不需要加引号 10 # Publish = models.ForeignKey(to=Publish) 11 12 # 书籍与作者是多对多关系,外键字段建立在较为频繁的书籍表中,通过ManytoManyField方法就能建立第三张表 13 Authors = models.ManyToManyField(to='Author') 14 # 补充注意:Authors字段只是一个虚拟字段,并不会在Book表中创建出来,只是告诉Django ORM为多对多关系创建一个第三方表 15
-
出版社表
1 #出版社表 2 class Publish(models.Model): 3 name = models.CharField(max_length=64) 4 addr = models.CharField(max_length=64) 5
-
作者表
图书表与作者表是多对多的关系
理论上外键字段在哪张表都一样,但是推荐外键字段建立在查询较为频繁的表中
作者表与作者详情表是一对一关系
同样,还是推荐将外键字段建立在使用频率较高的表中
原因:就是为了方便后面我们基于ORM查询
# 作者表 class Author(models.Model): name = models.CharField(max_length=64) # 比Int类型长一些 phone = models.BigIntegerField() # 作者表与作者详情表一对一关系 Author_detail = models.OneToOneField(to='AuthorDetail')
- 作者详情表
1 # 作者详情表 2 class AuthorDetail(models.Model): 3 age = models.IntegerField() 4 addr = models.CharField(max_length=64)
3)开始创建
4)查看
下载之后点一下apply,将新出现的数据库链接删除,重新点一下db.sqlite3,就会出现数据库了
二.Django请求生命周期流程图
三.路由层
一般我们刚创建好的Django框架中的urls.py里面存放的是总路由,即总的路由和视图函数或app的对应关系
由上面我们可以得到:
url方法第一个参数是一个正则表达式,路由匹配按照正则匹配, 一旦正则能够匹配到内容 会立刻执行对应的视图函数,不会再继续匹配了
注意:用户输入url尾缀路由若没有加最后的斜杠,django会默认将斜杠加上,看下图(将urls中的路由加上/)
补充:你可以在配置文件settings中指定是否开启该功能APPEND_SLASH = True/False(默认是True处于打开状态,改为False则失效)
补充:网站首页一打开是非常不友好的,可以通过正则(^$)设置一下
1.无名分组和有名分组
基于路由层的路由是由正则表达式写的,我们可以在表达式后面来一个数字,如下,表示test后面必须加上四个一到九的数字才能访问
url(r'^test/[0-9]{4}', views.test),
但是,如果给正则表达式加一个括号,就表示分组的意思
url(r'^testadd/([0-9]{4})', views.testadd),
会产生下面的报错信息
解决方法:
在视图函数中给一个形参接收这个数字就可以了
def testadd(request, xxx): return HttpResponse('testadd')
什么叫无名分组?
在路由匹配的时候给某段正则表达式加了括号,匹配的时候会将括号内的正则表达式匹配到的内容当作位置参数传递给对应的视图函数,因此视图函数就要多准备一个形参去接收这个位置参数
什么叫有名分组?
给一段正则表达式起一个别名 匹配的时候会将括号内正则表达式匹配到的内容当做关键字参数传递给对应的视图函数
#有名分组 # \d+表示一个或多个数字,?P固定写法,表示给正则表达式命名为ttt url(r'^testadd/(?P\d+) ', views.testadd)
解决方法:在视图函数中添加关键字形参去接收关键字实参
# ttt就是接收关键字参数的 def testadd(request, ttt): return HttpResponse('testadd')
注意:
-
无名有名不能混合使用
url(r'^test/(\d+)/(?P\d+)/ ', views.test) # 会报找不到位置参数的错误
-
无名有名虽然不能混合使用,但是同一种命名方式 可以使用多个
1 #无名叠加 2 url(r'^test/(\d+)/(\d+)/',views.test) 3 4 #有名叠加 5 url(r'^test/(?P\d+)/(?P ',views.test),\d+)/
2.反向解析
通过一些方法 能够得到一个结果 该结果可以访问到对应的url
例如:可以通过html页面访问另一个url页面
但是,如果将urls中的test名修改了,后面的html所有关于这个的url链接都将失效
如何解决此问题?
-
第一步:
先给路由与视图函数对应关系 起一个名字(相当于赋予一个令牌)
url(r'^test/',views.testadd,name='add')
-
第二步:若是前端解析,将{% url ‘add’ %}添加html中
-
第三不:若是后端解析,先在view.py导入reverse模块
1 url(r'^test/',views.testadd,name='add') 2 3 4 from django.shortcuts import reverse 5 6 def test(request): 7 _url = reverse('add') 8 print(_url) 9 10 # 通过reverse 模块就能拿到test路由名 11 # /test/ 12 # 注意:一定要提前命名name,否则reverse会找不到
3.有名无名反向解析
分析:先在urls.py文件中建立有名无名分组
# 无名分组 url(r'^testadd/([0-9]{4})', views.testadd, name='add'), #有名分组 url(r'^test/(?P\d+) ', views.test),
然后views中导入reverse模块,运行home函数
-
无名后端反向解析
1 from django.shortcuts import render, HttpResponse, redirect, reverse 2 3 def test(request, ttt): 4 return HttpResponse('test') 5 6 7 def testadd(request, xxx): 8 return HttpResponse('testadd') 9 10 11 def home(request): 12 _url = reverse('add') 13 print(_url) 14 return HttpResponse('欢迎欢迎')
运行后会报错,表示查找add时缺一个参数
报错原因: 访问这条url:url(r'^testadd/([0-9]{4})', views.testadd, name='add')
必须以/testadd/1234/这种形式,否则无法访问,因此结果的不确定性让Django无法做判断,因此需要人为的给予数字
解决方案:在_url = reverse('add')
中随意给一个数字,例如
_url = reverse('add', args=(1234,)) # 注意:此处要以元组的形式添加 #补充:这个数字通常就是数据的主键值
再次启动访问home的url就能得到/testadd/1234,通过他就能访问testadd这条url了
-
无名前端反向解析
def home(request): return render(request, 'home.html')
1 2 3 "{% url 'add' 1234 %}">2222 4 "{% url 'add' 5628 %}">2222 5 "{% url 'add' 3637 %}">2222 6 "{% url 'add' 3839 %}">2222
同上可以获得
# 有名分组 url(r'^test/(?P\d+) ', views.test)
-
有名后端反向解析
1 # 在reverse中添加kwargs={‘名字’:符合正则的符号} 2 def home(request): 3 _url = reverse('add', kwargs={'year':11}) # 标准写法 4 _url = reverse('add', args=(11,) 5 print(_url) 6 return HttpResponse('欢迎欢迎')
-
有名前端反向解析
{% url 'add' 1 %} # 推荐使用 {% url 'add' year= 1 %} # 标准的
总结:
1 # 无名分组反向解析: 2 3 url(r'^testadd/(\d+)/',views.testadd,name='add') 4 #前端解析 5 {% url 'add' 1 %} 6 #后端解析 7 reverse('add',args=(12,))
#
1 有名分组反向解析: 2 3 url(r'^testadd/(\d+)/',views.testadd,name='add') 4 5 #前端解析 6 {% url 'add' 1 %} # 推荐使用 7 {% url 'add' year = 1} # 标准使用 8 #后端解析 9 _url = reveerse('add', args=(11,) # 推荐使用 10 _url = reveerse('add', kwargs={'year':11} # 标准使用
#伪代码 url(r'^edit_user/(\d+)/',views.edit_user,names='edit') {% for user_obj in user_queryset %} "edit_user/{{ user_obj.id }}/">编辑 "{% url 'edit' user_obj.id %}">编辑 {% endfor %} def edit_user(request,edit_id): reverse('edit',args=(edit_id,)) """
4.路由分发
1).什么是路由分发?
当django项目比较庞大的时候 路由与视图函数对应关系较多
总路由代码太多冗长
考虑到总路由代码不好维护 django支持每个app都可以有自己的urls.py
并且总路由不再做路由与视图函数的对应关系 而仅仅只做一个分发任务的操作
根据请求的不同 识别出当前请求需要访问的功能属于哪个app然后自动
下发到对应app里面的urls.py中 然后由app里面的urls.py做路由与视图函数的匹配
不仅如此每个app除了可以有自己的urls.py之外 还可以有自己的static文件夹 templates模板文件
基于上面的特点 基于django分小组开发 会变得额外的简单
每个人只需要开发自己的app即可 之后只需要创建一个空的django项目
将多个人的app全部拷贝项目下 配置文件注册
总路由分发一次
2).如何实现一个简单的路由分发
第一步:在控制台通过命令行创建多个app文件夹,并将app在settings中注册
第二步:建立urls,导入include模块,各种导入,看下图
注意事项:
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^app01/', include(app01_urls)), url(r'^app02/', include(app02_urls)), ] # 在总路由^app01/后面千万不能添加$,否则无法找到
补充:简写写法
# 将导入和路由对应关系全部拿掉,用app.urls的方法 # from app01 import urls as app01_urls # from app02 import urls as app02_urls # url(r'^app01/',include(app01_urls)), # url(r'^app02/',include(app02_urls)) # 简写 url(r'^app01/',include('app01.urls')), url(r'^app02/',include('app02.urls'))
5.名称空间(了解)
问题:如果在不同的app中的路由对应关系中添加了相同的name,那么在反向解析的时候能否准确找到
结果是一定的,不能准确找到,那如何解决这个问题?
可以再主路由对应列表中添加namespace属性,如下所示
url(r'^app01/', include('app01.urls', namespace='app01')) url(r'^app02/', include('app02.urls', namespace='app02'))
后端解析
reverse('app01:id') reverse('app02:id')
前端解析
{% url 'app01:index' %} {% url 'app02:index' %}
如何避免使用这种方法?
1 # 在给路由与视图函数起别名(name的名字)的时候只需要保证永远不出现冲突的情况即可 2 # 通常情况下我们推荐期别名的时候加上当前应用的应用名前缀 3 #例如 4 url(r'^index/',views.index,name='app01_index') 5 url(r'^index/',views.index,name='app02_index')
6.虚拟环境
为什么使用虚拟环境?
我们想做到针对不同的项目 只安装项目所需要的功能模块 项目用不到的一概不装 来避免加载资源时的消耗
注意:这次创建需要用以前创立好的虚拟环境来创立
虚拟环境就类似于一个纯净的python解释器环境
每创建一个虚拟幻境就类似于你重新下载一个python解释器
另外虚拟环境不推荐使用太多,可以创建几个玩一玩,否则占用资源
7.DJango版本区别
路由层:
-
用的是url,第一个参数是正则表达式
-
2.x和3.x用的path,第一个参数不是正则表达式,写的是什么就匹配什么,精确匹配
1.x版本 urlpatterns = [ url(r'^admin/', admin.site.urls), ] 2.x和3.x版本 urlpatterns = [ path('admin/', admin.site.urls), ]
如果觉得path不好用 2.x、3.x给你提供了一个跟url一样的功能 re_path 等价于1.x里面的url功能
虽然path不支持正则表达式 但是它给你提供了五种默认的转换器
-
str:匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
-
int:匹配正整数,包含0。
-
slug,匹配字母、数字以及横杠、下划线组成的字符串。
-
uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
-
path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)
除了默认的五种转换器之外 还支持你自定义转换器
1 class MonthConverter: 2 regex='\d{2}' # 属性名必须为regex 3 4 def to_python(self, value): 5 return int(value) 6 7 def to_url(self, value): 8 return value # 匹配的regex是两个数字,返回的结果也必须是两个数字
8.伪静态
伪静态 url以.html结尾 给人的感觉好像是这个文件是写死的 内容不会轻易的改变 伪静态
为了提高你的网站被搜索引擎收藏的力度 提供网站的SEO查询效率
但是 无论你怎么做优化 都抗不过RMB玩家
四.视图层
1)JsonResponse
视图函数必须要返回一个HttpResponse对象,因为比如这三板斧都是直接或间接的继承HTTresponse
-
HttpResponse
-
render
-
redirect
JsonResponse
前后端数据交互
举例:
from django.http import JsonResponse def xxx(request): user_dict = {'username':'jason','password':'123'} json_str=json.dumps(user_dict) return HttpResponse(json_str)
若添加中文进去,就会发生自动转码的事情,如下图
from django.http import JsonResponse def xxx(request): user_dict = {'username':'jason好帅哦 我好喜欢!','password':'123'} json_str=json.dumps(user_dict) return HttpResponse(json_str)
原因:
所以只要将ensure_ascii修改成False即可
json_str = json.dumps(user_dict,ensure_ascii=False)
也可以使用JsonResponse
return JsonResponse(user_dict,json_dumps_params={'ensure_ascii':False})
若是一个列表
l = [1,2,3,4,5,6,7,8,9,] return JsonResponse(l,safe=False) # 序列化非字典格式数据 需要将safe改为False
forml表单
1.必须做的事
method必须是post
enctype必须是formdata
2.暂时需要做的
提交post请求需要将中间件里面的一个csrfmiddleware注释掉
后端如何获取用户上传的文件
file_obj = request.FILES.get('前端input框name属性值')
file_obj.name # 文件名
for line in file_obj:
print(line)
# django中推荐以下写法
for chunk in file_obj.chunks():
print(chunk)
render原理
def ab_render(request): temp = Template("{{ user_dict }}{{ user_dict.username }}{{ user_dict.password }}
") user_dict = Context({'user_dict':{'username':'jason','password':123}}) res = temp.render(user_dict) return HttpResponse(res)
补充:视图函数不一定是函数,也有可能是类
FBV:基于函数的视图
CBV:基于类的视图
CBV基本写法
from django.views import View class MyLogin(View): def get(self,request): return render(request,'login.html') def post(self,request): return HttpResponse('我是类里面的post方法') url(r'^login/',views.MyLogin.as_view())
朝login提交get请求会自动执行MyLogin里面的get方法
而提交post请求也会自动执行MyLogin里面的post方法
为什么MyLogin针对不同的请求方法能够自动执行对应的方法
研究源码的突破口
url(r'^login/',views.MyLogin.as_view())
猜想
as_view要么是类里面定义的普通函数 @staticmethod
要么是类里面定义的绑定给类的方法 @classmethod
看源码发现是绑定给类的方法