Django框架进阶2 django orm表关系创建,django请求生命周期流程图,路由层(路由匹配,无名分组,有名分组,反向解析,路由分发,名称空间),虚拟环境,django版本区别,伪静态,视图层(三板斧继承HttpResponse,JsonResponse,form表单上传文件 后端如何获取)
django orm表关系创建
一对多
多对多
一对一
以图书管理管理系统
图书表
图书和出版社是一对多关系
一对多外键关系 外键字段建在多的那一方
出版社
作者表
图书与作者是多对多外键关系
多对多外键关系 外键字段无论建在哪张表里面都行(但是推荐建在查询频率高的那种表中)
作者与作者详情是一对一的外键关系
一对一外键关系 外键字段建在哪张表里面都可以(但是推荐你建在查询频率较高的那种表中)
# 方便后面基于orm查询
注意: ForeignKey字段以及OneToOneField字段 在创建表的时候orm都会自动给该字段加_id的后缀
在书写表关系的时候 要先把基表全部写出来 之后再考虑外键字段
from django.db import models # Create your models here. class Book(models.Model): # id是自动创建的 我们就不写了 title = models.CharField(max_length=64) # price为小数字段 总共8位小数位占2位 price = models.DecimalField(max_digits=8,decimal_places=2) # 书籍与出版社 是一对多外键关系 publish = models.ForeignKey(to='Publish') # 默认关联字段就是出版社表的主键字段 # publish = models.ForeignKey(to=Publish) # to后面也可以直接写表的变量名 但是需要保证该变量名在当前位置的上方出现 # 书籍与作者 是多对多外键关系 authors = models.ManyToManyField(to='Author') # 书籍和作者是多对多关系 """ authors字段是一个虚拟字段 不会真正的在表中创建出来 只是用来告诉django orm 需要创建书籍和作者的第三张关系表 """ class Publish(models.Model): name = models.CharField(max_length=64) addr = models.CharField(max_length=64) class Author(models.Model): name = models.CharField(max_length=32) phone = models.BigIntegerField() # 一对一外键关系建立 author_detail = models.OneToOneField(to='AuthorDetail') class AuthorDetail(models.Model): age = models.IntegerField() addr = models.CharField(max_length=255)
在pycharm快捷窗口输入数据库迁移命令
makemigrations
migrate
运行项目
打开自带的db.sqlite3,pycharm选择数据库连接类型为SQLite
第一次连接,需要点击左下角安装连接SQLite数据库驱动,然后直接点击测试连接,通过即可
生成如下数据数据库
django请求生命周期流程图
路由层
路由匹配
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'test',views.test), url(r'testadd',views.testadd) ]
url方法第一个参数是一个正则表达式
路由匹配按照正则匹配 一旦正则能够匹配到内容 会立刻执行对应的视图函数
不会再继续匹配了
注意:用户输入url不加最后的斜杠 django会默认自动加上
可以在配置文件中指定是否开启该功能 APPEND_SLASH = True/False
APPEND_SLASH = True # 默认,不匹配会加上斜杠测试 APPEND_SLASH = False # 不匹配,就报错,不会加上斜杠再测试
有时候测试浏览器有缓存测试不出,F12打开调试页面,之前设定自动清缓存,就会出结果
分组就是给一段正则表达式加括号
补充:网站首页一打开是非常不友好的,可以通过正则(^$)设置一下
无名分组
在路由匹配的时候给某段正则表达式加了括号
匹配的时候会将括号内正则表达式匹配到的内容当做位置参数传递给对应的视图函数
# 无名分组 url(r'^test/([0-9]{4})/',views.test) # 视图函数 def index(request,args): return HttpResponse('')
有名分组
给一段正则表达式起个别名
匹配的时候会将括号内正则表达式匹配到的内容当做关键字传递给对应的视图函数
# 有名分组 url(r'^test/(?P<year>\d+)/',views.test) # 视图函数 def index(request,year): ...
补充:
# 无名有名能否混合使用 >>> 不能!!! # url(r'^test/(\d+)/(?P<year>\d+)/', views.test), # 虽然不能混合使用 但是同一种命名方式 可以使用多个 # url(r'^test/(\d+)/(\d+)/',views.test), url(r'^test/(?P<xxx>\d+)/(?P<year>\d+)/',views.test),
反向解析
通过一些方法 能够得到一个结果 该结果可以访问到对应的url
使用方法
先给路由与视图函数对应关系 起一个名字
url(r'^testadd/',views.testadd,name='add')
前端解析
# <a href="{% url 'add' %}">111</a> 在对应链接进行替换 {% url 'add' %}
后端解析
from django.shortcuts import render,HttpResponse,redirect,reverse reverse('add') # 获得对应的实际url
无名分组反向解析
# 无名分组反向解析 url(r'^testadd/(\d+)/',views.testadd,name='add')
前端解析
# <a href="{% url 'add' 1 %}">111</a> 替换链接 {% url 'add' 1 %} # 设定无名分组位置参数为1,即为 testadd/1/
后端解析
reverse('add',args=(12,)) # 参数为元祖
有名分组反向解析
# 有名分组反向解析 url(r'^testadd/(?P<year>\d+)/',views.testadd,name='add')
前端解析
{% url 'add' 1 %} # 推荐使用 {% url 'add' year= 1 %} # 标准的
后端解析
reverse('add',args=(12,)) reverse('add',kwargs={'year':12})
解析作用(伪代码诠释)
""" url(r'^edit_user/(\d+)/',views.edit_user,names='edit') {% for user_obj in user_queryset %} <a href="edit_user/{{ user_obj.id }}/">编辑</a> <a href="{% url 'edit' user_obj.id %}">编辑</a> {% endfor %} def edit_user(request,edit_id): reverse('edit',args=(edit_id,)) """
路由分发
当django项目比较庞大的时候 路由与视图函数对应关系较多,总路由代码太多冗长
考虑到总路由代码不好维护 django支持每个app都可以有自己的urls.py
并且总路由不再做路由与视图函数的对应关系 而仅仅只做一个分发任务的操作
根据请求的不同 识别出当前请求需要访问的功能属于哪个app,自动下发到对应app里面的urls.py中 然后由app里面的urls.py做路由与视图函数的匹配
不仅如此每个app除了可以有自己的urls.py之外 还可以有自己的static文件夹 templates模板文件
基于上面的特点 基于django分小组开发 会变得额外的简单
每个人只需要开发自己的app即可 之后只需要创建一个空的django项目
将多个人的app全部拷贝项目下 配置文件注册,总路由分发一次
需要一个分发的模块
1.总路由
from django.conf.urls import url,include # 路由分发 注意事项应用名后面千万不能加$ # 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'))
2.子路由
# app01 from django.conf.urls import url from app01 import views urlpatterns = [ url(r'^index/',views.index) ] # app02 from django.conf.urls import url from app02 import views urlpatterns = [ url(r'^index/',views.index) ]
http://127.0.0.1:8000/app01/index/
名称空间(了解)
# app下name都为index urlpatterns = [url(r'^index/', views.index,name='index')]
注:在路由分发下,反向解析当前名字所在应用app,但name相同时,会混淆出错
使用名称空间
url(r'^app01/',include('app01.urls',namespace='app01')) url(r'^app02/',include('app02.urls',namespace='app02'))
后端解析 reverse导入也可以用此路径 (from django.core.urlresolvers import reverse)
reverse('app01:index') reverse('app02:index')
例如返回应答, 跳转到该页
return redirect(reverse('goods:index'))
前端解析
{% url 'app01:index' %} {% url 'app02:index' %}
注意:可以不用名称空间避免该问题
# 在给路由与视图函数起别名的时候只需要保证永远不出现冲突的情况即可
# 通常情况下我们推荐期别名的时候加上当前应用的应用名前缀
url(r'^index/',views.index,name='app01_index') url(r'^index/',views.index,name='app02_index')
虚拟环境
我们想做到针对不同的项目 只安装项目所需要的功能模块
项目用不到的一概不装 来避免加载资源时的消耗
如何创建虚拟环境
注意:这次创建需要用以前创立好的虚拟环境来创立
虚拟环境就类似于一个纯净的python解释器环境
大白话 没创建一个虚拟环境就类似于你重新下载一个python解释器
虚拟环境不推荐你使用太多
学习阶段我们还是用本机的环境即可 将所有模块全部装到本机环境下
django版本区别
路由层
1.X用的是url
2.X、3.X用的是path
url第一个参数是一个正则表达式
而path第一个参数不支持正则表达式 写什么就匹配什么
如果你觉得path不好用 2.x、3.x给你提供了一个跟url一样的功能
re_path 等价于1.x里面的url功能
虽然path不支持正则表达式 但是它给你提供了五种默认的转换器
str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
int,匹配正整数,包含0。
slug,匹配字母、数字以及横杠、下划线组成的字符串。
uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)
path('login/<int:year>/',login)
除了默认的五种转换器之外 还支持你自定义转换器
class MonthConverter: regex='\d{2}' # 属性名必须为regex def to_python(self, value): return int(value) def to_url(self, value): return value # 匹配的regex是两个数字,返回的结果也必须是两个数字
伪静态
url以.html结尾 给人的感觉好像是这个文件是写死的 内容不会轻易的改变
伪静态
为了提高你的网站被搜索引擎收藏的力度 提供网站的SEO查询效率
但是 无论你怎么做优化 都抗不过RMB玩家
修改urls.py(Django3.0环境下)
from django.contrib import admin from django.urls import path,re_path from django.shortcuts import HttpResponsedef reg(request): return HttpResponse('reg') urlpatterns = [ path('admin/', admin.site.urls), path('reg.html',reg) ]
视图层
视图函数必须要返回一个HttpResponse对象,三板斧直接间接继承了HttpResponse
HttpResponse
render
redirect
JsonResponse
from django.http import JsonResponse def xxx(request): user_dict = {'username':'jason好帅哦 我好喜欢!','password':'123'} # json_str = json.dumps(user_dict,ensure_ascii=False) # return HttpResponse(json_str) l = [1,2,3,4,5,6,7,8,9,] # return JsonResponse(user_dict,json_dumps_params={'ensure_ascii':False}) return JsonResponse(l,safe=False) # 序列化非字典格式数据 需要将safe改为False
前后端数据交互
form表单上传文件
action
method post
enctype formdata # 控制的数据提交的编码格式
# 默认情况下form表单是不能够直接发送文件的
# 如果你要发送文件 必须将该参数有默认的urlencoded改为formdata
例:form表单选择图片,下载至本地
templates/home.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> </head> <body> <form action="" method="post" enctype="multipart/form-data"> <input type="file" name="myfile"> <input type="submit"> </form> </body> </html>
urls.py
from django.conf.urls import url,include from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^app01/',include('app01.urls')), url(r'^app02/',include('app02.urls')), url(r'^index/', views.xxx), url(r'^home/', views.home), ]
app01/views.py
from django.shortcuts import render,HttpResponse,redirect def home(request): if request.method == 'POST': # 获取用户上传的文件数据 print(request.FILES) file_obj = request.FILES.get('myfile') print(file_obj.name) # 获取文件名 with open(file_obj.name,'wb') as f: # 写入当前文件夹保存 for line in file_obj: f.write(line) return render(request,'home.html')