Django框架
Django是什么
Django是别人写好的web框架,包罗万象,只要按照别人写好的方法来一个个的对应上去用就可以
1 MVC
M:model(模型层), 跟数据库打交道的
V:view(视图层), html页面
C:controller(控制器层), 处理逻辑,调用页面和数据
2 MTV
M 代表模型(Model): 负责业务对象和数据库的关系映射(ORM)。
T 代表模板 (Template):负责如何把页面展示给用户(html)。
V 代表视图(View): 负责业务逻辑,并在适当时候调用Model和Template
Django的安装
pip install Django==1.1.1(这里的1.1.1是版本)
如何验证是都安装成功:cmd中,输入django-admin有东西跳出来,说明已经安装成功
Django项目创建
打开cmd,切换到安装Django的目录下,输入 djang-admin startproject mysite(mysite是项目名)
pycharm创建项目的话,file→newproject→Django
项目启动
打开cmd,切换到django目录下,输入命令
python manager.py runsever
python manager.py runsever 127.0.0.1:8000(本地启动默认是8000端口,也可以把IP地址省略)
pycharm启动:
点右上角的绿色启动按钮即可
创建的时候,老版本的django可能会右一个bug,就是templats文件夹的路径是错误,需要手动修改
在setting文件中,路径修改成os.path.jion(BASE_DIR, ‘templates’)
创建应用
什么是应用
Django相当于一个公司,而应用相当于公司下面的一个个部门,可以有多个部门
同理:一个django下面,可以有多个应用,比如ATM项目,下面有注册,登录,转账 等应用
如何创建应用
应用名 符合规范即可
打开cmd,定位到django项目目录下,输入 python manager.py startapp app01(应用名)
或者在pycharm中,tools → Run manager.py Task,之后输入 start app01
注意:创建完成之后,在setting.py(配置目录)中要注册,才生效(配置的时候,需要从app01里面导入views)
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01', # 新建的应用名 ]
主要文件介绍
mysite1 # 项目名称 app01 # 应用名称 migrations # 迁移数据库的时候,会把迁移记录存在这个文件夹里面 __init__.py # 包 admin.py # django自带的后台管理系统 apps.py # 应用配置相关,暂时不用管 models.py # M:model层,跟数据库打交道的 tests.py # 测试脚本文件 views.py # V:view(逻辑层) mysite1 __init__.py settings.py # 整个django项目的配置文件 urls.py # 路由文件,记录后缀与视图函数的对应关系 wsgi.py # wsgiref服务器,在本地项目使用wsgiref, 后续上线的时候,我们会换成 uwsgi服务器,uwsgi承载的并发量更高,而wsgiref承载的并发量小,WSGI协议 templates # 写html页面的地方 db.sqlite3 # django框架自带的小型数据库,mysql manage.py # 整个django项目的入口文件
django的小白三板斧
是写在views下面的,默认只有一个render
1. HttpResponse() 2. render() 3. redirect # return HttpResponse('index') # 返回值是字符串 # return render(request, 'index.html') # 渲染html页面的 # return redirect('http://www.mzitu.com') # 重定向的 # return redirect('/home/') # 重定向的
静态文件配置
什么是静态文件
比如写好的html,js,css页面,图片,等
html文件,直接放在templates文件夹下
其他的文件,放在static文件夹下(需要自己手动新建)
配置文件路径
在setting.py的最后,写入以下(django提供的,变量名一个字母都不能变)
STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static') ]
配置完成之后,新建的html页面,可以直接动态导入
在head中
动态引入 {% load static %} <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}"> <script src="{% static 'js/jquery.min.js' %}"></script> <script src="{% static 'js/bootstrap.min.js' %}"></script>
form表单实现登录功能
因为get请求的话,请求的内容会在浏览器地址栏里显现出来,所以不安全,所以要用post请求来提交,如下
<form action="" method="post"> 1. 什么都不写:当前地址提交 2. 写全: 3. 写后缀:/login/
使用form提交的时候需注意,先去配置文件中,把csrf中间件给注释掉,如下
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
登录功能简单实现
html页面内容
<html lang="en"> <head> <meta charset="UTF-8"> <title>登录</title> {% load static %} <link rel="stylesheet" href="{% static 'css/bootstrap.css' %}"> <script src="{% static 'js/jquery-3.6.0.js' %}"></script> <script src="{% static 'js/bootstrap.js' %}"></script> </head> <body> <div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <h1 class="text-center">登录页面</h1> <form action="" method="post"> <div class="form-group"> <lable for="username">用户名:</lable> <input type="text" id="username" name="username" class="form-control"> </div> <div class="form-group"> <lable for="username">密码:</lable> <input type="password" id="password" name="password" class="form-control"> </div> <input type="submit" class="btn btn-success" value="点我登录"> </form> </div> </div> </div> </body> </html>
在后端 要 接收前端name 中的 username 和password 的数据
注意:前端穿过来变量,都是 str 的形式,用request.'POST'来接收
def login(request): if request.method == 'POST': # 因为会受到2个请求,get 和 post 所以需要判断 # 接收登录的 用户名 和 密码 username = request.POST.get('username') password = request.POST.get('password')
pycharm连接数据库
很简单,用过就会,略
Django连接MySQL数据库
1, 更改django中数据库的设置(在配置文件中修改)
2,在应用下面的__init__.py下面,输入一下代码
DATABASES = { #这是配置文件里的内容 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'app01', 'HOST': 'localhost', 'PORT': '3306', 'USER': 'root', 'PASSWORD': '123', 'CHARSET': 'utf8' } }
Django操作orm
什么是orm
是一个关系映射对象
orm不仅在python中有,其他语言中也有orm,可以操作数据库
orm的特点
在操作的时候,不需要在写原生的sql语句了
orm写在哪里
写在应用下的models.py 文件里面
如何使用
要使用orm,必须先创建好库
用函数创建,一下是一一对应关系
表名 >> 类名
记录 >> 对象
字段 >> 类属性
如何创建一张表
表名:UserInfo
字段:uid,username,password
class UserInfo(models.Model): uid = models.AutoField(primary_key=True) # 说明这个是主键,自增 username = models.CharField(max_length=32) # 32位长度的字符串 password = models.CharField(max_length=32) def __str__(self): # __str__方法,打印的时候,显示用户名 return self.username
在models文件下写完之后,需要执行一下2行代码
python3 manage.py makemigrations # 只是生成迁移记录
python3 manage.py migrate # 才是真正的生成数据
字段的增删改查
字段,传统的就叫做表头
原表如下
class Author(models.Model): name = models.CharField(max_length=32) password = models.CharField(max_length=64)
增加字段
比如要添加hobby字段
class Author(models.Model): name = models.CharField(max_length=32) password = models.CharField(max_length=64) hobby = models.CharField(max_length=32, verbose_name='爱好', default='study') 默认是study hobby = models.CharField(max_length=32, verbose_name='爱好', null=True) 字段可以为空
删除字段
直接注释掉对应的字段,在进行数据库的迁移和生成
修改字段
直接修改对应的字段,然后进行数据库迁移
查询字段
1.直接用pycharm,或者Navicat查询
2.后端匹配前端数据,查询数据库的话,直接看代码
数据的增删改查(重点)
先要在views文件中,导入models
from app01 import models
应用场景:
用户在前端,增删改查他自己的数据,就是访问了相对应的页面执行的程序
数据的增加
推荐用objects组件,里面封装了很多东西,也比较方便
def func(request):
第一种(推荐,因为只需要一步,简单) # res = models.Author.objects.create(name='gudawei', phone='111') # print(res)
第二种
res = models.Author(name='gudawei2', phone='222') # 这个只是生成了对象 res.save() # 这才是真正的保存 return render(request, 'func.html')
数据的删除
分物理删除和软删除
物理删除
def func(request): res = models.Author.objects.filter(name='gudawei2').delete() # 上面的filter(筛选)中,name可以是任何一个字段里的值,
# 比如pk=1(pk指的是主键),name='gudawei', phone='111'都可以 print(res) # 打印的话,是显示影响的行数, (1, {'app01.Author': 1}) return render(request, 'func.html')
软删除(常用)
不过在企业中,一般做的是软删除,不是真正的删除,因为可以恢复数据,可以减少企业损失
新建一个is_delete字段,默认值设置成0,代表当前数据有效,如果是1,那么就是删除
'''默认值是0,如果删除了,把默认值是设置成1,那就代表删除了''' is_delete = models.BooleanField(default=0) # 存true:1,False:0
models.Author.objects.filter(pk=1).update(is_delete=True) # 执行之后,数据库中id=1的字段 的is_delete的值,就是1,代表软删除
修改数据
现在的authot表格如下
需求:把gudawei的phone修改成444
在SQL中,是update author set phone(phone=444)
在orm中
models.Author.objects.filter(name='gudawei').update(phone='444')
数据的查询
用objects下面的filter方法来制定条件查询
最后加 .first 的话,如果前端传来用户名匹配到的对象为空,那就说明用户名不存在
filter里面填的是前端传来的值如果是username,那就是filter(username=‘前端传来的username’).first(),如果判断是None,那就说明该用户不存在
res = models.Author.objects.filter(phone=444) # 查询phone是444的第一条记录
print(res[0].name)
# 打印的是queryset对象,可以后面加。来取值,空就是没有<QuerySet []>
res = models.Author.objects.filter(phone=666).first() # 查询phone是666的第一条记录,last()就是最后一条
print(res) # 查询不到,打印是 None
模板页面传值
就是把后台的代码,传值到前端页面中用,通过render来传过去,有2中方式
第一种(因为变量可能比较多,不推荐)
def func(request): a = '哈哈哈' return render(request, 'func.html', {'username': a})
<body> <div> func{{ username }} </div> </body> </html>
第二种(用locals()因为数据库里传来的值都是对象,可以取值)
def func(request): author_list = models.Author.objects.all() return render(request, 'func.html', locals())
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div> func {% for author in author_list %} <!-- 这里是for循环,有头有尾,把表格写活了--> <div> <p>{{ author.name }}</p> <p>{{ author.phone }}</p> </div> {% endfor %} </div> </body> </html>
使用orm创建外键关系
外键关系有三种
一对一 外键字段建立在查询频率较多的表
一对多 外键字段建在多的一方
多对多 外键字段不建在任何一张表中,而是会自动创建出第三张虚拟表
当创建外检关系的时候,字典名后面会自动加上_id,不需要手动添加
创建外键字段的时候,需要先创建基础字段,写完之后,在一个个写外键关系
class Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8, decimal_places=2) # 创建和出版社的外键关系 publish = models.ForeignKey(to='Publish') # to指哪张表,to_field指的是哪个字段来关联,如果是主键id可以省略 authors = models.ManyToManyField(to='Author') class Publish(models.Model): title = 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) # 与作者详情表建立一对一的外键关系 author_detail = models.OneToOneField(to='AuthorDetail') class AuthorDetail(models.Model): phone = models.CharField(max_length=64) wx = models.CharField(max_length=32)
在Django 1.X中,级联更新级联删除 是 默认的
在django 2.X中,级联更新级联删除 是 需要手动作指定的,
需要在ForeignKey时候,里面要加上一个参数 on_delete=models.CASCADE
OneToOneFreid里面,同样加上on_delete=models.CASCADE
django 2.X中,on_delete属性有多个参数
CASCADE:这就是默认的选项,级联删除,你无需显性指定它。 PROTECT: 保护模式,如果采用该选项,删除的时候,会抛出ProtectedError错误。 SET_NULL: 置空模式,删除的时候,外键字段被设置为空,前提就是blank=True, null=True,定义该字段的时候,允许为空。 SET_DEFAULT: 置默认值,删除的时候,外键字段设置为默认值,所以定义外键的时候注意加上一个默认值。 SET(): 自定义一个值,该值当然只能是对应的实体了
Django的请求周期生命图(必须会自己画出来)
路由层
1. 路由匹配:
urls.py 这个文件是django框架的总路由文件,意味着还有分路由文件,每个应用都支持有自己的路由文件。 # 控制django是否自动加斜杠匹配 APPEND_SLASH = False url(r'^test/$', views.test), ''' 路由匹配规则: 路由从上往下依次匹配,如果路由第一次匹配到了,那么,就会执行对应的视图函数,就不在往下匹配了。 ''' 2. django2中的路由文件 path # 精准匹配 re_path # 类似于url,支持正则表达式
无名分组有名分组
# 1. 无名分组: 正则表达式匹配大的内容用括号括起来 # 2. 有名分组:正则表达式匹配大的内容用括号括起来,然后起个名字 url(r'^test/(\d+)', views.test),
#正则中/d+ 表示至少匹配一个数字,匹配完成之后,以位置参数的形式传给views.test,text函数中,需要加一个参数 url(r'^test/(\d+)/(\w+)/(\d+)/(\d+)', views.test), 无名分组会把括号中匹配的数字当成位置参数传给视图函数, 支持多个匹配规则。 url(r'^test/(?P<year>\d+)/(?P<month>\d+)/(?P<day>\d+)', views.test), 有名分组会把括号中匹配的数字当成关键字参数传给视图函数, 支持多个匹配规则。
在test函数中,添加关键字参数year, mont,day, # 无名和有名不能混合使用 get请求传参方式: https://www.bilibili.com/movie/?spm_id_from=333.1007.0.0 https://www.cnblogs.com/qingmiaokeji/p/16026631/a/1/b/2/c/3.html
反向解析(了解)
就是通过路由别名可以得到该别名对应的路由地址
需要在views文件里面,render之后导入reverse
# 后端反向解析 url(r'^test/v1/v2/v3/v4', views.test, name='aaaa'), print(reverse('aaaa')) 打印的结果是test/v1/v2/v3/v4
# 前端反向解析
打开浏览器,检查,elements中能看到,路由名字可以变,但是只要指定aaaa就是动态的 url(r'^test/v1/v2/v3/v4', views.test, name='aaaa'), <a href="{% url 'aaaa' %}">点我</a>
# 无名分组反向解析 url(r'^test/(\d+)', views.test, name='xxxx'), # 后端反向解析 # 一般用不到,如果真的用到了,一般传主键值,或这id值 print(reverse('xxxx', args=(id, ))) # 同样在test中需要加上1个参数 # 前端反向解析 <a href="{% url 'xxxx' 3333 %}">点我</a> # 333是符合urls中的正则表达式
# 有名分组反向解析 url(r'^test/(?P<year>\d+)/(?P<month>\d+)', views.test, name='xxxx'), # 后端反向解析 print(reverse('xxxx', args=(id, ))) # 前端反向解析 <a href="{% url 'xxxx' year=2022 month=9 %}">点我</a>
路由分发
因为项目大了之后,总路由压力会很大,所以可以当成中转站,分发给不同的应用
django支持每一个应用下面有自己的
1. urls.py
2. static文件夹
3. templates
路由分发的两种方式: 1. from app01 import urls as app01_urls from app02 import urls as app02_urls # url(), 的第一个参数不能加$ url(r'^app01/', include(app01_urls)), url(r'^app02/', include(app02_urls)), 2. # 第二种方式,需要导入模块
from django.conf.urls import include
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:xxxx')) print(reverse('app02:xxxx'))
虚拟环境
# 针对不同的项目,创建出来一个新的python环境,类似于是一个纯净版的python解释器
很简单,创建试一下就知道了
# 1. 混合项目开发:用不到json格式的数据 # 2. 前后端分离项目:用的都是json格式的数据 json格式的数据:跨语言数据传输 序列化数据为json import json 1. dumps: 序列化 2. loads:反序列化 # 前端中序列化和反序列化 1. JSON.stringify() # 序列化 2. JOSN.parse() # 反系列化 from django.http import JsonResponse def ab_json(request): # user_dict = {'username': 'ly大帅哥', 'gender': 'male'} l = [1, 2, 3] # res = json.dumps(user_dict, ensure_ascii=False) # return HttpResponse(res) # return JsonResponse(user_dict,json_dumps_params={'ensure_ascii':False}) return JsonResponse(l, safe=False)
CBV的书写
1. FBV: function base viewd 基于函数的视图,之前的def都是FBV
2. CBV:class base viewd 基于类的视图
CBV的路由: # cbv的路由如何写?固定写法 url(r'^index_cbv/', views.IndexView.as_view()),
CBV:必须继承View类 from django.views import View # 在CBV中,类中的方法名不能所以叫,只能叫请求方式的名称 # get, post # 请求方式一共有8种,目前学了2种 # 关键问题在于:CBV的路由该如何写? # 是如何请求到get方法的呢? # 如果你是get请求方式,那么就会触发get方法 # 如果你是post请求方式,那么就会触发post方法 class IndexView(View): def get(self, request): return HttpResponse('get') def post(self, request): return HttpResponse('post')
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="" method="post" enctype="multipart/form-data"> # 注意这里的改变 <p> <input type="file" name="mafile"> </p> <input type="submit" value="提交"> </form> </body> </html>
后端接受
def ab_file(request): if request.method == 'POST': print(request.FILES) # <MultiValueDict: {'myfile': [<InMemoryUploadedFile: 1211.png (image/png)>]}> file_obj = request.FILES.get('myfile') print(file_obj) # 接收到图片的名字 1211.png print(file_obj.name) # 接收到图片的名字 1211.png print(file_obj.size) # 接收到图片的名字 1211.png if file_obj.size > 1024000: # 限制大小1M return HttpResponse('文件太大了,重新上传') # 这里可以用那个ajax做弹窗告知,点击确定后返回当前页 # 然后要把图片存进来 with open(file_obj.name, 'wb') as f: # 这里的file_obj.name是路径,file_obj是对象,对应的名字下面的2进制数据 # 上传文件读取的时候要一行一行的读取,目的是为了防止内存爆满 for line in file_obj: # print(line) f.write(line) return render(request, 'ab_file.html')
当然,为了防止图片很多,名字重复覆盖的问题,可以自己加随机的字段来拼接图片的名字之后来存储
import uuid rend_str=uuid.uuid4() import random file_path = str(rend_str) + '.png' # with open(file_obj.name, 'wb') as f: with open(file_path, 'wb') as f: for line in file_obj: f.write(line)
CBV的源码分析(这个一定要搞懂)
自己看视频分析后,自己看源码
模板层
模板语法 之 传值
1. 传值 {{ }} # 一般给变量使用相关 {% %} # 一般跟逻辑相关的 2. 所有需要加括号的都不用加 3. '''模板文件一律使用点语法'''
# 相当于python的内置函数 语法: {{ 变量|过滤器:参数 }} '''竖杠左边的变量当成过滤器的第一个参数,冒号后面的当成第二个参数 如果冒号后面没有参数了,那就是只有一个参数。 过滤器最多传递两个参数。 ''' # 过滤器有很多个,大概有六七十个,我们需要掌握四五个就行 1. length:求长度 2. default:竖杠左边为真,就显示竖杠左边的值,如果为假,则显示默认值。{{Tue|dafault:‘我是默认值’}} 3. date:格式化时间 4. filesizeformat 5. safe ''' 安全相关的: 1. MySQL中会出现SQL注入的问题 2. 前端中会出现xss攻击 3. django中的form表单中会出现csrf跨站请求问题 ''' '''xss攻击:不让外部的标签直接生效''' # 前端的代码有时候也可以在后端写好,然后传递到前端使用 from django.utils.safestring import mark_safe s = mark_safe('<h1>xss攻击</h1>')
模板语法 之 标签
字典三剑客:keys,values,items
# 在html中可以使用 if else for循环等 {% for foo in user_dict.keys %} <p> {{ foo }} </p> {% endfor %} {% for foo in user_dict.values %} <p> {{ foo }} </p> {% endfor %} {% for foo in user_dict.items %} <p> {{ foo }} </p> {% endfor %}
模板语法 之 继承
# 导入模板 {% include 'left.html' %} # 模板继承 {% extends 'home.html' %} {% block css %} <style> h1 { color: red; } </style> {% endblock %} {% block js %} <script> alert(123) </script> {% endblock %} {% block content %} <h1>登录页面</h1> <form action=""> <p>username: <input type="text" class="form-control"></p> <p>password: <input type="text" class="form-control"></p> <input type="submit" class="btn btn-success btn-block"> </form> {% include 'left.html' %} {% endblock %}
模板语法 之 导入
{% include 'aa.html' %}
这里的aa.html是被导入的页面,一般只导入局部的页面
被导入页面中所有东西要删掉,只写需要被导入的东西
测试环境的准备
在test.py文件下,输入以下代码
import os if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day06.settings") import django django.setup() '''测试环境已经准备好了,只能在这个代码一下写ORM相关操作,否则不生效''' from app01 import models
orm的其他查询方法
# get # res = models.Book.objects.filter(pk=10).first() # 查不到数据为None # try: # res = models.Book.objects.get(pk=10) # 查不到直接报错,而且,get查询的数据有切只能为一条 # print(res) # order by 排序, # res = models.Book.objects.all().order_by('id') # 默认是升序排列 # res = models.Book.objects.all().order_by('-id') # 降序排列 # res = models.Book.objects.all().order_by('-id', 'price') # 降序排列 # res = models.Book.objects.all().order_by('-id', '-price') # 降序排列 # sql: select * from table order by id desc, price desc, title asc # res = models.Book.objects.all().order_by('-id', '-price', 'title') # 降序排列 # print(res) # 反转数据:前提是排序了 # res = models.Book.objects.all().order_by('id').reverse() # print(res) # count: # 查询表中一共多少条数据 # sql: select count(*) from table where id=1 # res = models.Book.objects.count() # 5 # res = models.Book.objects.filter(id=1).count() # 5 # print(res) # values, value_list # SQL:select title, price from table # SQL:select * from table # <QuerySet [{'title': '西游记', 'price': Decimal('111.22')}]> # res = models.Book.objects.filter(pk=1).values('title', 'price') # res1 = models.Book.objects.filter(pk=1).all() # print(res) # # print(res1) # for j in res: # print(j['title']) # res2 = models.Book.objects.filter(pk=1).values_list('title', 'price') # print(res2) # # for i in res2: # print(i[0]) # exclude # res = models.Book.objects.exclude(pk=1) # 排除数据 # print(res) # exist # res = models.Book.objects.filter(pk=111).exists() # print(res)
# 1. 查询书籍的价格大于200的 # sql:where price > 200 # orm: gt => greater than # res = models.Book.objects.filter(price__gt=200) # print(res) # 2. 查询书籍的价格小于等于200的 # sql: where price <= 200 less than equal # res = models.Book.objects.filter(price__lte=200) '''如何查看ORM执行的SQL语句?''' # 1. 如果返回结果是queryset对象的,都可以点query属性查看 # print(res) # print(res.query) # 3. 查询书籍价格是111, 222的 # sql: where price =111 or price =222 # sql: where price in (111, 222) # orm: # res = models.Book.objects.filter(price__in=[111, 222]) # print(res) # 4. 查询书籍价格在100-300之间的所有书籍 # sql: where price >= 100 and price <= 300 # sql: where price between 100 and 300 # orm: # res = models.Book.objects.filter(price__range=[100, 300]) # print(res) # 5. 查询书籍名称带有西的所有书籍 # sql: where title like '%西%' # orm: # res = models.Book.objects.filter(title__contains='西') # SQL:where title like '%西' # res = models.Book.objects.filter(title__startswith='西') # SQL:where title like '西%' # res = models.Book.objects.filter(title__endswith='西') # print(res) # 6. 查询书籍添加时间是2021-03月的 # res = models.Book.objects.filter(create_time__year=2021, create_time__month=3) res = models.Book.objects.filter(create_time__year=2021, create_time__month=3) print(res)
在配置文件中复制一下代码就可以了,操作orm的时候,下面会自己打印出来对应的sql语句 LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level': 'DEBUG', }, } }
外键字段的增删改查
1. 针对与一对多, 一对一 # 增 publish_obj = '' # 对象 create(publish_id=1) create(publish=publish_obj) # 改 update(publish_id=1) update(publish=publish_obj) 2. 针对多对多的 book_obj = Book.objects.filter(pk=1).first() # 增加 book_obj.authors.add(1, 2) book_obj.authors.add(author_obj, author_obj2); # 改 book_obj.authors.set([1, 2, 3]) book_obj.authors.set([author_obj, author_obj1, author_obj2]) # 删除 book_obj.authors.remove(1) # 清空 book_obj.authors.clear() '''针对多对多外键关系的增删改查,有4个方法:add, set, remove, clear, 以上4个方法只适用于自动创建的第三张表,其他情况不能用''' # 创建多对多关系的另外两种方式。
正反向的概念
1. 正向 2. 反向 ''' 外键字段在我手上,我查你就是正向 外键字段不在我手上,我查你就反向 ''' 举例: 1. book >>> publish >>> 正向 2. publish >>> book >>> 反向 3. author >>> book >>> 反向 4. book >>> author >>> 正向 # 在多表查询的时候,按照正反向查询的以上口诀 ''' 1. 正向查询按照字段(外键字段) 2. 反向查询按照表名小写或者表名小写_set.all() '''
# 1. 查询书籍主键为1的出版社名称 # 书查出版社 >>> 正向 =>>> 按字段 # book_obj = models.Book.objects.filter(pk=1).first() # print(book_obj.publish) # Publish object # print(book_obj.publish.title) # 2. 查询书籍主键为1的作者 # 书 >> 作者 >>> 正向 >>> 字段 # book_obj = models.Book.objects.filter(pk=1).first() # print(book_obj.authors) # app01.Author.None # print(book_obj.authors.all()) # <QuerySet [<Author: ly>]> # for i in book_obj.authors.all(): # print(i.name) # 3. 查询作者ly的手机号 # 作者 >>> 详情 >>> 正向 >>> 字段 # author_obj = models.Author.objects.filter(name='ly').first() # print(author_obj.author_detail) # print(author_obj.author_detail.phone) ''' 总结: 针对与正向查询,查询出的结果可能有多个的时候,就要使用字段名.all() 如果查询结果只有一个的情况,直接使用字段名即可。 ''' # 4. 查询东京出版社出版的所有书籍 # 出版社 >>> 书 >>> 反向 >>> 表名小写 publish_obj = models.Publish.objects.filter(title='东京出版社').first() print(publish_obj.book_set.all()) # 5. 查询作者ly写过的书 # 作者 >>> 书 >>> 反向 >>> 表名小写 author_obj = models.Author.objects.filter(name='ly').first() print(author_obj.book_set.all()) # 6. 查询手机号是120的作者的姓名 # 详情表 >>> 作者 >>> 反向 >>> 表名小写 author_detail = models.AuthorDetail.objects.filter(phone=120).first() print(author_detail.author) print(author_detail.author.name) ''' 总结: 针对于反向查询只要查询出来的结果可能有多个的时候,就要加_set.all() 如果查询出来的结果只有一个,就不用在加_set.all(),直接用表名小写 '''
'''基于双下换线跨表查询: 正反向的查询概念同样适用在这里''' # 1. 查询书籍主键为1的出版社名称,图书标题 # 书 》 出版本 》正向 》 字段 # res = models.Book.objects.filter(pk=1).values('title', 'publish__title') # print(res) # 2. 查询书籍主键为1的作者 # 书 作者 正向 字段 # res = models.Book.objects.filter(pk=1).values('title', 'authors__name') # print(res) # 3. 查询作者ly的手机号 # 作者 详情 正向 字段 # res = models.Author.objects.filter(name='ly').values('name', 'author_detail__phone') # print(res) # 4. 查询东京出版社出版的所有书籍 # 出版社 书 反向 表名小写 # res = models.Publish.objects.filter(title='东京出版社').values('title', 'book__title') # print(res) # 5. 查询作者ly写过的书 # 作者查书 反向 表名小写 # res = models.Author.objects.filter(name='ly').values('name', 'book__title') # print(res) # 6. 查询手机号是120的作者的姓名 # 详情查作者 反向 表名小写 # res = models.AuthorDetail.objects.filter(phone=120).values('phone', 'author__name') # print(res) # 7. 查询书籍主键为1的作者的手机号 # book author author_detail phone res = models.Book.objects.filter(pk=1).values('title', 'authors__author_detail__phone') print(res)
# F查询 '''F查询可以拿到数据表的字段原数据''' 1. 把书籍的价格在原来的基础上增加100元 sql: update book set price = price + 100; '''F查询''' from django.db.models import F # 1. 把书籍的价格在原来的基础上增加100元 # orm '''F默认是操作的数字''' # models.Book.objects.update(price=F('price')+100) # 2. 把所有书籍的标题后面都加上'好产品'三个字 # orm # models.Book.objects.update(title=F('title') + '好产品') from django.db.models.functions import Concat from django.db.models import Value models.Book.objects.update(title=Concat(F('title'), Value('好产品')))
# 1. 查询书籍价格大于300的或者标题为哈哈的书籍 '''Q查询''' # # 1. 查询书籍价格大于300的或者标题为哈哈的书籍 # and关系 # or的关系需要借助于 Q from django.db.models import Q # res = models.Book.objects.filter(Q(price__gt=300), Q(title='哈哈')) # Q包括起来之后,逗号连接还是and关系 # res = models.Book.objects.filter(Q(price__gt=300) | Q(title='哈哈')) # Q包括起来之后,逗号连接还是and关系 # res = models.Book.objects.filter(~Q(price__gt=300) | Q(title='哈哈')) # Q包括起来之后,~连接还是not关系 res = models.Book.objects.filter(~(Q(price__gt=300) | Q(title='哈哈'))) # Q包括起来之后,~连接还是not关系 print(res)
# max, min, sum, count, avg sql: select count(*) from table '''在django中使用聚合函数的时候,前提是要用关键字 aggregate''' # 查询书籍的最高价格 '''聚合查询''' # 查询书籍的最高价格 # orm from django.db.models import Max, Min, Sum, Count, Avg # res = models.Book.objects.aggregate(Max('price')) # print(res) # # res = models.Book.objects.aggregate(Min('price')) # print(res) res = models.Book.objects.aggregate(max_price=Max('price'), min_price=Min('price'), sum_price=Sum('price'), count=Count('id'), avg_price=Avg('price')) print(res)
# group by # sql_mode = 'only_full_group_by' 1. concat 2. concat_ws 3. group_concat '''在ORM中,分组查询需要使用关键字:annotate''' # 1. 统计每一本书的作者个数 '''分组查询''' # 1. 统计每一本书的作者个数 '''正反向查询同样适用''' # 书 作者 正向 字段 # res = models.Book.objects.annotate() # 只要遇到annotate,annotate前面没有values的情况下,就是按照models后面的表id进行分组 # res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num') # print(res) # 2. 统计出每个出版社的最便宜的书 # 出版社 书 反向 表名小写 # res = models.Publish.objects.annotate(min_price=Min('book__price')).values('title', 'min_price') # print(res) # 3. 统计不止一个作者的图书 # 统计 图书的作者个数,筛选出作者个数大于1的 # 3.1 查询每个图书的作者个数: 书查作者 正向 字段 # 3.2 过滤掉作者个数大于1的 # res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num') res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1).values('title', 'author_num') print(res)
事物
# 事务的四大特性和隔离级别 1. start transaction 2. rollback 3. commit try: '''执行的SQL语句''' with transaction.atomic(): # '''SQL1''' # '''SQL2''' pass # '''在这个代码块中的SQL语句,都是属于同一个事务''' except Exception as e: print(e) # 查询事务事务失败的原因 # 回滚事务 transaction.rollback()
# 如果你的字段存储的可能性能够列举完,一般采用choices参数 举例: 性别:男, 女, 其他 # 1 2 3 学历:小学,中学,大专,本科,硕士,博士,博士后,... # 1 2 3 4 5 6 7 8 9 来源:朋友圈,qq,广告,朋友介绍,... # 1 2 3 4 5 # 使用choices参数就可以实现 '''choices参数''' # gender int gender_choices = ( (1, '男'), (2, '女'), (3, '其他'), ) gender = models.IntegerField(choices=gender_choices, null=True) score_choices = ( ('A', '优秀'), ('B', '良好'), ('C', '一般'), ('D', '及格'), ) '''存储的数据类型该如何选择? 数据类型的选择按照第一个参数的数据类型''' score = models.CharField(max_length=16, choices=score_choices, null=True) res = models.Author.objects.filter(pk=4).first() # 对象 print(res) print(res.gender) '''如果你想获取数字对用的中文,固定句式:get_字段名_display()''' print(res.get_gender_display()) print(res.get_score_display())
unique 如果设置为unique=True 则该字段在此表中必须是唯一的 。 db_index 如果db_index=True 则代表着为此字段设置索引。 res.book_set.all() #> res.students.all() related_name 反向操作时,使用的字段名,用于代替原反向查询时的'表名_set'。 db_constraint 是否在数据库中创建外键约束,默认为True。 db_table 默认创建第三张表时,数据库中表的名称。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)