Django表关系、路由层、视图层介绍
文章目录
Django表关系、路由层、视图层介绍
一、Django orm表关系创建
表关系的创建
- 一对多
- 多对多
- 一对一
图书管理系统举例:
图书表:
图书和出版社是一对多的外键关系
一对多外键关系,外键字段建在多的那一方
出版社表:
作者表:
图书与作者是多对多外键关系
多对多外键关系,外键字段无论建在哪张表里面都可以.(推荐建在查询频率较高的那张表中)
作者与作者详情表是一对一的外键关系(一对一外键关系,外键字段建在那张表里面都可以,但还是推荐建在查询频率高的那张表中)
#方便后面基于orm查询
foreignkey字段以及OneToOneField、ManyToManyField字段,在创建表的时候orm都会自动给该字段加_id的后缀
创建:
在书写表关系的时候 要先把基表全部写出来 之后再考虑外键字段
from django.db import models
#图书表
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)
二、Django请求生命周期流程图
三、路由层
路由匹配
注意注意:url匹配是Django1.x的使用方式,现在官方推荐使用Django2.x的path来进行路由匹配,有关Django2.x的path用法请参考Django路由匹配之path使用方法
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'test',views.test),
url(r'testadd',views.testadd)
]
#用户输入url不加最后的斜杆,django会默认自动加上,你可以在配置文件中指定是否开启该功能
APPEND_SLASH = True/False
url方法第一个参数是一个正则表达式
路由匹配按照正则匹配。一旦正则能够匹配到内容,会立即执行对应的视图函数,不会再继续匹配了。
无名分组
分组就是给一段正则表达式加括号
在路由匹配的时候给某段正则表达式加了括号
匹配的时候会将括号内正则表达式匹配到的内容当作位置参数传递给对应的视图函数
#无名分组
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')
前端解析
{% url 'add' %}
后端解析
from django.shortcuts import render,HttpResponse,redirect,reverse
reverse('add')
无名分组反向解析
无名分组反向解析
url(r'^testadd/(\d+)/',views.testadd,name='add')
前端解析
{% url 'add' 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全部拷贝到项目下,配置文件注册,总路由分发一次。
需要一个分发模块
-
总路由
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'))
-
子路由
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^index/',views.index)
]
from django.conf.urls import url
from app02 import views
urlpatterns = [
url(r'^index/',views.index)
]
名称空间(了解)
url(r'^app01/',include('app01.urls',namespace='app01'))
url(r'^app02/',include('app02.urls',namespace='app02'))
后端解析
reverse('app01:index')
reverse('app02: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玩家。
七、视图层
三板斧
视图函数必须要返回一个HttpResponse对象(现返回自定义数据,可以用下面代码框中的JsonResponse代替,因为JsonResponse内部也是通过HttpResponse来返回数据的,但是JsonResponse增加了下面所说的方法。)
HttpResponse
redirect
render
JsonResponse
ensure_ascii=False,如果为true,页面展示中文时是其他类型数据,手动改为false,告诉json字典里面是什么数据,就显示什么数据
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表单上传文件 后端如何获取
HTML页面提交文件
enctype formdata
#HTML页面提交文件
<form action="" method="post" enctype="multipart/form-data">
<input type="file" name="myfile">
<input type="submit">
</form>
视图函数,接收文件并保存文件
method post
#视图函数,接收文件并保存文件
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')