Django框架3
一.表关系的创建
一对多,多对多以及一对一
以图书管理系统为例,来实现这几个关系
图书表
图书和出版社是一对多的关系
一对多外键关系,外键字段放在多的那一边
出版社
作者表
图书与作者是多对多的关系
多对多外键关系,外键字段无论建立在哪一张表里都可以,但是推荐建立在查询评率比较高的那张表里
作者与作者详情是一对一的关系
一对一外键关系,外键字段建在哪一张表里都可以,同样推荐建立在查询频率比较高的那张表里
方便我们后面基于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=255)
# 作者表
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请求生命周期流程图
三.路由层
1.路由匹配
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'test/',views.test),
url(r'testadd/', view.testadd)
]
url方法第一个参数是一个正则表达式
路由匹配按照正则匹配 一旦正则能够匹配到内容 会立刻执行对应的视图函数,不会在继续匹配
用户在输入url时,不加最后的斜杠,django会默认自动加上,
可以在配置文件中指定是否开启功能
APPEND_SLASH = True/False
^表示以什么开头,$表示以什么来结尾
这样写会导致永远访问不到testadd,总是被test拦截
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^test/',views.test),
url(r'^testadd/', view.testadd)
]
这样写又会导致test/后面无论写什么也会导致可以访问
所以再次修改
urlpatterns = [
url(r'^admin/$', admin.site.urls),
url(r'^test/$',views.test),
url(r'^testadd/$', view.testadd)
]
首页匹配
url(r'^$', views.hone)
2.无名分组与有名分组
无名分组
在路由匹配的时候给某段正则表达式加了括号
匹配的时候会将括号内的正则表达式匹配到的内容当做位置参数传递给对应的是函数
# 无名分组
url(r'test/([0-9]{4})/', view.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/(?P<year>d+)/(?P<month>d+)', views.test)
或者
url(r'test/(d+)/(d+)', views.test)
3.反向解析
通过一些方法,能够得到一个结果,该结果可以访问到对应的url
使用方式
先给路由和视图函数对应关系起一个名字
url(r'^testadd/', view.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, name='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, ))
练习
用反向解析加无名分组,把用户的编辑功能实现
# urls.py文件
urlpatter = [ url(r'^edit_user/(d+)', view.edit_user, name='edit')]
# 编辑功能
def edit_user(request, edit_id):
# 获取用户要修改的数据的id值
# edit_id = request.GET.get('edit_id')
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
# 修改方式
models.User.objects.filter(id=edit_id).update(name=username, password=password)
# 再次跳转到数据展示页面
return redirect('/main')
# 根据id获取数据对象 并且展示到html页面上
edit_obj = models.User.objects.filter(id=edit_id).first()
return render(request, 'editor.html', {'edit_obj': edit_obj})
# 前端解析
<a href="{% url 'edit' obj.id %}" class="btn btn-primary"> 编辑</a>
4.路由分发
当django项目比较庞大时,路由与视图函数对应关系比较多
总路由代码太冗余,考虑到总路由的代码不好维护,django支持每个app都可以有自己的urls.py,并且总路由不再做路由和视图函数的对应关系,而仅仅做分发任务的工作
根据请求的不同,识别出当前请求需要访问的功能属于哪一个app然后自动下发到对应的app里面的urls.py中,然后由app里面的urls.py做路由与视图函数的匹配
每个app除了有自己的urls.py文件以外,还可以有自己的static文件夹 templates模板文件
所有分组开发,可以实行
每个人只需开发自己的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.子路由
from djano.conf.urls import url,include
from app01 import views
urlpatterns = [
url(r'^index/', views.index)
]
from django.conf.urls import url, include
from appo2 import views
urlpatterns = [
url(r'^index/', views.index)
]
5.名称空间
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')
6.虚拟环境
我们要做到针对不同的项目,只安装项目所需要的功能模块
项目用不到的不需要装,为了避免加载资源时的消耗
如何去创建虚拟环境
虚拟环境就类似于一个纯净的python解释器环境
但是虚拟环境不推荐使用过多
7.django版本的区别
路由层:
1.x用的是url
2.x和3.x用的是path
1. url第一个参数是一个正则表达式
而path第一个参数不支持正则表达式 写什么就匹配什么
如果你觉得path不好用 2.x、3.x给你提供了一个跟url一样的功能
re_path 等价于1.x里面的url功能
2. 虽然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是两个数字,返回的结果也必须是两个数字
8.伪静态
url以.html结尾,让人联想成死的,内容不会改变称为伪静态
为了提高你的网站被搜索引擎收藏的力度 提供网站的SEO查询效率
四.视图层
视图函数必须要返回一个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表单上传文件
1.必须要做的事
action
method post
enctype formdata
2.暂时要做的事
提交post请求需要将中间件里面的一个csrfmiddleware注释掉
后端如何获取用户上传的文件
file_obj = request.FILES.get('前端input框name属性值')
file_ob.name # 获取文件名
for line in file_obj:
print(line)
# django中推荐以下写法
for chunk in file_obj.chunks():
print(chunk)