用户功能
管理功能
django支持任意多个app

​ 注意:

		1. 使用命令行创建项目,不会自动创建templates文件夹,只能手动建
		2. settings文件中手动写[os.path.join(BASE_DIR, 'templates')]

​ pycharm创建:

​ 能够自动创建template文件夹和路径配置

​ 也能够支持创建一个应用并注册。

三板斧

from django.shortcuts import render,HttpResponse, redirect

​ HttpResponse

# 返回字符串
urls:
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'login/', views.login),


def login(request):
    return HttpResponse('Hello word~')

​ render

# 返回HTML页面,并且可以为该html传值
urls:
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'login/', views.login),
    url(r'reg/', views.reg),
]



def reg(request):
    user_dict = {'name':'jason','pwd':123}
    # return render(request,'reg.html')
    # 给模板传值的方式1
    # return render(request, 'reg.html',{'xxx':user_dict})  # 将user_dict传递给reg.html页面 页面上通过xxx就能够获取到该字典
    # 给模板传值的方式2
    return render(request,'reg.html',locals())  # 会将当前名称空间中所有的变量名全部传递给reg.html页面
    # locals()  会出现效率问题,但还是要是用

​ redirect

# 重定向 既可以是我们自己的路径也可以是网上的路径
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'login/', views.login),
    url(r'reg/', views.reg),
    url(r'home/', views.home),
    url(r'index/', views.index),
]


def home(request):
    # return redirect('/index')
		return redirect('http://www.xiaohuar.com')

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


注意: 重定向的可以是自己写的,也可以是第三方的。

django返回的都是HttpResponse对象

我们写的视图函数必须得有返回值

静态文件配置

用户能够访问到的所有资源,都是程序员提前爆露好的,如果没有暴露,用户永远也访问不到。

用户能够在浏览器中输入网址访问到相应的资源,前提是后端暴露了该资源接口。
在django中如果你想让用户访问到对应的资源,我们只需要在urls.py中设置对应关系,反过来如果我没有在urls.py中开设资源 用户就永远就访问不到对应的资源。
返回给浏览器的html页面上所有的静态资源 也需要请求后端加载获取
通常我们将网站所用到的html文件全部存放在templates文件夹下
网站用到的静态资源全部存放到static文件夹下

静态文件

​ 网站所用到的

​ 自己写好的js

​ 自己写好的CSS

​ 第三方的框架 bootstrap fontwesome sweetalert

通常情况下网站所用到的静态文件资源,统一都放在static文件夹下

settings:
STATIC_URL = '/static/'  # 是访问静态资源的接口前缀
"""只要你想访问静态资源 你就必须以static开头"""
# 手动配置静态文件访问资源
STATICFILES_DIRS = [
    os.path.join(BASE_DIR,'static'),
    os.path.join(BASE_DIR,'static1'),
    # os.path.join(BASE_DIR,'static2'),
] # (支持多个)

打开网页,F12, 自定义关闭旁边,Network, Disable cache (while DevTools is open),关闭缓存

接口前缀 动态解析

{% load static %}
<link rel="stylesheet" href="{% static'bootstrap/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap/js/bootstrap.min.js' %}"></script>

from表单

form是一个复杂的系统标签,其内部又可包含很多的一些输入标签

例如input 输入文本标签 checkbox 多选标签等等

form表单有几个属性我们需要注意一下

  1:action属性,里面写的是url链接,接就是表单提交的地址

  2:method属性,里面写的是数据提交的方式,可以写入get或者post

  3:enctype属性,提交数据的编码格式

form表单中的标签的前后台交互

from表单上传文件

form表单上传文件
1.提交方式必须是post
2.需要将form标签的enctype属性由默认的urlencoded改为formdata

后端需要从request.FILES中获取上传的文件

django内部针对不同数据格式的数据 会解析到不同的方法中
request.GET
request.POST
request.FILES

form表单被我们应用在前后台交互的环节的,里面的值可以在后台通过某些key取出来

下面就来讲解不同标签取值的方法

1. input 标签
input标签我们最为常见,里面有三个属性比较重要
  a. type 他是代表input的类型
  b. name 他就是后台取值的依据(key)
  c. val 他是我们输入的值,也是后台需要的值
  

然后我们根据type的类型,又可以把input进行细分

  a. text 表示普通的文本,明文输入
  b. password 输入的也是文本,密文输入
  c. number 输入的是数字,不是数字不让输入
  d. submit 提交按钮,提交form表单的内容
  e. button 普通的按钮
  f. radio 单选框,我们需要注意的是单选框的所有的name值必须相同
      如果name不相同,就说明不在同一个选择方位,也就不存在单选,然后想要在后台取到他的值,

      你必须在定义的时候给附上一个值给value,这样才能取到值
  g. checkbox 复选框,内容和单选框一样
  h. file 选择文件,可以选择文件提交给后台

以上基本是input的所有类型,需要注意几个点
  1.取值都是通过name进行取值,所以必须给name赋值
  2.文本类型想要附上初始值,直接在value中加入值就可以
  3.选择框如果想要默认选中谁,那就在谁的标签中加入checked属性

2. select 标签
select标签是一个下拉框的形式让用户进行选择选项
所以select标签中必须包含option标签才能显示属性
形式为:

然后select中有全局属性name,这个name是后台又来进行取值的
每个option标签的文本内容是显示给用户看的,我们需要取的是option标签中的value属性,所以在开始必须给option的value赋值
后台通过select的name取值,直接取到的就是对应option的value

如果我们向让他默认选择某个option,可以在option标签中加入selected属性,如果都不加,默认是显示第一个

3. button 按钮标签
新出的标签,与input中type为button的按钮一样

4. textarea 文本框标签

与input中的text一样都是输入文本的,但是textarea标签没有字数的限制,并且输入框可以拖拉。

from表单 action参数可以写的形式
  1. 若不写 :默认朝当前地址提交
  2. 只写后缀/index/
  3. 写全路径

from表单默认朝后端提交的方式 默认是get请求

​ get请求携带参数的方式,是在url后面?

​ url ?username=admin&password=123

​ 缺点:

  1. 不安全
  2. get请求携带的参数有大小限制

如果前期要提交post请求 就去settings中注释掉一个中间件

request对象及方法前后端数据交互

如何获取请求方式

获取post请求携带的数据
request.POST
获取get请求携带的数据:request.GET
get和post在后端获取用户数据的时候 规律是一样的
<QueryDict: {'username': ['admin', 'tank'], 'password': ['123']}>
tank <class 'str'>
123 <class 'str'>
request.POST.get('username') 默认只取列列表的最后一个元素
如果你想将列表完整的取出 你必须用getlist()

    request.GET  # 你就把它当成一个大字典 里面放的是get请求携带过来的数据
request.POST  # 你就把它当成一个大字典 里面放的是post请求携带过来的数据
"""上面的大字典 所有的value都是一个列表"""
    
request.GET.get('key')  # 默认取的是列表的最后一个元素 并不是直接将列表取出
request.GET.getlist('key')  # 直接将value的列表取出


request.POST.get('key')  # 默认取的是列表的最后一个元素 并不是直接将列表取出
request.POST.getlist('key')  # 直接将value的列表取出

pycharm连接数据库(pycharm充当数据库的客户端)

点击database,添加数据库

Django连接数据库

Django自带一个小型的sqlite3数据库....该数据库功能不是很强大 尤其是对日期格式的数据 不是很兼容

​ Django连接MySQL

​ 1. 配置文件

DATABASES = {
	'default': {
		'ENGINE': 'django.db.backends.sqlite3', # 指定数据库
		'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), # 到底是用哪个库
	 'USER':'root',
   'PASSWORD':'root',
   'HOST':'127.0.0.1',
   'PORT':3306,
   'CHARSET':'utf8'
}
}

现在已经不再使用MySQLdb模块,但是默认还是使用MySQLdb 连接数据库,所以告诉Django不要使用MySQLdb,而是改用pymysql 连接

在项目名称下边的__init__.py也可以在应用名下面的__init__.py文件中指定
import pymysql
pymysql.install_as_MySQLdb()

Django orm简介

​ orm对象关系映射

​ 类 数据库的表

​ 对象 表的记录

​ 对象获取属性 记录的某个字段对应的值

优点: 能够让一个不会数据库操作的人也能够简单快捷的去使用数据库

缺点: 由于封装程度太高,可能会导致sql语句的执行效率变低

有时候结合项目需求可能需要你手写sql语句

注意事项:

1. django的orm不会自动帮你创建库,需要你手动指定一个django项目用一个数据库
2. 表会自动帮你创建,你只需要书写符合Django orm语法的代码即可
from django.db import models
# Create your models here.
class Userinfo(models.Model):
# 设置id字段为userinfo表的主键  id int primary key auto_increment
id = models.AutoField(primary_key=True) # 在django中 你可以不指定主键字段 django orm会自动给你当前表新建一个名为id的主键字段    # 设置username字段  username varchar(64)  CharField必须要指i定max_length参数
username = models.CharField(max_length=32)  # 在django orm中 没有char字段  但是django 暴露给用户 可以自定义char字段    # 设置password字段  password int
password = models.IntegerField()

数据库迁移(同步)命令:

  1. python manage.py makemigrations # 不会创建表 仅仅是生成一个记录 将你当前的操作记录到一个小本本上(migrations文件夹)

  2. python manage.py migrate # 将你的orm语句真正的迁移到(同步)到数据库中

  3. 只要你在models.py中修改了跟数据库相关的代码 你就必须重新开始执行上面两条命令

当你第一次执行上面两条命令的时候 django会自动创建很多张表 这些表都是django默认需要用到的表
你自己写的模型类所对应的表 表名有固定格式
应用名_表名

字段的增删改查

增:

在项目的modles文件夹下
 phone = models.BigIntegerField(default=110)  # 新增的字段 可以提前设置默认值 
addr = models.CharField(max_length=64,null=True)  # 新增的字段 可以设置为空 

新增的字段
1.直接提供默认值 default
2.设置改字段可以为空 null=True
注意的是 不要轻易的注释models.py中任何跟数据库相关的代码
主要是跟数据库相关的代码 你在处理的时候一定要小心谨慎

数据的增删改查

表记录的增改查:

可以再客户端浏览器显示

{# 模板语法注释 #},不能在网页显示,只能在后端显示,

数据的查:

​ get()

​ 1. 条件存在的情况下,获取的直接是数据对象的本身

​ 2. 条件不存在的情况下,会直接报错,所以不推荐使用get方法查询数据

filter()
  1. 条件存在的清华况下获取的是一个可以看成列表的数据,列表里面放的才是一个个数据
    2. 条件不存在的情况下,并不会报错,会返回一个可以看成空列表的数据
    3. filter括号内可以写多个参数逗号隔开这多个参数在查询的时候 是and关系
  2. filter的结果支持索引取值 但是不支持负数 并且不推荐你使用索引 推荐你使用它封装好的方法 first取第一个数据对象数据的增

数据的增

1. create()
 	1. 括号内写关键字参数的形式,创建数据
      	2. 该方法会有一个返回值,返回值就是当前的对象前身。

##### 2. 利用对象点方法的方式

user_obj = User(username='jason')

user_obj.save()  # 将当前对象保存到数据库中

改:

def edit_user(request):
  # 1.如何获取用户想要编辑的数据
  edit_id = request.GET.get('edit_id')
  if request.method == 'POST':
    # 将用户新修改的所有的数据
    username = request.POST.get("username")
    password = request.POST.get("password")
    """POST中也是可以获取GET请求携带的参数""" 
    # 去数据库中修改对应的数据 
    # 方式1:        models.Userinfo.objects.filter(pk=edit_id).update(username=username,password=password)# 批量更新
    # 方式2: 获取当前数据对象 然后利用对象点属性的方式 先修改数据  然后调用对象方法保存
    # 不推荐你使用第二种方式  效率低   挨个重新写入一遍
     edit_obj = models.Userinfo.objects.filter(pk=edit_id).first()  # pk能够自动帮你查询出当前表的主键字段名
       edit_obj.username = username
       edit_obj.password = password
       edit_obj.save()
        """update方法会将filter查询出来的queryset对象中所有的数据对象全部更新""" 
        # 跳转到数据展示页面
        return redirect('/userlist')
      # 2.根据主键值去数据库中查询出当前对象 展示给用户看
      edit_obj = models.Userinfo.objects.filter(pk=edit_id).first()
      # pk能够自动帮你查询出当前表的主键字段名 
      # 3.将查询出来的数据对象传递给前端页面 展示给用户看
      return render(request,'edit_user.html',locals())

删:

def delete_user(request):
  # 获取想要删除的数据id 直接删除
  delete_id = request.GET.get('delete_id')    models.Userinfo.objects.filter(pk=delete_id).delete()  # 批量删除
  return redirect('/userlist')

图书管理系统OR表创建:

​ 表与表之间的关系:

​ 一对多:外键字段建立在多的一

​ models.Foreignkey(to="关联的表名") # 自动建表关系,默认就是跟关联表的主键字段

“”“

Foreignkey字段在创建的时候 orm会自动在字段后面加_id

"""

​ 多对多: ManyToManyField(to=“关联的表名”) # 并不会创建一个实际字段,仅仅是用来告诉django orm自动创建第三张表

​ 一对一:

​ OneToOneField(to="关联的表名")

​ """

​ OneToOneField字段在创建的时候orm会自动在字段后面加_id

​ """

​ 一对一(作者和作者信息)

​ 一对多(书和出版社)

​ 多对多(书和作者)

​ 图书表(book):id/title/price/publish_id

​ 出版社表(publish):id/name/ addr

​ 作者表(author): id/name/phone

​ 多对多第三方表:id/book_id/author_id

django请求生命周期

  1. 当用户在浏览器中输入url时,浏览器会生成请求头和请求体发给服务端请求头和请求体中会包含浏览器的动作(action),这个动作通常为get或者post,体现在url之中.
  2. url经过Django中的wsgi,再经过Django的中间件,最后url到过路由映射表,在路由中一条一条进行匹配, 一旦其中一条匹配成功就执行对应的视图函数,后面的路由就不再继续匹配了.
  3. 视图函数根据客户端的请求查询相应的数据.返回给Django,然后Django把客户端想要的数据做为一个字符串返回给客户端.
    4.客户端浏览器接收到返回的数据,经过渲染后显示给用户.


路由层 urls.py

路由匹配

urls.py: 路由与视图函数的对应关系》》》路由层

  1. urls.py 的第一个参数是正则,
  2. 一旦匹配上会立刻执行对应的视图函数,不再往下匹配了
  3. APPEND_SLASH = False # 控制url后面不自动增加斜杠
  4. APPEND_SLASH = True # 默认是True自动加斜杠
  5. ^ & # 上升号表示限制开头,&表示限制结尾,放在一起可以限制一个独一无二的字符,用于首页
  6. url(r'', admin.site.urls) # 空表示接受全部的,不会在往下走,直接拦截住。url(r'', views.errors) # 用于尾页

有名与无名分组

无名分组
urlpatterns = [
  url(r'^test/(\d+)/', views.test)
]  
会将括号内的内容当做位置参数传递给后面的视图函数test(reqiest, args)
在调用视图函数test的时候 会将\d+匹配到的内容 当做位置参数传递给test


有名分组(给正则表达式定义别名)
urlpatterns = [
  url(r'^test/(?P<month>\d+)/', views.test)
]  
定义了别名month
会将括号内的内容当做关键字传递给后面的视图函数test(request, month=123)
在调用视图函数test的时候 会将\d+匹配到的内容 当做关键字参数(month='')传递给test
views内定义的形参必须是month

无名和无名不能结合使用,但是可以单独使用.
有名无名单独使用的情况下  可以用多个

注意:+表示(1-无穷大)*(0-无穷大)?(0-1)

反向解析

​ 根据某一个东西动态解析出一个结果,该结果可以直接访问对应的url。

给路由与视图函数对应关系 起一个别名 后续根据这个别名 就能够动态解析出所对应的url。

url(r'^test_add/', views.testadd,name='xxx')

起别名 别名一定不要重复

  1. 前端解析

    {% url 'xxx' %}

    222

  2. 后端解析

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

url = reverse('修改名')

有名和无名反向解析
<a href="{% url 'xxx' year=1 %}">222</a>
<a href="{% url 'xxx' 1 %}">222</a>
url = reverse('xxx', args=(1,))
url = reverse('xxx', kwargs=('year':231,))

通过名字能够动态表达出一个结果,这个结果能够访问到这个名字对应的url

url(r'^edit_user/(\d+)/',views.edit_user,name='edit')
user_queryset = models.User.objects.all()
{% for user_obj in user_queryset %}
<td>
<a href="{% url 'edit' user_obj.pk %}">编辑</a>
<a>删除</a>
</td>{% endfor %}def edit(request,edit_id):
pass

注意: 反向解析的别名一定不能重复

路由分发

​ Django里面的APP可以有自己的static文件,templates文件夹,urls.py等。正是由于上述的特点 你基于django开发项目 就真正可以做到分组分功能分模块独立的去开发。

注意:当应用特别多的时候 总路由中的代码过于冗长 不好维护

​ 项目名下的urls.py不再做路由与视图函数对应关系了,而是做一个中转站,只负责将请求分发到不同的app中

在APP的urls.py 完成路由于视图函数的对应关系

from django.conf.urls import url,include

url(r'^app01/',include(app01_urls)),
url(r'^app02/',include(app02_urls))

# 1.在应用下自己手动创建urls.py
# 2.在路由中导入 
# 1
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))
# 2
url(r'^app01/',include('app01.urls')),
url(r'^app02/',include('app02.urls'))  

名称空间

总路由
url(r'^app01/',include('app01.urls',namespace='app01'))
url(r'^app02/',include('app02.urls',namespace='app02'))

    print(reverse('app01:index'))
    print(reverse('app02:index'))

因为起别名冲突才用到名称空间,所以通常情况下 起别名的时候 前面可以加上你的应用名

伪静态

url看起来像是一个静态页面(.html结尾)

将动态网页假装成静态的

这样做可以提高搜索引擎的SEO查询优先级

搜索在收录网站的时候会优先收录看上去像是静态文件的资源

但是无论你怎么使用伪静态进行优化 你也干不过RMB玩家(参考百度)

虚拟环境

​ 不同的项目应该有各自独立的解释器环境 最大化节省资源
​ 实际功能中针对不同的项目 会有一个叫requestsments.txt文件
​ 该文件中列出来是一个个该项目需要用的到模块名和版本号
​ eg:
django = 1.11.11
nginx = 1.21
​ 后期通过命令直接会去下载该文件内所有的模块及对应版本
​ 每创建一个虚拟环境,就类似于重新下载一个纯净的python解释器环境

​ 虚拟环境会占用硬盘资源,所以不要创建太多。

Django版本的区别

#### 	Django1.x

#### 	Django2.x

区别:

urls.py中1.x用的是url,而2.x用的是path

并且2.x中的path第一个不支持正则表达式,写什么就匹配什么

如果你觉得不好用,2.x里面还有re_path 这个re_path就是你1.x里面的url

Django之视图层

视图函数

三板斧:
  1. HttpResponse # 返回字符串

  2. render # 返回一个html页面 还可以给模板传递

    from django.template import Template,Context
                    def index(request):
                        res = Template("<h1> {{ user }} </h1>")
                        con = Context({'user':{'username':'jason','pwd':'123'}})
                        ret = res.render(con)
                        print(ret)
                        return HttpResponse(ret)
    
  3. redirect # 重定向

JsonResponse

​ 返回json格式数据

​ 为什么要给前端回json 格式字符串

​ 前后端分离 就是基于json格式传输数据的

​ 后端给前端返回一个json的格式的字符串(相当于反悔了一个大字典)

​ 然后前端利用序列化反序列化转换成前端对应的数据类型

​ js常用数据类型:

​ 数值类型
​ 字符类型
​ 数组
​ 自定义对象
​ undefined与null
​ 布尔值
​ symbol

​ JSON.stringify 序列化 >>> json.dumps

​ JSON.parse 反序列 >>> json.loads

FBV与CBV

FBV (Function Based View) 基于函数的视图

CBV (Class Based View) 基于类的视图

CBV 的请求流程

当服务端使用cbv模式的时候,用户发给服务端的请求包含urI和method,这两个信息都是字符串类型
服务端通过路由映射表匹配成功后会自动去找dispatch方法,然后Django会通过dispatch反射的方式找到类中对应的方法并执行
类中的方法执行完毕之后,会把客户端想要的数据返回给dispatch方法,由dispatch方法把数据返回经客户端

在类中写的两个方法(get和post)

get请求会触发get方法,post请求会触发post方法

url(r'^reg/',views.MyReg.as_view()),
# url(r'^reg/',views.view)
# CBV和FBV在路由匹配上 规则都是一样的 都是路由后面跟的函数的内存地址

CBV结合源码

# CBV路由
url(r'^reg/',views.MyReg.as_view())(通过as_view进入源码)

@classonlymethod
def as_view(cls, **initkwargs):
  def view(request, *args, **kwargs):
    self = cls(**initkwargs)  # cls就是我们自己的写的MyReg类
    if hasattr(self, 'get') and not hasattr(self, 'head'):
      self.head = self.get
      self.request = request
      self.args = args
      self.kwargs = kwargs
      # 上面的一通操作 就是给我们自己写的类的对象赋值
      return self.dispatch(request, *args, **kwargs)
    # 对象在查找属性或方法的时候 顺序是什么?  先从自己找 再从产生对象的类中找  再去类的父类中找...
    """也就意味着你在看源码的时候 你一定要牢记上面的话"""
    return view

  # views.py 
  from django.views import View


  class MyReg(View):
    def get(self,request):
      return render(request,'reg.html')

    def post(self,request):
      return HttpResponse("我是MyReg类中post方法")


    """CBV最精髓的部分"""
    def dispatch(self, request, *args, **kwargs):
      if request.method.lower() in self.http_method_names:  # 判断当前请求方式在不在默认的八个请求方式中
        handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        # handler = getattr(自己写的类产生的对象,'小写的请求方法(get\post)','获取不到对应的方法就报错')
        # handler就是我们自己定义的跟请求方法相对应的方法的函数内存地址
        else:
          handler = self.http_method_not_allowed
          return handler(request, *args, **kwargs)  # 在调用获取到的方法

CBV说明1:

在url中会执行一个叫做as_view的方法,这个方法在加载url时候会执行 得到一个返回值view 是一个函数 供url调用,

当url执行view时候,会把下边这句代码作为返回值

self.dispatch(request, *args, **kwargs)

所view 会调用当前调用类的dispatch

CBV说明2:

在执行dispatch的时候,如果views中的视图类 自定义了dispatch那么将优先执行自定义的dispatch 由此可在请求处理前增加一些处理,

然后再用super方法去调用父类的dispatch ,当然也可以自己把父类的功能全部写在自己的自定义里边。

简单的说-----分发

复杂的说-----找到这次请求对应的类型,然后用反射把这个方法取到,然后作为自己的返回值,返回给调用这个方法的view

方式二:

案例:
urls.py

from django.urls import path,register_converter,re_path
from app01 import views
urlpatterns = [
 re_path(r'^login/',views.LoginView.as_view()), # 必须调用类下的方法as_view
]

views.py

from django.shortcuts import render,HttpResponse,redirect
from django.views import View
class LoginView(View):
	def dispatch(self, request, *args, **kwargs): # 可在该方法内做一些预处理操作
  # 当请求url为:http://127.0.0.1:8008/login/会先触发dispatch的执行

	# 如果http协议的请求方法为GET,则调用下述get方法

	# 如果http协议的请求方法为POST,则调用下述post方法
   obj=super().dispatch(request, *args, **kwargs) # 必须继承		父类的dispatch功能
 		return obj # 必须返回obj
 		def get(self,request):
	 	return render(request,'login.html')
	 	def post(self,request):
	 	name=request.POST.get('name')
		 pwd=request.POST.get('pwd')
		 if name == 'egon' and pwd == '123':
		 res='登录成功'
		 else:
		 res='用户名或密码错误'
		 return HttpResponse(res)

测试:

python manage.py runserver 8001

# 验证GET请求:在浏览器输入:http://127.0.0.1:8001/login/

# 验证POST请求:在表单内输入数据然后提交

Django源码分析及实际应用

django的配置文件有两个
	一个是暴露给用户可以自定义配置的
	一个是默认的全局配置文件
		用户指定了就用用户的
		用户没有指定就用默认的

from django.conf import settings

settings = LazySettings()

class LazySettings(LazyObject):

  def _setup(self, name=None):

    # os.environ你可以把它看成是一个全局的大字典
    settings_module = os.environ.get(ENVIRONMENT_VARIABLE)  # 从大字典中取值
    # settings_module = 'day59.settings'

    self._wrapped = Settings(settings_module)  # Settings('day59.settings')

    class Settings(object):
      def __init__(self, settings_module):  # settings_module = 'day59.settings'
        for setting in dir(global_settings):  # 循环获取global_settings文件中所有的名字
          if setting.isupper():  # 在判断名字是否是大写
            # 如果是大写 利用反射 获取到大写的名字所对应的值  不停地添加到对象中
            setattr(self, setting, getattr(global_settings, setting))
            # store the settings module in case someone later cares
            self.SETTINGS_MODULE = settings_module
            mod = importlib.import_module(self.SETTINGS_MODULE)  # 'day59.settings'
            # from day59 import settings
            # mod 指代的就是暴露给用户的配置文件模块名

            for setting in dir(mod):  # 循环获取暴露给用户配置文件中所有的名字
              if setting.isupper():  # 判断是否是大写
                setting_value = getattr(mod, setting)  # 如果是大写 获取大写的变量名所对应的值
                setattr(self, setting, setting_value)  # 不停的给对象设置值

思考:基于django settings源码 实现自己的项目也有两个配置文件 一个是暴露给用户的一个是项目默认
用户配置了 就用用户的
用户没有配置 就用默认的
django settings源码
django暴露给用户一个自定义配置的文件 
用户配置了就用用户的 用户没有配置就使用默认的  并且配置文件中的变量名必须是大写才有效
from django.conf import settings

settings = LazySettings()

class LazySettings(object):
  ...

  class Settings(object):
    # 循环获取默认的配置文件中所有的大写配置
    # 利用setattr给对象不停的设置键值对
    # 再循环获取暴露给用户的自定义配置文件中所有的大写的配置
    # 再利用setattr给对象不停的设置键值对
    """字典的键存在的情况 再设值其实就是替换"""

Django之模板

为将前端页面和Python的代码分离, Django专门提供了模板系统 (Template System,即模板层)来实现 django的模板=HTML代码+模板语法 .

存放于templates目录下的html文件称之为模板文件,如果我们想要返回的html页 面中的数据是动态的,那么必须在html页面中嵌入变量,这便用到了django的模 板语法。

模板层

{{}}变量相关

{%%}逻辑相关

模板传值

传函数名的时候 会自动加括号调用函数 将函数的返回值展示在html页面上

django模板语法不支持函数传参

django模板语法在获取容器类型内部元素的值的时候 统一只采用 句点符(.)

1. 变量:{{ 变量名 }}

如果html代码中的数据不是固定死的,而是动态变化的,则必须在html中嵌入变 量,为此,模板语法提供了变量的概念,允许我们在html代码中嵌入变量,我们 只需要在视图函数中用render方法为html文件中指定的变量赋值即可

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Title</title>
</head>
<body>
<p>{{ msg }}</p>
<p>{{ dic }}</p>
<p>{{ obj }}</p>
<p>{{ li }}</p>
</body>
</html>

我们需要在视图函数中为模板test.html的变量名msg、li、dic、obj、obj_li赋值,views.py内容如下

from django.shortcuts import render
def test(request):

# 传给模板的变量值可以是任意python类型,如下

 msg='hello world'
 dic={'k1':1,'k2':2}
 class Person(object):
 def __init__(self,name,age):
 self.name=name
 self.age=age
 obj=Person('egon',18)
 li = [1,'aaa',obj]
 return render(request,'test.html',{'msg':msg,'dic':dic,'obj':obj,'li':li})

# 注意:

# 1、render函数的第三个参数包含了要传给模板的变量值,是一个字典类型,该字典中的key必须与模板文件中

的变量名相对应,render函数会去templates目录下找到模板文件,然后根据字典中的key对应到模板文件中的变量名
进行赋值操作,最后将赋值后的模板文件内容返回给浏览器

# 2、可以将render函数的第三个参数简写为locals(),如下

 return render(request,'test.html',locals()) #locals()会将函数test内定义的名字与值转换为字
典中的k与v
深度查询:句点符的应用

当视图函数传给模板的值中包含多个元素时,若想取出其中的单个元素,就必须
使用句点符了。
句点符既可以引用容器类型的元素,也可以引用对象的方法,如下

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Title</title>
</head>
<body>
<!--调用字符串对象的upper方法,注意不要加括号-->
<p>{{ msg.upper }}</p>
<!--取字典中k1对应的值-->
<p>{{ dic.k1 }}</p>
<!--取对象的name属性-->
<p>{{ obj.name }}</p>
<!--取列表的第2个元素,然后变成大写-->
<p>{{ li.1.upper }}</p>
<!--取列表的第3个元素,并取该元素的age属性-->
<p>{{ li.2.age }}</p>
</body>
</html>

过滤器

过滤器类似于python的内置函数,用来把视图传入的变量值加以修饰后再显示,
具体语法如下

{{ 变量名|过滤器名:传给过滤器的参数 }}

常用内置过滤器

1、default
作用:如果一个变量值是False或者为空,使用default后指定的默认值,否则,使用变量本身的值,如果
value=’‘则输出“nothing”
{{ value|default:"nothing" }}
2、length
作用:返回值的长度。它对字符串、列表、字典等容器类型都起作用,如果value是 ['a', 'b', 'c', 'd'],那么输出是4
{{ value|length }}

3、filesizeformat
#作用:将值的格式化为一个"人类可读的"文件尺寸(如13KB、4.1 MB、102bytes等等),如果 value 是
12312312321,输出将会是 11.5 GB
{{ value|filesizeformat }}

4、date
作用:将日期按照指定的格式输出,如果value=datetime.datetime.now(),按照格式Y-m-d则输出2019-02-02
{{ value|date:"Y-m-d" }}

5、slice
作用:对输出的字符串进行切片操作,顾头不顾尾,如果value=“egon“,则输出"eg"
{{ value|slice:"0:2" }}

6、truncatechars
作用:如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾,
如果value=”hello world egon 嘎嘎“,则输出"hello...",注意8个字符也包含末尾的3个点
{{ value|truncatechars:8 }}

7、truncatewords
作用:同truncatechars,但truncatewords是按照单词截断,注意末尾的3个点不算作单词,如果value=”
hello world egon 嘎嘎“,则输出"hello world ..."
{{ value|truncatewords:2 }}

8、safe(前端)
作用:出于安全考虑,Django的模板会对HTML标签、JS等语法标签进行自动转
义,例如value="<script>alert(123)</script>",模板变量
{{ value }}会被渲染成&lt; script&gt;alert(123)&lt;/script&gt;交给浏览器后会被解析成普通字符”<script>alert(123)</script>“,
失去了js代码的语法意义,但如果我们就想让模板变量{{ value }}被渲染的结果又要有语法意义,那么就用到了过滤器safe,
比如value='<a href="https://www.baidu.com">点我啊</a>',
在被safe过滤器处理后就成为了真正的超链接,不加safe过滤器则会当做普通字符显示’<a href="https://www.baidu.com">点我啊</a>‘
{{ value|safe }}
过滤器 描述 示例
upper 以大写方式输出 {{ user.name|upper}}
add 给value加上一个数值 {{ user.age|add:”5” }}
addslashes 单引号加上转义号
capfirst 第一个字母大写 `{{ ‘good’
center 输出指定长度的字符串,把变量居中 {{ “abcd”|center:”50” }}
cut 删除指定字符串 {{ “You are not a Englishman”|cut:”not” }}
date 格式化日期
default 如果值不存在,则使用默认值代替 {{ value|default:”(N/A)” }}
default_if_none 如果值为None, 则使用默认值代替
dictsort 按某字段排序,变量必须是一个dictionary
dictsortreversed 按某字段倒序排序,变量必须是dictionary
divisibleby 判断是否可以被数字整除 {{ 224|divisibleby:2 }} 返回 True
escape 按HTML转义,比如将”<”转换为”&lt”
filesizeformat 增加数字的可读性,转换结果为13KB,89MB,3Bytes等 {{ 1024|filesizeformat }} 返回 1.0KB
first 返回列表的第1个元素,变量必须是一个列表
floatformat 转换为指定精度的小数,默认保留1位小数 {{ 3.1415926|floatformat:3 }} 返回 3.142 四舍五入
get_digit 从个位数开始截取指定位置的数字 {{ 123456|get_digit:’1’}}
join 用指定分隔符连接列表 {{ [‘abc’,’45’]|join:’’ }} 返回 abc45
length 返回列表中元素的个数或字符串长度
length_is 检查列表,字符串长度是否符合指定的值 {{ ‘hello’|length_is:’3’ }}
linebreaks

标签包裹变量
{{ “Hi\n\nDavid”|linebreaks }} 返回
Hi
David
linebreaksbr 用标签代替换行符
linenumbers 为变量中的每一行加上行号
ljust 输出指定长度的字符串,变量左对齐 {{‘ab’|ljust:5}}返回 ‘ab ’
lower 字符串变小写
make_list 将字符串转换为列表
pluralize 根据数字确定是否输出英文复数符号
random 返回列表的随机一项
removetags 删除字符串中指定的HTML标记 {{value|removetags: “h1 h2”}}
rjust 输出指定长度的字符串,变量右对齐
slice 切片操作, 返回列表 {{[3,9,1]|slice:’:2’}} 返回 [3,9]`{{ 'asdikfjhihgie'
slugify 在字符串中留下减号和下划线,其它符号删除,空格用减号替换 {{ '5-2=3and5 2=3'|slugify }} 返回 5-23and5-23
stringformat 字符串格式化,语法同python
time 返回日期的时间部分
timesince 以“到现在为止过了多长时间”显示时间变量 结果可能为 45days, 3 hours
timeuntil 以“从现在开始到时间变量”还有多长时间显示时间变量
title 每个单词首字母大写
truncatewords 将字符串转换为省略表达方式 `{{ 'This is a pen'
truncatewords_html 同上,但保留其中的HTML标签 `{{ '

This is a pen

'
urlencode 将字符串中的特殊字符转换为url兼容表达方式 {{ ‘http://www.aaa.com/foo?a=b&b=c’|urlencode}}
urlize 将变量字符串中的url由纯文本变为链接
wordcount 返回变量字符串中的单词数
yesno 将布尔变量转换为字符串yes, no或maybe |{{ True|yesno }}{{ False|yesno }}{{None|yesno }} 返回 yesno maybe

2. 标签:

标签是为了在模板中完成一些特殊功能,语法为{% 标签名 %},一些标签还需要搭 配结束标签 {% endtag %}

常用标签之for标签
1、遍历每一个元素:
{% for person in person_list %}
	<p>{{ person.name }}</p>
{% endfor %} 
2、可以利用{% for obj in list reversed %}反向循环。
3、遍历一个字典: {% for key,val in dic.items %}  
	<p>{{ key }}:{{ val }}</p>
{% endfor %}
4、循环序号可以通过{{ forloop }}显示
forloop.counter 当前循环的索引值(从1开始)
forloop.counter0 当前循环的索引值(从0开始)
forloop.revcounter 当前循环的倒序索引值(从1开始) forloop.revcounter0 当前循环的倒序索引值(从0开始)
forloop.first 当前循环是第一次循环则返回True,否则返回False forloop.last 当前循环是最后一次循环则返回True,否则返回False forloop.parentloop 本层循环的外层循环
5、for标签可以带有一个可选的{% empty %} 从句,在变量person_list为空或者没有被找到时,则执行empty子 句
{% for person in person_list %}  
	<p>{{ person.name }}</p>
{% empty %}  
if 判断
# 1、注意:
{% if 条件 %}条件为真时if的子句才会生效,条件也可以是一个变量,if会对变量进行求值,在变量值为空、或者视图没有为其传值的情况下均为False
{% if xo %}
    <p>xo有值</p>
{% else %}
    <p>xo没有值</p>
{% endif %}

{% if xo %}
    <p>xo有值</p>
{% elif xo1 %}
    <p>xo1有值</p>
{% else %}
    <p>去他大爷的</p>
{% endif %}

if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断。

3. 自定义标签和过滤器

自定义过滤器

当内置的过滤器或标签无法满足我们需求时,我们可以自定义,具体操作步骤如下

  1. 在settings中的INSTALLED_APPS添加当前app的名字,不然django无法找到自定义的过滤器或标签

settings.py

# 在settings.py中找到该列表,然后加以配置
INSTALLED_APPS = [
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'app01.apps.App01Config',
 'app01', # 添加当前app的名字
]
  1. 在应用名下面新建一个templatetags文件夹(必须叫这个名字)

  2. 在该文件夹下 新建一个任意名称的py文件,在该py文件内自定义过滤器或标签

from django import template
register = template.Library() # 注意变量名必须为register,不可改变
#1、自定义过滤器
@register.filter
def my_multi_filter(v1 ,v2): # 自定义的过滤器只能定义最多两个参数,针对{{ value1 |filter_multi:value2 }},参数传递v1=value1,v2=value2
 return v1 * v2
#2、自定义标签
@register.simple_tag
def my_multi_tag(v1, v2): # 自定义的标签可以定义多个参数
 return v1 * v2
#3、自定义标签扩展之mark_safe
# 注释:我们可以用内置的标签safe来让标签内容有语法意义,如果我们想让自定义标签处理的结果也有语法意义,则不能使用内置标签safe了,需要使用mark_safe,可以实现与内置标签safe同样的功能
from django.utils.safestring import mark_safe
@register.simple_tag
def my_input_tag(id, name):
   res = "<input type='text' id='%s' name='%s' />" % (id, name)
    return mark_safe(res)
  1. 自定义过滤器或标签必须重新启动django方可生效
  2. 自定义过滤器或标签的使用
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--必须先加载存有自定义过滤器和标签的文件-->
{% load my_tags %} 
<!--salary的值为10, 经过滤器my_multi_filter的处理结果为120-->
{{ salary|my_multi_filter:12 }}
<!--结果为2-->
{% my_multi_tag 1 2 %}
<!--结果为一个input标签,该表的属性id="inp1" name="username"
注意: input的的属性值均为字符串;类型,所以my_input_tag后的;两个值均为字符串类型-->
{% my_input_tag "inp1" "username" %}
</body>
</html>
对比自定义标签与自定义过滤器
#1、自定义过滤器只能传两个参数,而自定义标签却可以传多个参数
#2、过滤器可以用于if判断,而标签不能
{% if salary|my_multi_filter:12 > 200 %}
{% else %}   {% endif %}

4. 模板的导入和继承

在实际开发中,模板文件彼此之间可能会有大量冗余代码,为此django提供了专 门的语法来解决这个问题,主要围绕三种标签的使用:include标签、extends标签、 block标签.

模板的导入之include标签
#作用:在一个模板文件中,引入/重用另外一个模板文件的内容,
{% include '模版名称' %}

事先需要再模板中 通过block划定区域

{% endblock %}
子板中如何使用
{% extends '模板的名字'%}

{% block 区域名字 %}
<h1>登录页面</h1>
{% endblock %}

一个页面上 block块越多 页面的扩展性越高
通常情况下 都应该有三片区域
{% block css %}

{% endblock %}

{% block content %}

{% endblock %}

{% block js %}

{% endblock %}

子板中还可以通过
{{ block.super }}  来继续使用母版的内容

模板的导入
    当你写了一个特别好看的form表单 你想再多个页面上都使用这个form表单
    你就可以将你写的form表单当作模块的形式导入 导入过来之后 就可以直接展示
    
    {% include 模块名 %}

模型层

ORM查询

如果要查询orm语句内部真正的sql语句有两种方式

  1. 如果是queryset对象,可以直接点query查看
  2. 配置文件中 直接配置
LOGGING = {
  'version': 1,
  'disable_existing_loggers': False,
  'handlers': {
    'console': {
      'level': 'DEBUG',
      'class': 'logging.StreamHandler',
    },
  },
  'loggers': {
    'django.db.backends': {
      'handlers': ['console'],
      'propagate': True,
      'level': 'DEBUG',
    },
  }}

只要是queryset对象就可以无限制的点queryset对象的方法

如:queryset.filter().filter().filter()

​ Django测试环境搭建

django测试环境搭建
import os


if __name__ == "__main__":
  os.environ.setdefault("DJANGO_SETTINGS_MODULE", "one_search.settings")
  import django
  django.setup()
  # 可以在下面测试django任何的py文件

补充(知道就行):

import os


if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "one_search.settings")
    import django
    django.setup()
    from app01 import models
    res = models.Book.objects.all()  # 惰性查询,你不去找他要,他就一直不显示
    print(res)
运行后如果数据库中的表没有数据,会提示,其中LIMIT表示限制一次拿多少条(参考分页)

单表查询

1 、models.Book.objects.create(title='西游记',price=123.23,publish_date='2019-		10-24')

2、from datetime import date
	ctime = date.today()
	book_obj = models.Book(title='三国演义',price=666.66,publish_date=ctime)
	book_obj.save()
1、models.Book.objects.filter(pk=3).update(price=999.66)

2、book_obj = models.Book.objects.filter(pk=3).first()
	book_obj.title = '在这儿改数据'
	book_obj.save()

models.Book.objects.filter(pk=3).delete()
查13条:

#1: .all() 查询所有
# 无参

# 返回值为QuerySet对象,QuerySet对象中包含了查询出的所有记录对象
  res = models.Book.objects.all()  # 惰性查询
  print(res)
  for i in res:
    print(i.title)
# 打印:
res = <QuerySet [<Book: 就是要改你>, <Book: 三国演义>]>
i.title = 就是要改你,三国演义
    
  
#2: filter() 过滤                 QuerySet
# 有参,参数为过滤条件
# 返回值为QuerySet对象,QuerySet对象中包含了符合过滤条件的多个记录对象
  res = models.Book.objects.filter(pk=3)# 惰性查询
  print(res) 
# 打印: <QuerySet [<Book: 就是要改你>]>



#3: get(**kwargs)								数据对象本身
#  有参,参数为筛选条件
#  返回值为一个符合筛选条件的记录对象(有且只有一个),如果符合筛选条件的对象超过一个或者没有都会抛出错误。
    res = models.Book.objects.get(pk=3)
    print(res)
# 打印:就是要改你   
    
    
#4:first()								  拿第一个
    # 无参
    # 返回查询出的第一个记录对象
    res = models.Book.objects.all()
    print(res)
    print(res.first())
# 打印: <QuerySet [<Book: 就是要改你>, <Book: 三国演义>]>    就是要改你   
  
  
#5: last()     	拿最后一个   
#  无参
# 返回查询出的最后一个记录对象
    res = models.Book.objects.all()
    print(res)
    print(res.last())
# 打印: <QuerySet [<Book: 就是要改你>, <Book: 三国演义>]>    三国演义
    
    
    
# 6.exclude  除此之外        QuerySet
#  有参,参数为过滤条件
#  返回值为QuerySet对象,QuerySet对象中包含了不符合过滤条件的多个记录对象
res = models.Book.objects.exclude(pk=3).filter(pk=4).filter(pk=1).filter(pk=4)
print(res)
# 打印: <QuerySet []>


# 7.values  QuerySet    列表套字典
# 有参,参数为字段名,可以指定多个字段
# 返回值为QuerySet对象,QuerySet对象中包含的并不是一个个的记录对象,而上多个字典,字典的key即我们传入的字段名
    res = models.Book.objects.values('title')
    for r in res:
        print(r.get('title'))
# 打印: <QuerySet [{'title': '就是要改你'}, {'title': '三国演义'}]>   就是要改你  三国演义

 
 
# 8.values_list  QuerySet    列表套元组
# 有参,参数为字段名,可以指定多个字段
# 返回值为QuerySet对象,QuerySet对象中包含的并不是一个个的记录对象,而上多个小元组,字典的key即我们传入的字段名
    res = models.Book.objects.values_list('title')
    print(res)
# 打印: <QuerySet [('就是要改你',), ('三国演义',)]>


# 9.count()  统计数据的个数
# 无参
# 返回包含记录对象的总数量
    res = models.Book.objects.count()
    res1 = models.Book.objects.all().count()
    print(res,res1)
# 打印:  2 2
    

# 10.distinct() 去重
# 如果使用的是Mysql数据库,那么distinct()无需传入任何参数
# 从values或values_list的返回结果中剔除重复的记录对象,返回值为QuerySet对象
"""去重:数据必须是一模一样的情况下才能去重"""
    res = models.Book.objects.all().distinct()
    res1 = models.Book.objects.values('title','price').distinct()
    print(res1)
# 打印: <QuerySet [{'title': '就是要改你', 'price': Decimal('999.66')}, {'title': '三国演义', 'price': Decimal('666.66')}]>



# 11.order_by()
# 有参,参数为排序字段,可以指定多个字段,在字段1相同的情况下,可以按照字段2进行排序,以此类推,默认升序排列,在字段前加横杆代表降序排(如"-id")
# 返回值为QuerySet对象,QuerySet对象中包含了排序好的记录对象
     res = models.Book.objects.order_by('price')  # 默认是升序
     res1 = models.Book.objects.order_by('-price')  # 加负号就是降序
     print(res)
# 打印 看备注


# 12.reverse()  前面必须是先结果排序才可以反转
# 无参
# 对排序的结果取反,返回值为QuerySet对象
    res = models.Book.objects.order_by('price').reverse()
    print(res)
# 打印:<QuerySet [<Book: 就是要改你>, <Book: 三国演义>]>


# 13.exists()
# 无参
# 返回值为布尔值
		 res = models.Book.objects.filter(pk=1).exists()
     print(res)
# 有查询的数据就返回True,没有就返回False

神奇的双下划线查询

表:

# 查询价格大于200的书籍
    res = models.Book.objects.filter(price__gt=200)
    print(res)
# 打印:  <QuerySet [<Book: 就是要改你>, <Book: 三国演义>, <Book: 老人与海>, <Book: 山海经>]>


# 查询价格小于200的书籍
    res = models.Book.objects.filter(price__lt=200)
    print(res)
# 打印:<QuerySet [<Book: 计算机原理>]>


# 查询价格大于或者等于200的书籍
    res = models.Book.objects.filter(price__gte=200)
    res1 = models.Book.objects.filter(price__lte=200)
    print(res,res1)
# 打印:    <QuerySet [<Book: 就是要改你>, <Book: 三国演义>, <Book: 老人与海>, <Book: 山海经>]> <QuerySet [<Book: 计算机原理>]>


# 价格是200 或者是123.23 或者666.66
    res = models.Book.objects.filter(price__in=[200,123.23,666.66])
    print(res)
# 打印: <QuerySet []>


# 价格在200 到700之间的书籍
    res = models.Book.objects.filter(price__range=(200,666.66))  # 顾头不顾尾
    print(res)
# 打印: <QuerySet [<Book: 老人与海>]>

模糊匹配

sql 原生语句使用模糊查询: like (%:匹配任意个数任意字符和 下划线 _ :匹配一个任意字符)

# 查询书籍名称中包含p的
    res = models.Book.objects.filter(title__contains='p')  # 区分大小写
    print(res)
# 打印  <QuerySet [<Book: python入门>]>


# 忽略大小写
    res = models.Book.objects.filter(title__icontains='P')  # 忽略大小写
    print(res)
# 打印  <QuerySet [<Book: python入门>]>


# 查询书籍名称是以三开头的书籍
  res = models.Book.objects.filter(title__startswith='三')
  print(res)
# 打印  <QuerySet [<Book: 三国演义>]>


# 查询书籍名称是以你开头的书籍
	res = models.Book.objects.filter(title__endswith='你')
  print(res)
# 打印 <QuerySet [<Book: 就是要改你>]>


# 查询出版日期是2019年的书籍
     res = models.Book.objects.filter(publish_date__year='2017')
  	 print(res)
# 打印 <QuerySet [<Book: 山海经>]>


# 查询出版日期是10月的书籍
     res = models.Book.objects.filter(publish_date__month='10')
  	 print(res)
# 打印 <QuerySet [<Book: 就是要改你>, <Book: 三国演义>, <Book: 老人与海>, <Book: python入门>]>

多表查询

1. 建表:

from django.db import models

# Create your models here.
"""
你在写orm语句的时候 跟你写sql语句一样
不要想着一次性写完
写一点查一点看一点
"""



class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8,decimal_places=2)
    publish_date = models.DateField(auto_now_add=True)

    # 书籍与出版社 是一对多关系
    publish = models.ForeignKey(to='Publish')
    # 书籍与作者 是多对多
    authors = models.ManyToManyField(to='Author')
    """
    authors虚拟字段
        1.告诉orm自动帮你创建第三张关系表
        2.orm查询的时候  能够帮助你更加方便的查询
    """

    def __str__(self):
        return self.title


class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=64)

    def __str__(self):
        return self.name
    """return返回的数据必须是字符串类型"""



class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    # author_detail = models.ForeignKey(unique=True,to='AuthorDetail')
    author_detail = models.OneToOneField(to='AuthorDetail')


    def __str__(self):
        return self.name

class AuthorDetail(models.Model):
    phone = models.BigIntegerField()
    addr = models.CharField(max_length=64)

一对多字段:

# 首先在test文件中加入下面代码:
import os


if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "many_search.settings")
    import django
    django.setup()
    from app01 import models
    # 创建数据
    models.Book.objects.create(title='三国演义',price=123.23,publish_id=1)  # publish_id直接传出版社主键值
    models.Book.objects.create(title='红楼梦',price=523.23,publish_id=3)  # publish_id直接传出版社主键值
    models.Book.objects.create(title='老人与海',price=23.23,publish_id=3)  # publish_id直接传出版社主键值

    publish_obj = models.Publish.objects.filter(pk=2).first()
    models.Book.objects.create(title='水浒传',price=123.23,publish=publish_obj)  # publish直接传出版社数据对象
查:
    book_obj = models.Book.objects.filter(pk=1).first()
    print(book_obj.publish)  # 获取到当前所对应的出版社对象
    print(book_obj.publish_id)  # 获取到的就是表中的实际字段
    
    
# 打印:	东海出版社 ,1
改:
    models.Book.objects.filter(pk=1).update(publish_id=3)
    publish_obj = models.Publish.objects.filter(pk=2).first()
    models.Book.objects.filter(pk=1).update(publish=publish_obj)

删:
models.Publish.objects.filter(pk=2).delete()
# 默认也是级联更新 级联删除

多对多的表关系:

增:
# 给主键为3的书籍添加两个作者 1 2
book_obj = models.Book.objects.filter(pk=3).first()
# print(book_obj.authors)  # 就相当于 已经在书籍和作者的关系表了
# book_obj.authors.add(1)
# book_obj.authors.add(2,3)
author_obj = models.Author.objects.filter(pk=1).first()
author_obj1 = models.Author.objects.filter(pk=2).first()
# book_obj.authors.add(author_obj)
book_obj.authors.add(author_obj,author_obj1)

    """
    add() 括号内既可以传数字也可以传数据对象并且都支持传多个
    """
改:
# 修改关系
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.set([3,])
book_obj.authors.set([1,3])
author_obj = models.Author.objects.filter(pk=1).first()
author_obj1 = models.Author.objects.filter(pk=2).first()
book_obj.authors.set((author_obj,))
book_obj.authors.set((author_obj,author_obj1))
"""
set() 括号内 既可以传数字也传对象 并且也是支持传多个的
但是需要注意 括号内必须是一个可迭代对象
"""
book_obj = models.Book.objects.filter(pk=3).first()
# book_obj.authors.remove(2)
# book_obj.authors.remove(1,2)
author_obj = models.Author.objects.filter(pk=1).first()
author_obj1 = models.Author.objects.filter(pk=2).first()
#book_obj.authors.remove(author_obj)
book_obj.authors.remove(author_obj,author_obj1)
    """
    remove() 括号内 既可以传数字也传对象 
    并且也是支持传多个的
    """
清空:
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.clear()
"""clear()括号内不需要传任何参数 直接清空当前书籍对象所有的记录"""

ORM跨表查询

谁有外键字段谁就是正向

没有外键字段的就是反向

正向查询按字段,反向查询表名小写

书籍对象 查 出版社 外键字段在书籍 正向查询

出版社 查 书籍 外键字段书籍 反向查询

基于对象的跨表查询,子查询

基于双下划线的跨表查询 连表查询

1.基于对象的跨表查询 子查询

步骤:先获取一个数据对性,然后利用对象点的方式查询到所对应的数据

2.基于双下划线的跨表查询 连表查询

表:app01_book

表:app01_book_authors

表:app01_author

表:app01_authordetail

表:app01_publish

# 1.查询书籍是三国演义的出版社名称
book_obj = models.Book.objects.filter(title='三国演义').first()
# 正向查询按字段
print(book_obj.publish.name)
print(book_obj.publish.addr)
# 打印 山海出版社山海

#2.查询书籍主键是2的作者姓名
book_obj = models.Book.objects.filter(pk=2).first()
print(book_obj.authors)
app01.Author.Noneprint(book_obj.authors.all())
# 打印: app01.Author.None,  <QuerySet [<Author: 钱>]>


# 3.查询作者是赵的手机号
author_obj = models.Author.objects.filter(name='赵').first()
print(author_obj.author_detail.phone)
print(author_obj.author_detail.addr)
# 打印: 110,  山东

"""
正向查询 按字段 当该字段所对应的数据有多个的时候 需要加.all()
否者点外键字段直接就能够拿到数据对象
"""

# 4.查询出版社是山海出版社出版过的书籍
publish_obj = models.Publish.objects.filter(name='山海出版社').first()
print(publish_obj.book_set)
app01.Book.Noneprint(publish_obj.book_set.all())
# 打印 app01.Book.None,  <QuerySet [<Book: 三国演义>, <Book: 水浒传>]>

# 5.查询作者是jason写过的所有的书
author_obj = models.Author.objects.filter(name='jason').first()
print(author_obj.book_set)
app01.Book.Noneprint(author_obj.book_set.all())
# 打印app01.Book.None,  <QuerySet [<Book: 三国演义>]>


# 6.查询手机号是110的作者
author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
print(author_detail_obj.author)
print(author_detail_obj.author.name)
print(author_detail_obj.author.age)
# 打印: 赵, 赵, 18

"""
反向查询按表名小写
什么时候需要加_set
当查询的结果可以是多个的情况下 需要加_set.all()
什么时候不需要加_set
当查询的结果有且只有一个的情况下 不需要加任何东西 直接表名小写即可"""

# 7.查询书籍是python入门的作者的手机号
book_obj = models.Book.objects.filter(title='三国演义').first()
print(book_obj.authors.all())
# 打印 <QuerySet [<Author: 赵>]>

# 2.基于双下划綫的跨表查询   连表查询
"""MySQL
		left join
  	inner join
  	right join
  	union
"""
# 1.查询书籍是三国演义的出版社名称正向
res = models.Book.objects.filter(title='三国演义').values('publish__name')
print(res)
# 打印: <QuerySet [{'publish__name': '山海出版社'}]>

# 反向
res = models.Publish.objects.filter(book__title='三国演义').values('name')
print(res)
# 打印: <QuerySet [{'name': '山海出版社'}]>

# 2.查询作者是李的手机号码正向
res1 = models.Author.objects.filter(name='李').values('author_detail__phone')
print(res1)
# 打印:<QuerySet [{'author_detail__phone': 114}]>

# 反向
res = models.AuthorDetail.objects.filter(author__name='jason').values('phone','author__age')
print(res)
# 打印:<QuerySet [{'phone': 114, 'author__age': 21}]>

# 3.查询手机号是120的作者姓名
res2 = models.AuthorDetail.objects.filter(phone=120).values('author__name')
print(res2)
# 打印: <QuerySet [{'author__name': '钱'}]>

res = models.Author.objects.filter(author_detail__phone=120).values('name','author_detail__addr')
print(res)
# 打印: <QuerySet [{'name': '钱', 'author_detail__addr': '河北'}]>

# 4.查询出版社是山海出版社出版的书籍名称
res = models.Publish.objects.filter(name='山海出版社').values('book__title','addr')
print(res)
# 打印:<QuerySet [{'book__title': '三国演义', 'addr': '山海'}, {'book__title': '水浒传', 'addr': '山海'}]>

# 5.查询作者是钱的写过的书的名字和价格
res = models.Author.objects.filter(name='钱').values('book__title','book__price')print(res)
# 打印: <QuerySet [{'book__title': '红楼梦', 'book__price': Decimal('523.23')}]>

# 6.查询书籍是三国演义的作者的手机号
res = models.Book.objects.filter(title='三国演义').values('authors__author_detail__phone')
print(res)
# 打印: <QuerySet [{'authors__author_detail__phone': 110}]>

聚合函数

关键字:aggregate

# 导入模块   
from django.db.models import Max,Min,Sum,Count,Avg
res = models.Book.objects.all().aggregate(Avg('price')) # 求所有书的平均价格
res1 = models.Book.objects.all().aggregate(Max('price')) # 求所有书的最大价格
res2 = models.Book.objects.all().aggregate(Min('price')) # 求所有书的最小价格
res3 = models.Book.objects.all().aggregate(Sum('price')) # 求所有书的价格和
res4 = models.Book.objects.all().aggregate(Count('title')) # 求所有书的计数
res5 = models.Book.objects.all().aggregate(Avg('price'),Max('price'),Min('price'),Sum('price'),Count('title'))
print(res5)

# 打印: 
{'price__avg': 198.73, 'price__max': Decimal('523.23'), 
 'price__min': Decimal('23.23'), 'price__sum': Decimal('794.92'), 'title__count': 4}  

分组查询

关键字:annotate

# 1.统计每一本书的作者个数
res = models.Book.objects.annotate(author_num=Count('authors')).values('author_num')
print(res)

# 打印: <QuerySet [{'author_num': 1}, {'author_num': 1}, {'author_num': 1}, {'author_num': 1}]>


# 2.统计出每个出版社卖的最便宜的书的价格
res = models.Publish.objects.annotate(price_min=Min('book__price')).values('price_min')
print(res)

# 打印: <QuerySet [{'price_min': Decimal('23.23')}, {'price_min': Decimal('123.23')}, {'price_min': None}]>


# 3.统计不止一个作者的图书
"""
步骤:
1.统计每本书对应的作者个数
2.基于上面的结果 筛选出作者个数大于1 的

"""
res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1).values('author_num')
print(res)

# 打印: <QuerySet []>


# 4.查询各个作者出的书的总价格
res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('sum_price')
print(res)

# 打印: <QuerySet [{'sum_price': Decimal('125.23')}, {'sum_price': Decimal('523.23')}, {'sum_price': Decimal('23.23')}, {'sum_price': Decimal('123.23')}]>

F与Q查询

from django.db.models import F, Q
# 1.查询出卖出数大于库存数的书籍
res = models.Book.objects.filter(maichu__gt=F('kucun'))
print(res)

# 打印: <QuerySet []>


# 2.将所有的书的价格 全部提高100块
models.Book.objects.update(price=F('price') + 100)

Q:

res = models.Book.objects.filter(title='三国演义', price=123.33) # 逗号就是and
res1 = models.Book.objects.filter(Q(title='三国演义'), Q(price=544.44))  # 逗号就是and
res2 = models.Book.objects.filter(Q(title='三国演义') | Q(kucun=666))  # 用来Q之后 就能够支持|表示或
res3 = models.Book.objects.filter(~Q(title='三国演义') | Q(kucun=666))  # esc下面那个键 波浪号  表示非
print(res)
print(res1)
print(res2)
print(res3)

# 打印结果: <QuerySet []>, <QuerySet []>, <QuerySet [<Book: 三国演义>], <QuerySet [<Book: 红楼梦>, <Book: 水浒传>, <Book: 老人与海>]>
Q查询进阶用法 用Q产生对象 然后再使用
q = Q()
q.connector = 'or'
q.children.append(('title__icontains','三'))
q.children.append(('kucun',666))
res = models.Book.objects.filter(q)
print(res)
"""
字符串的左边 跟变量名条件书写一模一样
"""

# 打印: <QuerySet [<Book: 三国演义>]>
only会将括号内的字段对应的值 直接封装到返回给你的对象中  点该字段 不需要再走数据库一旦你点了不是括号内的字段  就会频繁的去走数据库查询

ORM常用字段:

AutoField
int自增列,必须填入参数 primary_key=True。当model中如果没有自增列,则自动会创建一个列名为id的列。

IntegerField
一个整数类型,范围在 -2147483648 to 2147483647。(一般不用它来存手机号(位数也不够),直接用字符串存,)

CharField
字符类型,必须提供max_length参数, max_length表示字符长度.

DateField
日期字段,日期格式  YYYY-MM-DD,相当于Python中的datetime.date()实例。

DateTimeField
日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例。

这里需要知道的是Django中的CharField对应的MySQL数据库中的varchar类型,没有设置对应char类型的字段,但是Django允许我们自定义新的字段,下面我来自定义对应于数据库的char类型

from django.db import models

# Create your models here.
#Django中没有对应的char类型字段,但是我们可以自己创建
class FixCharField(models.Field):
    '''
    自定义的char类型的字段类
    '''
    def __init__(self,max_length,*args,**kwargs):
        self.max_length=max_length
        super().__init__(max_length=max_length,*args,**kwargs)

    def db_type(self, connection):
        '''
        限定生成的数据库表字段类型char,长度为max_length指定的值
        :param connection:
        :return:
        '''
        return 'char(%s)'%self.max_length
#应用上面自定义的char类型
class Class(models.Model):
    id=models.AutoField(primary_key=True)
    title=models.CharField(max_length=32)
    class_name=FixCharField(max_length=16)
    gender_choice=((1,'男'),(2,'女'),(3,'保密'))
    gender=models.SmallIntegerField(choices=gender_choice,default=3)

字段合集:

AutoField(Field)
        - int自增列,必须填入参数 primary_key=True

    BigAutoField(AutoField)
        - bigint自增列,必须填入参数 primary_key=True

        注:当model中如果没有自增列,则自动会创建一个列名为id的列
        from django.db import models

        class UserInfo(models.Model):
            # 自动创建一个列名为id的且为自增的整数列
            username = models.CharField(max_length=32)

        class Group(models.Model):
            # 自定义自增列
            nid = models.AutoField(primary_key=True)
            name = models.CharField(max_length=32)

    SmallIntegerField(IntegerField):
        - 小整数 -32768 ~ 32767

    PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正小整数 0 ~ 32767
    IntegerField(Field)
        - 整数列(有符号的) -2147483648 ~ 2147483647

    PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正整数 0 ~ 2147483647

    BigIntegerField(IntegerField):
        - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807

    BooleanField(Field)
        - 布尔值类型

    NullBooleanField(Field):
        - 可以为空的布尔值

    CharField(Field)
        - 字符类型
        - 必须提供max_length参数, max_length表示字符长度

    TextField(Field)
        - 文本类型

    EmailField(CharField):
        - 字符串类型,Django Admin以及ModelForm中提供验证机制

    IPAddressField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制

    GenericIPAddressField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
        - 参数:
            protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
            unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both"

    URLField(CharField)
        - 字符串类型,Django Admin以及ModelForm中提供验证 URL

    SlugField(CharField)
        - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)

    CommaSeparatedIntegerField(CharField)
        - 字符串类型,格式必须为逗号分割的数字

    UUIDField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证

    FilePathField(Field)
        - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
        - 参数:
                path,                      文件夹路径
                match=None,                正则匹配
                recursive=False,           递归下面的文件夹
                allow_files=True,          允许文件
                allow_folders=False,       允许文件夹

    FileField(Field)
        - 字符串,路径保存在数据库,文件上传到指定目录
        - 参数:
            upload_to = ""      上传文件的保存路径
            storage = None      存储组件,默认django.core.files.storage.FileSystemStorage

    ImageField(FileField)
        - 字符串,路径保存在数据库,文件上传到指定目录
        - 参数:
            upload_to = ""      上传文件的保存路径
            storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
            width_field=None,   上传图片的高度保存的数据库字段名(字符串)
            height_field=None   上传图片的宽度保存的数据库字段名(字符串)

    DateTimeField(DateField)
        - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]

    DateField(DateTimeCheckMixin, Field)
        - 日期格式      YYYY-MM-DD

    TimeField(DateTimeCheckMixin, Field)
        - 时间格式      HH:MM[:ss[.uuuuuu]]

    DurationField(Field)
        - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型

    FloatField(Field)
        - 浮点型

    DecimalField(Field)
        - 10进制小数
        - 参数:
            max_digits,小数总长度
            decimal_places,小数位长度

    BinaryField(Field)
        - 二进制类型
      
      
      
对应关系:
    'AutoField': 'integer AUTO_INCREMENT',
    'BigAutoField': 'bigint AUTO_INCREMENT',
    'BinaryField': 'longblob',
    'BooleanField': 'bool',
    'CharField': 'varchar(%(max_length)s)',
    'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
    'DateField': 'date',
    'DateTimeField': 'datetime',
    'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
    'DurationField': 'bigint',
    'FileField': 'varchar(%(max_length)s)',
    'FilePathField': 'varchar(%(max_length)s)',
    'FloatField': 'double precision',
    'IntegerField': 'integer',
    'BigIntegerField': 'bigint',
    'IPAddressField': 'char(15)',
    'GenericIPAddressField': 'char(39)',
    'NullBooleanField': 'bool',
    'OneToOneField': 'integer',
    'PositiveIntegerField': 'integer UNSIGNED',
    'PositiveSmallIntegerField': 'smallint UNSIGNED',
    'SlugField': 'varchar(%(max_length)s)',
    'SmallIntegerField': 'smallint',
    'TextField': 'longtext',
    'TimeField': 'time',
    'UUIDField': 'char(32)',
posted on 2019-10-28 23:46  luelue  阅读(181)  评论(0编辑  收藏  举报