Django进阶2

Django ORM中创建表关系

"""
表与表之间的关系
    一对多
    
    多对多
    
    一对一
    
    没有关系

判断表关系的方法:换位思考
"""

我们以图书表,出版社表,作者表,以及作者详情表为例:

# 图书表  
# 出版社表
# 作者表
# 作者详情表

"""
图书和出版社是一对多的关系,外键字段建在多的一方,图书是多的一方
图书和作者是多对多的关系,要建一个中间表book2author记录图书和作者的关系
作者表和作者详情表是一对一
"""

Django连接数据库

settings.py,注释原有数据库配置

# 连接mysql数据库
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'day61',    # name对应数据库的名字
        'USER': 'root',
        'PASSWORD': '123456',
        'HOST': '127.0.0.1',
        'PORT': 3306,
        'CHARSET': 'utf8'
    }
}

在项目或应用下的__init__.py中添加

import pymysql
pymysql.install_as_MySQLdb()

创建模型类:ps:django1.11.11中orm创建的外键字段默认就是级联更新、级联删除的

models.py

from django.db import models

# Create your models here.
# 创建表关系 先将基表创建出来,然后再添加外键字段

# 图书表
class Book(models.Model):
    title = models.CharField(max_length=32,verbose_name="图书名")
    price = models.DecimalField(max_digits=8,decimal_places=2,verbose_name="价格")
    # max_digits=8,decimal_places=2:小数总共八位,小数点后面占两位
    """
    图书和出版社是一对多关系,并且图书是多的一方,所以外键字段放在图书表里
    """
    publish = models.ForeignKey(to="Publish")          #一对多表关系  #publish是和Publish表关联的外键字段,默认是和Publish表的主键id字段关联,也可以使用to_field="id"指定

    """
    图书和作者是多对多关系,在ORM中不用手动创建中间表了,ORM会自动帮我们创建中间表,ORM中外键字段建在任意一方均可,但是推荐建在查询频率较高的一方
ps:
如果字段对应的是ForeignKey 那么orm会自动在字段后面加上_id后缀
如果你自己加上了_id那么orm还是会再自动在字段后面加上_id后缀
后面在定义ForeignKey的时候就不要自己加_id了
""" authors = models.ManyToManyField(to="Author") # 多对多表关系 """ authors 是一个虚拟字段,主要用来告诉ORM 图书表和作者表是多对多的关系 让ORM自动帮你创建第三方中间表 """ # 出版社 class Publish(models.Model): name = models.CharField(max_length=32,verbose_name="出版社名称") addr = models.CharField(max_length=32,verbose_name="出版社地址") # 作者 class Author(models.Model): name = models.CharField(max_length=32,verbose_name="姓名") age = models.IntegerField(verbose_name="年龄") """ 作者和作者详情是一对一关系,外键字段建在任意一方都可以,推荐建在查询频率较高的一方,这里外键建在作者表中 """ author_detail = models.OneToOneField(to="AuthorDetail") #一对一表关系
# OneToOneField也会自动给字段加_id后缀
#作者详情表 class AuthorDetail(models.Model): phone = models.BigIntegerField(verbose_name="手机号") addr = models.CharField(max_length=32,verbose_name="作者地址")

执行数据库迁移命令

# python3 manage.py makemigrations
# python3 manage.py migrate

查看数据库中的表

 

解释:

圈红圈的忽略:django自动创建的

app01_author:作者表

app01_authordetail:作者详情表

app01_book:图书表

# 图书表
class Book(models.Model):
    title = models.CharField(max_length=32,verbose_name="图书名")
    price = models.DecimalField(max_digits=8,decimal_places=2,verbose_name="价格")
    # max_digits=8,decimal_places=2:小数总共八位,小数点后面占两位
    """
    图书和出版社是一对多关系,并且图书是多的一方,所以外键字段放在图书表里
    """
    publish = models.ForeignKey(to="Publish")          #一对多表关系  #publish是和Publish表关联的外键字段,默认是和Publish表的主键id字段关联,也可以使用to_field="id"指定

    """
    图书和作者是多对多关系,在ORM中不用手动创建中间表了,ORM会自动帮我们创建中间表,ORM中外键字段建在任意一方均可,但是推荐建在查询频率较高的一方
    """
    authors = models.ManyToManyField(to="Author")      # 多对多表关系
    """
    authors 是一个虚拟字段,主要用来告诉ORM 图书表和作者表是多对多的关系
    让ORM自动帮你创建第三方中间表
    """
图书表模型类代码

 我们发现代码中加上id主键应该有五个字段(id、title、price、publish、authors)

但是在数据库表中只有(id、title、price、publish_id)

现在来解释:

为什么代码里写的外键名是publish,但是在数据库中是publish_id?

#ForeignKey   一对多外键
# OneToOneField  一对一外键
# 会自动在字段后面加_id后缀

为什么只有四个字段?

# 因为代码中authors是虚拟字段,主要用来告诉ORM 图书表和作者表是多对多的关系,让ORM自动帮你创建第三方中间表(就是多出来的表app01_book_authors)

app01_book_authors:这是django自动帮我们创建的图示表与作者表之间的中间表

app01_publish:出版社表

orm表与表关系创建总结

# orm中如何定义三种关系
    publish = models.ForeignKey(to='Publish')  # 一对多,默认就是与出版社表的主键字段做外键关联,也可以使用to_field="id"指定

    authors = models.ManyToManyField(to='Author')   # 多对多表关系
        
    author_detail = models.OneToOneField(to='AuthorDetail')   # 一对一表关系
        
# ps:        
    ForeignKey
    OneToOneField
    会自动在字段后面加_id后缀

补充:

# 在django1.X版本中外键默认都是级联更新删除的
# 多对多的表关系可以有好几种创建方式 这里暂且先介绍一种

补充2:

"""
  一对多
      models.ForeignKey(to='关联表名')  常用
      models.ForeignKey(to=关联表名)      关联表名必须出现在上方(了解即可)
          1.在django1.X版本中外键默认就是级联更新删除的
          2.会自动给字段加_id后缀 无论你有没有加(自己不要自作聪明的加上)
          3.一对多 外键字段建在多的一方
          
  一对一
      models.OneToOneField(to='关联表名')
          1.在django1.X版本中外键默认就是级联更新删除的
          2.会自动给字段加_id后缀 无论你有没有加(自己不要自作聪明的加上)
          3.外键建在任意一方均可 但是推荐你建在查询频率较高的表中(orm查询方便)
          
  多对多
      models.ManyToManyField(to='关联表名')
          1.在django1.X版本中外键默认就是级联更新删除的
          2.该字段是一个虚拟字段不会真正的在表中展示出来 而是用来告诉Django orm当前表和关联表是多对多的外键关系 需要自动创建第三张关系表
          3.在Django orm中多对多的表关系有好几种(三种)创建方式
          4.外键建在任意一方均可 但是推荐你建在查询频率较高的表中(orm查询方便)
          
  判断表关系的方式:换位思考
  """

Django请求周期流程图(必会)

# 扩展知识点
    """
    缓存数据库
        提前已经将你想要的数据准备好了 你来直接拿就可以
        提高效率和响应时间
        
    当你在修改你的数据的时候 你会发现数据并不是立刻修改完成的
    而是需要经过一段时间才会修改
        博客园
    
    了解即可
    """

Django 每个部分详解

路由层

Django中路由的作用

URL配置(URLconf)就像Django 所支撑网站的目录。它的本质是URL与要为该URL调用的视图函数之间的映射表;你就是以这种方式告诉Django,对于客户端发来的某个URL调用哪一段逻辑代码对应执行

典型的例子:

from django.urls import path

urlpatterns = [
    path('articles', views.special),
]
articles这个路由对应着视图函数中special这个方法,浏览器输入这个链接,就会响应到special这个函数来执行

 简单的路由配置

from django.conf.urls import url
urlpatterns = [
     url(正则表达式, views视图函数,参数,别名),
]
  • 正则表达式:一个正则表达式字符串
  • views视图函数:一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串
  • 参数:可选的要传递给视图函数的默认参数(字典形式)
  • 别名:一个可选的name参数

示例:

from django.conf.urls import url
urlpatterns = [
     url("", views.index,{"name":"lqz"}),    # "name":"lqz"}参数写完,在视图层index函数就能接收到传的{"name":"lqz"},写key就行
]


# 视图层
def index(request,name):
    print(name)
    return HttpResponse("ok")

路由匹配

注意事项

1、惰性匹配

url(r'test',views.test),
url(r'testadd',views.testadd)
"""
url方法第一个参数是正则表达式
    只要第一个参数正则表达式能够匹配到内容 那么就会立刻停止往下匹配
    直接执行对应的视图函数
"""

示例:

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 路由匹配
    url(r'test', views.test),
    url(r'testadd', views.testadd),
]

views.py

from django.shortcuts import render,HttpResponse,redirect

# Create your views here.

def test(request):

    return HttpResponse("test")

def testadd(request):

    return HttpResponse("testadd")

浏览器访问时,输入的包含test就可访问

 2、自动加斜杠

url(r'test/', views.test),
url(r'testadd/', views.testadd),

"""
你在输入url的时候会默认加斜杠
    django内部帮你做到重定向
        一次匹配不行
        url后面加斜杠再来一次
"""

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 路由匹配
    url(r'test/', views.test),
    url(r'testadd/', views.testadd),
]

views.py

from django.shortcuts import render,HttpResponse,redirect

# Create your views here.

def test(request):

    return HttpResponse("test")

def testadd(request):

    return HttpResponse("testadd")
views.py

浏览器输入:http://127.0.0.1:8000/testadd访问时

你也可以在settings.py配置文件中取消django自动加斜杠/的机制APPEND_SLASH

在settings.py中添加下面代码即可:

# 取消自动加斜杠
APPEND_SLASH = False    # True是开启

补充1:

url(r'test/', views.test),
url(r'testadd/', views.testadd),

浏览器输入http://127.0.0.1:8000/dfsjfdjksjtest/也能访问test,因为正则可以匹配

 解决:加^匹配开头...

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

 补充2、基于补充1

在浏览器输入http://127.0.0.1:8000/test/fafdsdfsfds还是能访问test

 解决:加上$匹配结尾

url(r'^test/$', views.test),
url(r'^testadd/$', views.testadd),

配置网站首页

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 首页
    url(r'^$',views.home),       # 推荐写法
]

views.py

from django.shortcuts import render,HttpResponse,redirect

# Create your views here.

# 首页
def home(request):
    return HttpResponse("home")

浏览器访问:http://127.0.0.1:8000/

无名分组

"""
分组:就是给某一段正则表达式用小括号扩起来
"""

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 首页
    url(r'^$',views.home),
    # 分组
    url(r'^test/(\d+)', views.test),
]

views.py

from django.shortcuts import render,HttpResponse,redirect

# Create your views here.

# 首页
def home(request):
    return HttpResponse("home")

def test(request):

    return HttpResponse("test")

此时浏览器访问

我们接收一下这个参数看看是什么

views.py

from django.shortcuts import render,HttpResponse,redirect

# Create your views here.

# 首页
def home(request):
    return HttpResponse("home")

def test(request,xxx):
    print(xxx)
    return HttpResponse("test")

此时浏览器访问:http://127.0.0.1:8000/test/123

在后端查看打印信息:

因此我们发现:

# 无名分组
    url(r'^test/(\d+)', views.test),    # 无名分组就是将括号内正则表达式匹配到的内容当作位置参数传递给后面的视图函数

# 视图函数
def test(request,xx):
    print(xx)
    return HttpResponse('test')

有名分组

"""
可以给正则表达式起一个别名
"""

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 首页
    url(r'^$',views.home),
    # 无名分组
    url(r'^test/(\d+)', views.test),
    # 有名分组
    url(r'^testadd/(?P<year>\d+)', views.testadd),   # 给\d+这个表达式起了个别名year,这个year会传给视图函数testadd
]

views.py

from django.shortcuts import render,HttpResponse,redirect

# Create your views here.

# 首页
def home(request):
    return HttpResponse("home")

def test(request,xxx):
    print(xxx)
    return HttpResponse("test")

def testadd(request):

    return HttpResponse("testadd")

浏览器访问一下,看一下报错信息(testadd视图函数缺少一个关键字参数year)

 我们给testadd视图函数加上year关键字参数year

views.py

from django.shortcuts import render,HttpResponse,redirect

# Create your views here.

# 首页
def home(request):
    return HttpResponse("home")

def test(request,xxx):
    print(xxx)
    return HttpResponse("test")

def testadd(request,year):
    print(year)
    return HttpResponse("testadd")

浏览器访问并在后端查看这个year关键字参数打印

 

 因此我们发现:

url(r'^testadd/(?P<year>\d+)',views.testadd)    # 有名分组就是将括号内正则表达式匹配到的内容当作关键字参数传递给后面的视图函数

def testadd(request,year):
    print(year)
    return HttpResponse('testadd')

无名、有名分组是否可以混合使用

"""
两者不能混用
但是同一个分组可以使用N多次
"""

# 单个的分组可以使用多次
url(r'^index/(\d+)/(\d+)/(\d+)/',views.index),
url(r'^index/(?P<year>\d+)/(?P<age>\d+)/(?P<month>\d+)/',views.index),

反向解析

# 通过一些方法得到一个结果 该结果可以直接访问对应的url触发视图函数

# 先给路由与视图函数起一个别名
    url(r'^func_kkk/',views.func,name='ooo')
# 反向解析
    # 后端反向解析
      from django.shortcuts import render,HttpResponse,redirect,reverse
      reverse('ooo')
  # 前端反向解析
      <a href="{% url 'ooo' %}">111</a>

不包含分组的反向解析

前端反向解析示例:

无论urls.py里面的匹配正则参数怎么改,通过别名ooo。html的a标签都能访问到func

urls.py:注意别名不能出现冲突

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),

    # 反向解析
    url(r'^func', views.func,name="ooo")   # 先给路由与视图函数关系起一个别名
]

views.py

from django.shortcuts import render,HttpResponse,redirect

# Create your views here.
# 反向解析
def func(request):

    return HttpResponse("func")

home.html

html文件通过模板语法{% url "urls.py中设置的别名" %}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<a href="{% url "ooo" %}">111</a>
<a href="{% url "ooo" %}">222</a>
<a href="{% url "ooo" %}">333</a>
<a href="{% url "ooo" %}">444</a>
<a href="{% url "ooo" %}">555</a>
<a href="{% url "ooo" %}">666</a>
<a href="{% url "ooo" %}">777</a>
</body>
</html>

后端反向解析示例(views.py需要导入reverse)

from django.shortcuts import render,HttpResponse,redirect,reverse

# Create your views here.

# 首页
def home(request):
    # urls.py中的正则匹配怎么改这边都能通过别名接收到,注意要导入reverse模块
    print(reverse("ooo"))     # reverse("urls.py中设置的反向解析的名字")
    return render(request,"home.html")

 无名、有名分组的反向解析

无名分组反向解析

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 首页
    url(r'^$', views.home),
    # 无名分组反向解析
    url(r'^index/(\d+)/', views.index,name="ooo"),
]

views.py

from django.shortcuts import render,HttpResponse,redirect,reverse

# Create your views here.
# index
def index(request,args):
    print(args)
    return HttpResponse("index")

def home(request):
    print(reverse("ooo",args=(1,)))   # 后端写法,这个打印的是url路径,如果想跳转得url = reverse("ooo",args=(1,))然后return redirect(url)
    return render(request,"home.html")

home.html:前端书写

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--无名分组反向解析前端写法-->
<a href="{% url "ooo" 123 %}">123</a>
</body>
</html>

补充:

# 关于url模板语法中123,是依据路由层分组匹配规则而写的
# 无名分组反向解析
    url(r'^index/(\d+)/', views.index,name="ooo"),
"""
这个数字写代码的时候应该放什么
    数字一般情况下放的是数据的主键值  数据的编辑和删除
"""

# 可以将url中的123换成模板语法的变量(不过变量不需要{{}},直接写就好了)
# 例如:
{%for user_obj in user_queryset%}
    <a href="{% url 'ooo' user_obj.id %}">编辑</a>
{%endfor%}

有名分组反向解析

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 首页
    url(r'^$', views.home),
# 有名分组反向解析 url(r'^func/(?P<year>\d+)/', views.func,name="xxx"), ]

views.py

from django.shortcuts import render,HttpResponse,redirect,reverse

# Create your views here.
# 有名分组
def func(request,year):
    print(year)
    return HttpResponse("func")

def home(request):
    # 有名分组反向解析后端写法1,和无名分组一样,提供一个满足匹配规则的数字
    print(reverse("xxx",args=(1,)))     #推荐写法
    # 有名分组反向解析后端写法2:
    # print(reverse("xxx",kwargs={"year":123}))     #正规写法
    return render(request,"home.html")

home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--前端有名分组反向解析-->
<!--正规写法-->
{#<a href="{% url "xxx" year=123 %}">123</a>#}
<!--推荐写法,因为这个写法和无名分组一样,好记-->
<a href="{% url "xxx" 123 %}">123</a>

</body>
</html>

路由分发

"""
django的每一个应用都可以有自己的templates文件夹 urls.py static文件夹
正是基于上述的特点 django能够非常好的做到分组开发(每个人只写自己的app)
作为组长 只需要将手下书写的app全部拷贝到一个新的django项目中 然后在配置文件里面注册所有的app再利用路由分发的特点将所有的app整合起来

当一个django项目中的url特别多的时候 总路由urls.py代码非常冗余不好维护
这个时候也可以利用路由分发来减轻总路由的压力

利用路由分发之后 总路由不再干路由与视图函数的直接对应关系
而是做一个分发处理
    识别当前url是属于哪个应用下的 直接分发给对应的应用去处理
    
"""

环境简述:

项目中有两个应用(app01,app02),现在通过在项目的urls.py中配置路由分发到每个应用的urls.py

ps:

应用中的urls.py文件是开发这个应用的自己写的,默认没有的,需要自己建

第一种实现:

day62\day62\urls.py:总路由

注意事项:

总路由的第一个参数不要加$,因为加了$符就不再往后面匹配了,例如:url(r'^app01/$',include(app01_urls)),

from django.conf.urls import url,include
from django.contrib import admin
from app01 import urls as app01_urls
from app02 import urls as app02_urls

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 总路由中,路由分发,注意,需要导入一个include方法
    url(r'^app01/',include(app01_urls)),      # 只要url前缀是app01开头,全部交给app01处理
    url(r'^app02/',include(app02_urls)),      # 只要url前缀是app02开头,全部交给app02处理
# 注意事项:总路由里面的url千万不能加$符结尾
]

app01\urls.py

from django.conf.urls import url
from app01 import views urlpatterns = [ url(r'^reg/', views.reg), ]

app02\urls.py

from django.conf.urls import url
from app02 import views urlpatterns = [ url(r'^reg/', views.reg), ]

app01\views.py

from django.shortcuts import render,HttpResponse,redirect

# Create your views here.

def reg(request):
    return HttpResponse("app01:reg")

app02\views.py

from django.shortcuts import render,redirect,HttpResponse

# Create your views here.
def reg(request):
    return HttpResponse("app02:reg")

浏览器访问:http://127.0.0.1:8000/app01/reg/

 

 第二种实现:终极写法,推荐使用

day62\day62\urls.py:总路由

from django.conf.urls import url,include
from django.contrib import admin


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 总路由中,路由分发,注意,需要导入一个include方法
    url(r'^app01/',include("app01.urls")),
    url(r'^app02/',include("app02.urls")),
]

app01\urls.py

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^reg/', views.reg),
]

app02\urls.py

from django.conf.urls import url
from app02 import views

urlpatterns = [
    url(r'^reg/', views.reg),
]

app01\views.py

from django.shortcuts import render,HttpResponse,redirect

# Create your views here.

def reg(request):
    return HttpResponse("app01:reg")

app02\views.py

from django.shortcuts import render,redirect,HttpResponse

# Create your views here.
def reg(request):
    return HttpResponse("app02:reg")

路由分发的本质

urls.py

# 路由分发本质    include  可以无限制的嵌套N多层
    url(r'^index/',([],None,None))
# 示例:
from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/',([
        url(r'^index_1/',([
                url(r'^index_1_1',views.index),
                url(r'^index_1_2',views.index),
                url(r'^index_1_3',views.index),
                          ],None,None)),
        url(r'^index_2/',views.index),
        url(r'^index_3/',views.index),
                ],None,None)),

]

views.py

from django.shortcuts import render, HttpResponse,redirect

def index(request):
    return HttpResponse("ok")

浏览器访问

应用独立static和Templates

参考:https://blog.csdn.net/qq_39112101/article/details/89524071

名称空间

# 当多个应用出现了相同的别名 反向解析不会自动识别应用前缀

环境:

接着上面路由分发的环境,有两个应用,考虑到两个应用中urls.py里面起别名冲突的情况,想正常使用反向解析的解决方案(就可以用名称空间)

应用别名冲突情况下:

day62\day62\urls.py:总路由

from django.conf.urls import url,include
from django.contrib import admin


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 总路由中,路由分发,注意,需要导入一个include方法
    url(r'^app01/',include("app01.urls",namespace="app01")),
    url(r'^app02/',include("app02.urls",namespace="app02")),
]

app01\urls.py

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^reg/', views.reg,name="reg"),     # 别名和app02冲突
]

app02\urls.py

from django.conf.urls import url
from app02 import views

urlpatterns = [
    url(r'^reg/', views.reg,name="reg"),
]

app01\views.py  

from django.shortcuts import render,HttpResponse,redirect,reverse

# Create your views here.

def reg(request):
    # 此时冲突的情况下后端使用反向解析,使用名称空间解析
    print(reverse("app01:reg"))     # /app01/reg/
    return HttpResponse("app01:reg")

app02\views.py

from django.shortcuts import render,redirect,HttpResponse,reverse

# Create your views here.
def reg(request):
    # 此时反向解析,使用名称空间解析
    print(reverse("app02:reg"))     # /app02/reg/
    return HttpResponse("app02:reg")

此时前端使用反向解析示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

{% url "app01:reg" %}
{% url "app02:reg" %}

</body>
</html>

总结:为什么要用名称空间,就是因为应用别名冲突了,所以,我们如果起别名的时候保证不冲突就不用使用名称空间了

"""
一般情况下 有多个app的时候我们在起别名的时候会加上app的前缀
这样的话就能够确保多个app之间名字不冲突的问题
"""

day62\day62\urls.py:总路由

from django.conf.urls import url,include
from django.contrib import admin


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 总路由中,路由分发,注意,需要导入一个include方法
    url(r'^app01/',include("app01.urls")),
    url(r'^app02/',include("app02.urls")),
]

app01\urls.py

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^reg/', views.reg,name="app01_reg"),
]

app02\urls.py

from django.conf.urls import url
from app02 import views

urlpatterns = [
    url(r'^reg/', views.reg,name="app02_reg"),
]

app01\views.py  

from django.shortcuts import render,HttpResponse,redirect,reverse

# Create your views here.

def reg(request):
    # 此时反向解析,使用名称空间解析
    print(reverse("app01_reg"))     # /app01/reg/
    return HttpResponse("app01:reg")

app02\views.py  

from django.shortcuts import render,redirect,HttpResponse,reverse

# Create your views here.
def reg(request):
    # 此时反向解析,使用名称空间解析
    print(reverse("app02_reg"))     # /app02/reg/
    return HttpResponse("app02:reg")

不冲突情况下前端正常使用反向解析:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

{% url "app01_reg" %}
{% url "app02_reg" %}

</body>
</html>

伪静态

"""
静态网页
    数据是写死的 万年不变
    
伪静态
    将一个动态网页伪装成静态网页
    
    为什么要伪装呢?
        https://www.cnblogs.com/Dominic-Ji/p/9234099.html
        伪装的目的在于增大本网站的seo查询力度
        并且增加搜索引擎收藏本网上的概率
    
    搜索引擎本质上就是一个巨大的爬虫程序
    
    总结:
        无论你怎么优化 怎么处理
        始终还是干不过RMB玩家
"""

Django怎么实现伪静态:

例如在app01中如下配置:

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^reg.html', views.reg),
]

app01\views.py

from django.shortcuts import render,HttpResponse,redirect

# Create your views here.

def reg(request):
    return HttpResponse("app01:reg")

在浏览器访问

 

虚拟环境

"""
在正常开发中 我们会给每一个项目配备一个该项目独有的解释器环境
该环境内只有该项目用到的模块 用不到一概不装

linux:缺什么才装什么

虚拟环境
    你每创建一个虚拟环境就类似于重新下载了一个纯净的python解释器
    但是虚拟环境不要创建太多,是需要消耗硬盘空间的

扩展:
    每一个项目都需要用到很多模块 并且每个模块版本可能还不一样
    那我该如何安装呢? 一个个看一个个装???
    
    开发当中我们会给每一个项目配备一个requirements.txt文件
    里面书写了该项目所有的模块即版本
    你只需要直接输入一条命令即可一键安装所有模块即版本
    ps:requirements.txt使用
    # pip3 install -r requirements.txt   #安装requirements.txt依赖
    # 生成requirements.txt文件  (在项目根目录下执行)
    # pip3 freeze > requirements.txt
    # 使用
    # pip install -r requirements.txt
    
"""

创建虚拟环境步骤:

1、pycharm-file-New Project

 

 2、创建完成(标志venv文件夹)

Django1.x和2.x版本区别

"""
1.django1.X路由层使用的是url方法
    而在django2.X和3.X版本中路由层使用的是path方法
    url()第一个参数支持正则
    path()第一个参数是不支持正则的 写什么就匹配什么
    
    
    如果你习惯使用path那么也给你提供了另外一个方法re_path就和url一样,django2.x和django3.x中还是支持url的不过不推荐使用(也不合理)
        from django.urls import path, re_path        # django2.x和3.x正确使用方法
        from django.conf.urls import url             # url能够使用,但不推荐(容易让人版本混淆)
        
        re_path(r'^index/',index),
        url(r'^login/',login)
  2.X和3.X里面的re_path就等价于1.X里面的url
 
 
2.虽然path不支持正则 但是它的内部支持五种转换器
    path('index/<int:id>/',index)
    # 将第二个路由里面的内容先转成整型然后以关键字的形式传递给后面的视图函数

    def index(request,id):      
    print(id,type(id))
    return HttpResponse('index')
    
  
  
    str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
    int,匹配正整数,包含0。
    slug,匹配字母、数字以及横杠、下划线组成的字符串。
    uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
    path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)
    
3.除了有默认的五个转换器之外 还支持自定义转换器(了解)
# ================自定义转换器==================================
    # app01.path_converts中
    class MonthConverter:
        regex='\d{2}' # 属性名必须为regex

        def to_python(self, value):
            return int(value)

        def to_url(self, value):
            return value # 匹配的regex是两个数字,返回的结果也必须是两个数字
    

    # urls.py中注册转换器
    from django.urls import path,register_converter
    from app01.path_converts import MonthConverter
    from app01 import views
    
    register_converter(MonthConverter,'mon')

    urlpatterns = [
    path('articles/<int:year>/<mon:month>/<slug:other>/',     views.article_detail, name='aaa'),

]


4.模型层里面1.X外键默认都是级联更新删除的
  models.ForeignKey(to='Publish')
但是到了2.X和3.X中需要你自己手动配置参数 
models.ForeignKey(to='Publish',on_delete=models.CASCADE)
  on_delete其他选项
  CASCADE:删除作者信息一并删除作者名下的所有书的信息;
  PROTECT:删除作者的信息时,采取保护机制,抛出错误:即不删除Books的内容;
  SET_NULL:只有当null=True才将关联的内容置空;
  SET_DEFAULT:设置为默认值;
  SET( ):括号里可以是函数,设置为自己定义的东西;
  DO_NOTHING:啥也不干
"""

视图层:

 三板斧:

"""
HttpResponse
    返回字符串类型
render
    返回html页面 并且在返回给浏览器之前还可以给html文件传值
redirect
    重定向
"""
# 视图函数必须要返回一个HttpResponse对象  正确   研究三者的源码即可得处结论
The view app01.views.index didn't return an HttpResponse object. It returned None instead.

# render简单内部原理
    from django.template import Template,Context
    res = Template('<h1>{{ user }}</h1>')
    con = Context({'user':{'username':'jason','password':123}})
    ret = res.render(con)
    print(ret)
    return HttpResponse(ret)

JsonResponse对象

"""
json格式的数据有什么用?
    前后端数据交互需要使用到json作为过渡 实现跨语言传输数据

前端序列化
    JSON.stringify()                    json.dumps()
    JSON.parse()                        json.loads()
"""

JsonResponese使用示例:传字典

项目urls.py

from django.conf.urls import url,include
from django.contrib import admin


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 总路由中,路由分发,注意,需要导入一个include方法
    url(r'^app01/',include("app01.urls")),
    url(r'^app02/',include("app02.urls")),
]

app01\urls.py

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^reg.html', views.reg,name="app01_reg"),
    url(r'^ab_json/', views.ab_json),
]

app01/views.py

from django.shortcuts import render,HttpResponse,redirect,reverse
from django.http import JsonResponse     # 使用JsonRespone时需要导入JsonResponse模块

def ab_json(request):
    # 使用JsonRespone时需要导入JsonResponse模块
    user_dict = {"username":"王xx","password":"123","hobby":"girl"}

    return JsonResponse(user_dict,json_dumps_params={"ensure_ascii":False})

浏览器访问:

JsonResponese使用示例:传列表

注意:默认只能序列化字典,序列化非字典的对象时,要将safe选项改为False

项目urls.py

from django.conf.urls import url,include
from django.contrib import admin


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 总路由中,路由分发,注意,需要导入一个include方法
    url(r'^app01/',include("app01.urls")),
    url(r'^app02/',include("app02.urls")),
]

app01\urls.py

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^reg.html', views.reg,name="app01_reg"),
    url(r'^ab_json/', views.ab_json),
]

app01/views.py

from django.shortcuts import render,HttpResponse,redirect,reverse
from django.http import JsonResponse     # 使用JsonRespone时需要导入JsonResponse模块

def ab_json(request):
    # 使用JsonRespone时需要导入JsonResponse模块
    l = [111,333,444,222,"爱我中华"]
    return JsonResponse(l,json_dumps_params={"ensure_ascii":False},safe=False)

浏览器访问:

 form表单上传文件及后端获取

"""
form表单上传文件类型的数据
    1.method必须指定成post
    2.enctype必须换成multipart/form-data
"""

项目urls.py

from django.conf.urls import url,include
from django.contrib import admin


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 总路由中,路由分发,注意,需要导入一个include方法
    url(r'^app01/',include("app01.urls")),
    url(r'^app02/',include("app02.urls")),
]

app01\urls.py

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^reg.html', views.reg,name="app01_reg"),
    url(r'^ab_json/', views.ab_json),
    # 上传文件
    url(r'^ab_file/', views.ab_file),
]

app01/views.py

from django.shortcuts import render,HttpResponse,redirect,reverse
from django.http import JsonResponse     # 使用JsonRespone时需要导入JsonResponse模块

# Create your views here.

def reg(request):
    # 此时反向解析,使用名称空间解析
    print(reverse("app01_reg"))     # /app01/reg/
    return HttpResponse("app01:reg")

def ab_json(request):
    # 使用JsonRespone时需要导入JsonResponse模块
    l = [111,333,444,222,"爱我中华"]
    return JsonResponse(l,json_dumps_params={"ensure_ascii":False},safe=False)

# 上传文件
def ab_file(request):
    if request.method == "POST":
        # print(request.POST)     # 只能获取普通的键值对数据,不包含文件
        print(request.FILES)      # 获取文件数据
        # <MultiValueDict: {'file': [<InMemoryUploadedFile: 下载.jpg (image/jpeg)>]}>

        # 获得文件方法和POST、GET一样使用.get()方法
        file_obj = request.FILES.get("file")     # 文件对象
        print(file_obj.name)     # 获取上传文件的文件名   eg:下载.jpg
        # 保存文件
        with open(file_obj.name,'wb') as f:
            for line in file_obj:   # 类似与一行行的读取文件,官方推荐使用for line in file_obj.chunks()然后写文件
                f.write(line)

    return render(request,"form.html")

前端页面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <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">
    <p>username:<input type="text" name="username"></p>
    <p>file:<input type="file" name="file"></p>
    <input type="submit">
</form>
</body>
</html>
前端示例代码

总结:request对象方法

"""
request.method
request.POST
request.GET: http://127.0.0.1:8000/index/123?name=lqz&age=18 name=lqz&age=18会被转成字典,放到GET中
request.FILES
request.body  # 原生的浏览器发过来的二进制数据  后面详细的讲
request.META:HTTP请求的其他东西,放在里面,入客户端ip地址:REMOTE_ADDR
request.path 
request.path_info
request.get_full_path()  能过获取完整的url及问号后面的参数 
request.FILES:上传的文件
request.session:用的session

"""
    print(request.path)  # /app01/ab_file/
    print(request.path_info)  # /app01/ab_file/
    print(request.get_full_path())  # /app01/ab_file/?username=jason

 参考链接:

https://www.cnblogs.com/liuqingzheng/articles/9509801.html#_label1

FBV与CBV

# 视图函数既可以是函数也可以是类

我们之前在views.py写的函数就是FBV

def index(request):
  return HttpResponse('index')

CBV就是用类来写视图函数

CBV示例:

项目urls.py

from django.conf.urls import url,include
from django.contrib import admin


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 总路由中,路由分发,注意,需要导入一个include方法
    url(r'^app01/',include("app01.urls")),
    url(r'^app02/',include("app02.urls")),
]

app01\urls.py

from django.conf.urls import url
from app01 import views

urlpatterns = [
    # CBV路由
    url(r'^login/', views.MyLogin.as_view()),  # 注意as_view一定要加括号
]

app01\views.py

from django.shortcuts import render,HttpResponse,redirect,reverse
from django.views import View   # 使用CBV需要导入View模块,让类继承

# Create your views here.

class MyLogin(View):    # 要继承导入的View模块
    def get(self,request):
        return HttpResponse("get方法")

    def post(self,request):
        return HttpResponse("post方法")

浏览器访问

 总结:

"""
FBV和CBV各有千秋
CBV特点
    能够直接根据请求方式的不同直接匹配到对应的方法执行
"""

 CBV源码刨析

# 你自己不要修改源码 除了bug很难找

# 突破口在urls.py
url(r'^login/',views.MyLogin.as_view())
# url(r'^login/',views.view)  FBV一模一样
# CBV与FBV在路由匹配上本质是一样的 都是路由 对应 函数内存地址
"""
函数名/方法名 加括号执行优先级最高
猜测
    as_view()
        要么是被@staicmethod修饰的静态方法
        要么是被@classmethod修饰的类方法  正确
        
    @classonlymethod
    def as_view(cls, **initkwargs):
        pass
"""

    @classonlymethod
    def as_view(cls, **initkwargs):
        """
        cls就是我们自己写的类   MyCBV
        Main entry point for a request-response process.
        """
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)  # cls是我们自己写的类
            # self = MyLogin(**initkwargs)  产生一个我们自己写的类的对象
            return self.dispatch(request, *args, **kwargs)
            """
            以后你们会经常需要看源码 但是在看python源码的时候 一定要时刻提醒自己面向对象属性方法查找顺序
                先从对象自己找
                再去产生对象的类里面找
                之后再去父类找
                ...
            总结:看源码只要看到了self点一个东西 一定要问你自己当前这个self到底是谁
            """
        return view
      
        # CBV的精髓
    def dispatch(self, request, *args, **kwargs):
        # 获取当前请求的小写格式 然后比对当前请求方式是否合法
        # get请求为例
        # post请求
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
            """
            反射:通过字符串来操作对象的属性或者方法
                handler = getattr(自己写的类产生的对象,'get',当找不到get属性或者方法的时候就会用第三个参数)
                handler = 我们自己写的类里面的get方法
            """
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)
        """
        自动调用get方法
        """

# 要求掌握到不看源码也能够描述出CBV的内部执行流程(******)

 

posted @ 2020-05-26 21:00  耗油炒白菜  阅读(250)  评论(0编辑  收藏  举报