Django建立表关系,Django生命周期图之路由层和视图层简介

一.Django框架中rom的表关系建立

1).表与表的关系

  • 一对一html

  • 一对多前端

  • 多对多python

    具体sql语句及关系请查看数据库基础连接git

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()
  • 图书表sql

    图书和出版社是一对多的关系数据库

    外键字段应该创建在多的一方django

 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
  • 出版社表json

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>固定写法,表示给正则表达式命名为ttt
    url(r'^testadd/(?P<ttt>\d+)', views.testadd)

 

解决方法:在视图函数中添加关键字形参去接收关键字实参

# ttt就是接收关键字参数的
def testadd(request, ttt): return HttpResponse('testadd')

注意:

  • 无名有名不能混合使用

url(r'^test/(\d+)/(?P<year>\d+)/', views.test) # 会报找不到位置参数的错误
  • 无名有名虽然不能混合使用,可是同一种命名方式 可使用多个

1 #无名叠加
2 url(r'^test/(\d+)/(\d+)/',views.test) 3 4 #有名叠加
5 url(r'^test/(?P<xxx>\d+)/(?P<year>\d+)/',views.test),

2.反向解析

经过一些方法 可以获得一个结果 该结果能够访问到对应的url

例如:能够经过html页面访问另外一个url页面

可是,若是将urls中的test名修改了,后面的html全部关于这个的url连接都将失效

如何解决此问题?

  • 第一步:

    先给路由与视图函数对应关系 起一个名字(至关于赋予一个令牌)

url(r'^test/',views.testadd,name='add')

  • 第二步:如果前端解析,将{% url ‘add’ %}添加html中

<a href="{% url 'add' %}">2222</a>

  • 第三不:如果后端解析,先在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<ttt>\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 <a href="{% url 'add' 1234 %}">2222</a>
4 <a href="{% url 'add' 5628 %}">2222</a>
5 <a href="{% url 'add' 3637 %}">2222</a>
6 <a href="{% url 'add' 3839 %}">2222</a>

同上能够得到

# 有名分组
url(r'^test/(?P<ttt>\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 %} <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,)) ​ """

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("<h1>{{ user_dict }}{{ user_dict.username }}{{ user_dict.password }}</h1>")   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

看源码发现是绑定给类的方法

相关文章
相关标签/搜索