ORM即Object Relational Mapping,全称对象关系映射。
当我们需要对数据库进行操作时,势必需要通过连接数据、调用sql语句、执行sql语句等操作,ORM将数据库中的表,字段,行与我们面向对象编程的类及其方法,属性等一一对应,即将该部分操作封装起来,程序猿不需懂得sql语句即可完成对数据库的操作。
ORM优点:
易用,学习曲线短
和Django紧密集合,用Django时使用约定俗成的方法去操作数据库
缺点:
不好处理复杂的查询,强制开发者回到原生SQL
紧密和Django集成,使得在Django环境外很难使用
DATABASES = {
# sqlite3 默认的,不需要更改
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
# }
# mysql 数据库 需要注意的是数据库不会自动创建,需要手动先进行创建数据库,表可以自动创建
'default': {
'ENGINE': 'django.db.backends.mysql',
'HOST': "192.168.9.224",
"USER": "test",
"PASSWORD": "test",
"PORT": 3306,
"NAME":"day71"
}
}
'''
'NAME':要连接的数据库,连接前需要创建好
'USER':连接数据库的用户名
'PASSWORD':连接数据库的密码
'HOST':连接主机,默认本机
'PORT':端口 默认3306
'ATOMIC_REQUEST': True,
设置为True统一个http请求对应的所有sql都放在一个事务中执行(要么所有都成功,要么所有都失败)。
是全局性的配置, 如果要对某个http请求放水(然后自定义事务),可以用non_atomic_requests修饰器
'OPTIONS': {
"init_command": "SET storage_engine=INNODB",
}
设置创建表的存储引擎为MyISAM,INNODB
'''
# 工程中的 __init__.py中添加, 在django项目启动时默认启动pymysql连接数据引擎
import pymysql
pymysql.version_info = (1, 4, 13, "final", 0)
pymysql.install_as_MySQLdb()
# 这里会有一个错误 版本 django3.1.1 python3.7
django.core.exceptions.ImproperlyConfigured: mysqlclient 1.4.0 or newer is required; you have 0.10.1.
# 解决方案 添加:pymysql.version_info = (1, 4, 13, "final", 0)
# 可参考:https://blog.csdn.net/knight_zhou/article/details/108576312
创建模型
# models.py 创建模型
from django.db import models
# Create your models here.
class User(models.Model):
id = models.AutoField(primary_key=True)
user = models.CharField(max_length=30)
passwd = models.CharField(max_length=20)
数据迁移
>python manage.py makemigrations # 记录数据库的变化
Migrations for 'myapps':
myapps\migrations\0001_initial.p
- Create model Users
>python manage.py migrate # 将变化同步到数据库中
# python manage.py makemigrations 这一步只是将记录保存到了migratons文件下,并没有提交到数据库
# python manage.py migrate 将记录在migrations的信息提交到数据库中
# 查看是否有未提交的 python manage.py showimigrate
# app下的views.py
from django.shortcuts import render, HttpResponse
from myapps import models
def login2(request):
if request.method == "GET":
return render(request, "login2.html")
elif request.method == "POST":
user = request.POST.get("username")
pwd = request.POST.get("pwd")
user = models.Users.objects.filter(username=user, password=pwd).first()
# 如果有数据就返回登陆成功,否则就返回用户密码错误
return HttpResponse("登陆成功" if user else "用户或密码错误")
# 在添加一条路由
from myapps.views import *
urlpatterns = [
path('login2/', login2),
]
# 4步曲
1、创建app
2、创建models
3、在django中注册app
4、数据迁移
# twodjango> python manage.py startapp curddemo
# curddemo/models.py
from django.db import models
# Create your models here.
class Curds(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
password = models.CharField(max_length=16)
address = models.CharField(max_length=128)
# twodjango/settings.py 注册app应用
INSTALLED_APPS = [
...
'curddemo.apps.CurddemoConfig'
]
# 迁移数据 》 注意这里是在控制台下操作
# twodjango> python manage.py makemigrations
# twodjango> python manage.py migrate
# 基础导入: curddemo/views.py
from django.shortcuts import render, redirect
from curddemo import models
# urls.py
from curddemo import views
urlpatterns = [
path('c_index/', views.c_index),
path('c_del/', views.c_del),
path('c_addr/', views.c_addr),
path('c_update/', views.c_update),
]
def c_index(request):
# 产生的是一个questSet列表, 列表中包含N个对象
curd_list = models.Curds.objects.all()
return render(request, "curd/index.html", {"curd_list": curd_list})
# templates/curd/index.html
<link rel="stylesheet" href="/static/css/bootstrap.css">
<div class="col-md-6 col-md-offset-3">
<h5><a href="/c_addr/">添加用户</a></h5>
<table border="1">
<tr>
<th>id</th>
<th>name</th>
<th>password</th>
<th>address</th>
<td>操作</td>
</tr>
{% for curd in curd_list %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ curd.name }}</td>
<td>{{ curd.password }}</td>
<td>{{ curd.address }}</td>
<td>
<a href="/c_del/?id={{ curd.id }}">删除</a> |
<a href="/c_update/?id={{ curd.id }}">编辑</a>
</td>
</tr>
{% endfor %}
</table>
</div>
def c_del(request):
id = request.GET.get("id")
models.Curds.objects.filter(id=id).delete()
return redirect("/c_index/")
# 对应的html页面 删除
def c_addr(request):
if request.method == "GET":
return render(request, "curd/c_addr.html")
elif request.method == "POST":
name = request.POST.get("name")
pwd = request.POST.get("pwd")
addr = request.POST.get("addr")
res = models.Curds.objects.create(name=name, password=pwd, address=addr)
print(res)
return redirect("/c_index/")
# templates/curd/c_addr.html
<div class="col-md-7 col-md-offset-3">
<h3>添加页面</h3>
<form action="/c_addr/" method="post">
<p>用户: <input type="text" name="name" class="form-control"></p>
<p>密码: <input type="text" name="pwd" class="form-control"></p>
<p>地址: <input type="text" name="addr" class="form-control"></p>
<p><input type="submit" value="提交"></p>
</form>
</div>
def c_update(request):
if request.method == "GET":
id = request.GET.get("id")
get_info = models.Curds.objects.filter(id=id).first()
print(get_info)
return render(request, "curd/c_update.html", {"get_info": get_info})
elif request.method == "POST":
id = request.POST.get("id")
name = request.POST.get("name")
pwd = request.POST.get("pwd")
addr = request.POST.get("addr")
print(request.POST)
models.Curds.objects.filter(id=id).update(name=name,password=pwd, address=addr)
return redirect("/c_index/")
# templates/curd/c_update.html
<div class="col-md-5 col-md-offset-3">
<h3>修改页面</h3>
<form action="/c_update/" method="post">
<p><input type="text" name="id" class="hidden" value="{{ get_info.id }}"></p>
<p>用户: <input type="text" name="name" class="form-control" value="{{ get_info.name }}"></p>
<p>密码: <input type="text" name="pwd" class="form-control" value="{{ get_info.password }}"></p>
<p>地址: <input type="text" name="addr" class="form-control" value="{{ get_info.address }}"></p>
<p><input type="submit" value="提交"></p>
</form>
</div>
```python
1 orm 创建表,新增字段,修改,删除字段,不能创建数据库
-字段属性phone=models.CharField(max_length=64,null=True)
-null=True 代表该列可以为空
2 数据的增删改查
-增(1):User.objects.create(address='')
-增(2):实例化产生一个user对象,user对象的save方法
# 用户对象
-删:User.objects.filter(id=1,name='lqz').first().delete()
# queryset对象
-删:User.objects.filter(id=1,name='lqz').delete()
-改:User.objects.filter(id=1,name='lqz').update()
# querySet对象无法直接 .属性,需要是用户对象才能加.属性
-查:User.objects.all()
user=User.objects.filter(name='lqz')
user.name
```
只需要在settings.py文件中加上这几行, 控制台就能直接查看到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',
},
}
}
AutoField(Field)
- int自增列,必须填入参数 primary_key=True
BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True
SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767
IntegerField(Field)
- 整数列(有符号的) -2147483648 ~ 2147483647
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整数 0 ~ 2147483647
BigIntegerField(IntegerField):
- 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
自定义无符号整数字段
class UnsignedIntegerField(models.IntegerField):
def db_type(self, connection):
return 'integer UNSIGNED'
PS: 返回值为字段在数据库中的属性,Django字段默认的值为:
'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)',
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)
- 二进制类型
(1)null
如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False.
(2)blank
如果为True,该字段允许不填。默认为False。
要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。
如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。
(3)default
字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用。
(4)primary_key
如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,
Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,否则没必要设置任何一个字段的primary_key=True。
(5)unique
如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的
(6)choices
由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,<br>而且这个选择框的选项就是choices 中的选项。
related_name --> 基于对象反向查询,用于替换表名小写_set
related_query_name --> 基于双下划线反向查询,用于替换表名小写
IntegerField chiose 在页面中直接显示结束
<https://blog.csdn.net/orangleliu/article/details/40268093>
# 字段
chiose = ((1, "tcp"), (2, "udp"), (3, "icmp"))
protocol = models.IntegerField(choices=chiose, default=1)
# 获取语法 表.get_表字段_display() # mapping表名.get_proto字段_display
# 页面 {{ mapping.get_protocol_display }}
ForeignKey # 外键类型在ORM中用来表示外键关联关系,一般把ForeignKey字段设置在 '一对多'中'多'的一方。
ForeignKey可以和其他表做关联关系同时也可以和自身做关联关系。
to: 设置要关联的表
to_field: 设置要关联的表的字段
related_name: 反向操作时,使用的字段名,用于代替原反向查询时的'表名_set'。
related_query_name: 反向查询操作时,使用的连接前缀,用于替换表名。
on_delete: 当删除关联表中的数据时,当前表与其关联的行的行为。
models.CASCADE: 删除关联数据,与之关联也删除
models.DO_NOTHING: 删除关联数据,引发错误IntegrityError
models.PROTECT: 删除关联数据,引发错误ProtectedError
models.SET_NULL: 删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
models.SET_DEFAULT: 删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
models.SET: 删除关联数据,
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
db_constraint: 是否在数据库中创建外键约束,默认为True, # 实际环境中尽量少使用外键
# 索引及元数据
class UserInfo(models.Model):
nid = models.AutoField(primary_key=True)
username = models.CharField(max_length=32)
class Meta:
# 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
db_table = "table_name"
# 联合索引
index_together = [
("pub_date", "deadline"),
]
# 联合唯一索引
unique_together = (("driver", "restaurant"),)
# admin中显示的表名称
verbose_name
# verbose_name加s
verbose_name_plural
创建一个test.py文件, 直接操作模型用于测试
import os
import django
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day76.settings')
django.setup()
from app01 import models
.........
一般用于数据库优化
only: 只取指定的字段,返回值是queryset里套对象,对象只有指定的字段
defer: 不取指定的字段,返回值是queryset里套对象,对象只有指定的字段
# 正常操作,打印都是querySet对象, 可以重修改模型 增加 __str__返回结果
ret = models.picture.objects.filter(name='linux')
print(ret) # ]>
# 模型中增加这两行
def __str__(self):
return "name: {0} price: {1}".format(self.name, self.price)
# 返回的结果就是 ]>
# 增 方法1
models.Book.objects.create(name="nginx架构解析",price=22.22, publish="上海出版")
# 增 方法2
name = models.Book(name="python3标准库", price=33.33, publish="机械工业出版")
name.save()
# 通过循环,添加多条记录用于测试
for i in range(1, 10):
prices = random.randrange(11, 99)
x = models.Book(name="test-{}".format(i), price="{0}".format(prices), publish="北京出版")
x.save()
# 改 方法1 : 如果有多个相同的名称,那么修改的就会是全部,没有加where条件
models.Book.objects.filter(name="test-7").update(publish="北京出版")
# 改 方法2
t8 = models.Book.objects.filter(name="test-8").first()
t8.publish = "南京出版"
t8.save()
# 删除, 最好是有多个条件,如果只是test-8那么全部的都会被删除
models.Book.objects.filter(name="test-8", id=12).delete()
查询API
all(): 查询所有结果
filter(**kwargs): 它包含了与所给筛选条件相匹配的对象
get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象
order_by(*field): 对查询结果排序('-id')
reverse(): 对查询结果反向排序
count(): 返回数据库中匹配查询(QuerySet)的对象数量。
first(): 返回第一条记录
last(): 返回最后一条记录
exists(): 如果QuerySet包含数据,就返回True,否则返回False
values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列
model的实例化对象,而是一个可迭代的字典序列
values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
distinct(): 从返回结果中剔除重复纪录
查询具体
# querySet对象,直接调用 值.query能打印出对应sql语句。
# 默认打印就是一个query对象,如果想直接看结果,需要在模型上 定义 def __str__(self) 方法
# 查询 all() queryset对象 ,]
print(models.Book.objects.all())
# filter()
f3 = models.Book.objects.filter(name="python3标准库")
# print(f3.query) # WHERE `app01_book`.`name` = python3标准库
f4 = models.Book.objects.filter(name="test-4", id=10) # 需要同时满足,没有就是一个[]
# 相当于是 WHERE (`app01_book`.`id` = 10 AND `app01_book`.`name` = test - 4)
# get 返回筛选的结果,只能是一个,如果有多个会直接raise, 最好用来查ID之类的数据
t7 = models.Book.objects.get(name="test-7")
print(t7) # 如果有>1个结果会直接报错, object
# print(t7.query) # 注意 object没有 query属性
# exclude 不要当前查找的,其它全部显示
t8= models.Book.objects.exclude(name="test-8")
print(t8) # 返回不满足的全部条件
print(t8.query) # WHERE NOT (`app01_book`.`name` = test - 8)
# order_by 查询结果排序 对价格进行降序
t9 = models.Book.objects.order_by("price") # 直接以价格为排序
print(t9.query) # ORDER BY `app01_book`.`price` ASC
t10 = models.Book.objects.order_by("-price") # 倒序, 价格高的在前
print(t10.query) # ORDER BY `app01_book`.`price` DESC
# reverse 感觉没啥用
# ret = models.picture.objects.all().reverse()
# print(ret)
# count 统计有多少行
# ret = models.picture.objects.all().filter(name="python").count()
# print(ret)
# first 取第一个值 last 取最后一个值
# ret = models.picture.objects.first()
# print(ret)
# ret = models.picture.objects.last()
# print(ret)
# exists 如果querySet存在就返回True, 否则就是False
# ret = models.picture.objects.filter(name="xiong").exists()
# print(ret) # 随便写一个不存在的返回就是 False, 这个很有用 ******
# values 返回一个特殊的querySet字典
# ret = models.picture.objects.all().values()
# print(ret)
# < QuerySet[{'id': 1, 'name': 'python', 'price': Decimal('22.22'), 'author': 'xiong',
# 'create_date': datetime.date(2019, 4, 3)},] >
# values_list 返回的与values差不多, 只不过它这个是一个列表加元组
# ret = models.picture.objects.all().values_list()
# print(ret)
# < QuerySet[(1, 'python', Decimal('22.22'), 'xiong', datetime.date(2019, 4, 3)),(....)]>
取范围 in\range
# in 10,50 那就是取这两个值,而不是取一个范围
m1 = Book.objects.filter(price__in=[10,50])
print(m1.query) # WHERE `app01_book`.`price` IN (10, 50)
# range 这个就是取范围了, 从10到100之内的数
m2 = Book.objects.filter(price__range=[10,50])
print(m2.query) # WHERE `app01_book`.`price` BETWEEN 10 AND 50
比大小
# 大于 gt 小于 lt 大于等于 gte 小于等于 lte
m3 = Book.objects.filter(price__gt=20.22)
print(m3.query) # WHERE `app01_book`.`price` > 20.2200
m4 = Book.objects.filter(price__lt=80)
print(m4.query) # WHERE `app01_book`.`price` < 80
m5 = Book.objects.filter(price__gte=50)
print(m5.query) # WHERE `app01_book`.`price` >= 50
m6 = Book.objects.filter(price__lte=50)
print(m6.query) # WHERE `app01_book`.`price` <= 50
# name__endswith 以9结尾的字符串
m9 = Book.objects.filter(name__endswith="9")
print(m9.query) # WHERE `app01_book`.`name` LIKE BINARY %9
# name__startswith 以linux开头的字符串
m10 = Book.objects.filter(name__startswith="linux")
print(m10.query) # WHERE `app01_book`.`name` LIKE BINARY linux%
# name__contains 包含 test的字符串
m7 = Book.objects.filter(name__contains="test")
print(m7.query) # WHERE `app01_book`.`name` LIKE BINARY %test%
# name__icontains 不区分大小写
m8 = Book.objects.filter(name__icontains="test")
print(m8.query) # WHERE `app01_book`.`name` LIKE %test%
class Book(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=64)
price = models.DecimalField(max_digits=6, decimal_places=2)
publish = models.CharField(max_length=64)
create_date = models.DateField(auto_now_add=True)
def __str__(self):
return "名称: {} 价格:{} 出版:{}".format(self.name, self.price, self.publish)
# 创建10条, 出版社后续手动在改
for i in range(1, 10):
prices = random.randrange(11, 99)
x = Book(name="test-{}".format(i), price="{0}".format(prices), publish="北京出版")
x.save()
# 1 查询出版社出版过的价格大于200的书籍
print(Book.objects.filter(price__gt=200))
# 2 查询2021年1月出版的所有以py开头的书籍名称
p1 = Book.objects.filter(create_date__year=2021, create_date__month=1, name__startswith="py")
print(p1)
print(p1.query)
# 3 查询价格为50,100或者150的所有书籍名称及其出版社名称
p2 = Book.objects.filter(price__in=[50,100,150]).values("name", "publish")
print(p2)
# 4 查询价格在100到200之间的所有书籍名称及其价格
p3 = Book.objects.filter(price__range=[100,200])
print(p3)
# 5 查询所有北京出版出版社的书籍的价格(从高到低排序,去重)
p4 = Book.objects.filter(publish="北京出版").order_by("name").distinct()
print(p4)
# 查找所有书名里包含python的书
ret = models.Book.objects.filter(name__startswith='python').values()
# 查找价格大于20元的书
ret = models.Book.objects.filter(price__gt='20').values()
# 查找价格小于10元的书名和价格, 以及出版社
ret = models.Book.objects.filter(price__lt='15').values('name','price','publish__addr')
# 查找在北京的出版社以及查看出版过的书
ret = models.Publish.objects.filter(name='北京').values('book__name')
ret = models.Book.objects.filter(publish__name='北京').values('name')
# 查找名字以上海开头的出版社
ret = models.Publish.objects.filter(addr__startswith='上海')
# 查找作者名字里面带“t”字的作者
ret = models.Author.objects.filter(name__startswith='t').values()
# 查找姓名是男的作者
ret = models.Author.objects.filter(author__sex=1).values('name')
# 查找手机号是135开头的作者
ret = models.Author.objects.filter(author__phone__startswith='135').values()
ret = models.Authordetail.objects.filter(phone__startswith='135').values()
# 查找手机号是135开头的作者的姓名
ret = models.Author.objects.filter(author__phone__startswith='135').values('name')
# 查找书名是“pyton”的书的出版社
ret = models.Book.objects.filter(name='python').values('publish__addr')
# 查找书名是“pyton”的书的所有作者
ret = models.Book.objects.filter(name__startswith='pyth').values('author__name')
# 查找书名是“pyton”的书的作者的姓名
ret = models.Book.objects.filter(name__startswith='pytho').values('author__author__sex')
# 查找书名是“pyton”的书的作者的手机号码
ret = models.Author.objects.filter(book__name='python').values('author__phone')
from django.db import models
class Publish(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=128)
address = models.CharField(max_length=128)
# 一对多的关系要建立在多的一方
class Book(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=128)
price = models.DecimalField(max_digits=7, decimal_places=2)
publish = models.ForeignKey(to="Publish", to_field="id", on_delete=models.CASCADE)
auther = models.ManyToManyField(to="Auther")
class Auther(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=128)
address = models.CharField(max_length=128)
autherdetail = models.OneToOneField(to="Autherdetail", on_delete=models.CASCADE, to_field="id")
class Autherdetail(models.Model):
id = models.AutoField(primary_key=True)
phone = models.IntegerField(max_length=11)
email = models.EmailField()
# 当makemigrations之后, 会自动生成五张表
multipath_auther
multipath_autherdetail
multipath_book
multipath_book_auther
multipath_publish
# on_delete选项
models.CASCADE # 级联删除, 1.X默认是级联删除
models.SET_NULL # 设制为空
models.DO_NOTHING # 什么都不做
一对一
# to="表名" 加引号,这个表能找着就行, 不加引号就需要定义在OneToOnefield之前
# 会自带唯一性约束, 如果没有id会自动加上pid
models.OneToOneField(to="Autherdetail", on_delete=models.CASCADE, to_field="id")
一对多
# 需要注意的是: 一旦确认一对多的关系,建立连接需要在多的一方
models.ForeignKey(to="Publish", to_field="id", on_delete=models.CASCADE)
多对多
# MaryToMaryField会自动创建第三张表, 多对多会自动创建第三张表
models.ManyToManyField(to="Auther")
pk=number, pk是直接查询的主键
一对一 添加 修改 与 一对多 的添加 添加 删除 一样
添加
# 一对一,需要先添加 详细页表 然后在关连
Autherdetail.objects.create(phone=1338888888, email="[email protected]")
Autherdetail.objects.create(phone=1348888999, email="[email protected]")
Autherdetail.objects.create(phone=1341111333, email="[email protected]")
# 添加方式一
Auther.objects.create(name="小黑", address="上海", autherdetail_id=1)
# 添加方式二, 需要注意的是 a1需要是一个object对象,而不是一个queryset对象
a1 = Autherdetail.objects.filter(pk=2).first()
Auther.objects.create(name="小红", address="南京", autherdetail=a1)
修改
# 一对一 不能重复 Duplicate entry
# 方法一, 需要注意的是添加一定是object对象,而不是queryset对象
Auther.objects.filter(name="小红").update(autherdetail_id=3)
# 方法二
a2 = Autherdetail.objects.filter(pk=2).first()
Auther.objects.filter(name="小红").update(autherdetail=a2)
# 方法三, 通过对象来修改
a3 = Autherdetail.objects.filter(pk=3).first()
au1 = Auther.objects.get(pk=1)
au1.autherdetail = a3
au1.save()
删除
# 直接删除 并不会删除关连表的数据
# 方法一: 通过queryset对象的 可以直接.下去
Auther.objects.filter(pk=1).delete()
# 方法二: object对象,通过.delete属性删除
au2 = Auther.objects.get(pk=3)
au2.delete()
# 如果直接删除关连的字段,那么作者表的关连关系,通过on_delete=models.CASCADE也会被直接删除,慎用
Autherdetail.objects.filter(pk=3).delete() # 级联删除
添加
1、 先创建一条记录,
2、 找着对应要关连的字段
3、 将它们关连起来
# 添加多表,需要先将数据的对象取出来, 然后在进行关连
b1 = Book.objects.filter(name="持续交付").first()
b2 = Book.objects.filter(name="python标准库").first()
a1 = Auther.objects.get(pk=2) # 严格注意,一定取的是object 不是queryset
a2 = Auther.objects.get(pk=4)
a3 = Auther.objects.get(pk=5)
# 添加单条记录
b1.auther.add(a3)
# 将表与表的字段进行关连
b1.auther.add(a1,a2)
b2.auther.add(a1,a2)
删除
# 没有修改,需要先删除,然后在添加,
# 删除多条是remove 可以是ID也可以是对象, 可以传多个,但不要混着用
# 删除单个或多条记录
# b1.auther.remove(Auther.objects.get(pk=5))
# b1.auther.remove(2,4)
# 会将b1的数据全部清空, 这个慎用!
b1.auther.clear()
# 先清空 b1的数据,然后在添加, 这要注意, 传的是一个可迭代对象 列表元组之类的
b1.auther.set([a3,])
1、关连自段在哪个表, 由这个表开始查询的就是正向查询 比如A 关连B ,由A开始查询, A就是正向查询 2、基于对象的查询是 子查询 inner join
一对一
# 表示例, 正反向查询
class Auther(models.Model):
autherdetail = models.OneToOneField(to="Autherdetail", on_delete=models.CASCADE, to_field="id")
class Autherdetail(models.Model):
# 正向查询: Auther --> 关连Auther --> Autherdetail --> 正向查询按字段
# 反向查询: Autherdetail --> Auther --> 反向查询按表名小写
示例
# 正向 根据名称 查找邮箱, 一对一
# 注意,filter查找出来的是queryset对象,而继续跨表查询 需要的是一个 object
email = Auther.objects.filter(name="小红").first().autherdetail.email
print(email)
# 反向查询 根据 邮箱 查找 对应的作者信息, 获取的都是对象 object
a1 = Autherdetail.objects.get(pk=2).auther
print(a1.name)
一对多
# 一对多的关系要建立在多的一方, 外键关系的建立是为了不写脏数据,数据重复建立不起来
class Book(models.Model):
publish = models.ForeignKey(to="Publish", to_field="id", on_delete=models.CASCADE)
class Publish(models.Model):
# 正向查询: Book --> 关连Publish --> Publish --> 正向查询按字段
# 反向查询: Publish --> Book --> 反向查询按表名小写_set.all()
示例
# 正向 根据 书名 查找出版社 一对多
p1 = Book.objects.filter(name="持续交付").first().publish.name
# print(p1.name) # 如果没有 publish.name 就p1.name
# 反向 根据 出版社 查找对应的书藉, 而单表查的时候 需要是一个 object
# 注意: 反向是 book_set, 不管是多条还是单条都是 .all()
# 最后加上.values('字段') 可直接查看列表
p2 = Publish.objects.filter(name="人民邮电出版社").first().book_set.all()
print(p2) # , ]>
print([p.name for p in p2]) # 直接通过列表生成式打印
多对多
class Book(models.Model):
auther = models.ManyToManyField(to="Auther")
class Auther(models.Model):
# 正向查询: Book --> 关连Auther --> Auther --> 正向查询按字段
# 反向查询: Auther --> Book --> 反向查询按表名小写_set.all()
示例
# 正向, 根据 书名查找 对应的作者 多对多
b1 = Book.objects.filter(name="持续交付").first().auther.all()
# 查找出所有的 作者信息,一个queryset对象]>
print(b1)
# 反向 根据作者 查找出对应的书
b2 = Auther.objects.filter(name="小红").first().book_set.all()
print([b.name for b in b2])
# 继续查找出 出版社, 根据书查出版社就是 正向查询
print([b.publish.name for b in b2])
基于双下划线的查询是 连表查询
一对一
# 以小红为例, name=小红 pk=2
# 以 auther为基表, 正向查询, 直接字段名称
a1 = Auther.objects.filter(name="小红").values("autherdetail__phone")
print(a1) #
# 以autherdetail为基表,反向查询,按表名小写
a2 = Autherdetail.objects.filter(auther__id=2).values("phone")
a3 = Autherdetail.objects.filter(phone="1348888999").values("auther__name")
<QuerySet [{'phone': 1348888999}]>
<QuerySet [{'auther__name': '小红'}]>
# 示例 查找 电话是 1348888999 的作者名称,出版的书以及出版社
# auther__book__publish__name, 只要有 就直接双下划线往下加
a4 = Autherdetail.objects.filter(phone="1348888999").values("auther__name", "auther__book__name", "auther__book__publish__name")
一对多
# 正向 查找 持续交付书的 出版社
b1 = Book.objects.filter(name="持续交付").values("publish__name")
# 反向
b2 = Publish.objects.filter(book__name="持续交付").values("name")
# 基结果都是queryset对象
<QuerySet [{'publish__name': '人民邮电出版社'}]>
<QuerySet [{'name': '人民邮电出版社'}]>
# 查询 人民邮电出版社 出版过的所有书籍的名字与价格
b3 = Publish.objects.filter(name="人民邮电出版社").values("book__name","book__price")
b4 = Book.objects.filter(publish__name="人民邮电出版社").values("name", "price")
多对多
# 查找出版社是 人民邮电出版社 出版的作者姓名,邮件,地址
b5 = Publish.objects.filter(name="人民邮电出版社").values("book__auther__name", "book__auther__autherdetail__email", "book__auther__address")
# 查询 小红 出过的所有书籍的名字, 以及对应的出版社
b6 = Auther.objects.filter(name="小红").values("book__name", "book__publish__name")
# 查看手机号码134开头的作者, 书以及出版社
b7 = Autherdetail.objects.filter(phone__startswith="134").values("auther__book__name", "auther__book__publish__name")
aggregate()
是QuerySet
的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。
# 语法 queryset.aggregate(这里加函数), 打印的结果是字典
# 先导入方法
from django.db.models import Avg, Sum, Count, Max, Min
# 统计作者总数 {'c': 3}
x1 = Auther.objects.aggregate(c=Count("name"))
# 统计所有图书的价格 {'c': Decimal('276.54')}
x2 = Book.objects.aggregate(c=Sum("price"))
# 统计所有图书的平均,最高,最低价格 {'a': Decimal('92.'), 'max': Decimal('99.11'), 'min': Decimal('87.59')}
x3 = Book.objects.aggregate(a=Avg("price"), max=Max("price"), min=Min("price"))
# 重要说明:
# group by 谁, 就以谁做基表,filter过滤, annotate取分组, values取值
# values在前,表示group by, 在后表示取值, 不加默认是values("pk")
# filter在前,表示where条件, 在后表示having
# annotate()为调用的`QuerySet`中每一个对象都生成一个独立的统计值(统计方法用聚合函数)。
# 总结 :跨表分组查询本质就是将关联表join成一张表,再按单表的思路进行分组查询。
示例
# 统计作者总数 {'c': 3}
x1 = Auther.objects.aggregate(c=Count("name"))
# 统计所有图书的价格 {'c': Decimal('276.54')}
# 全部就是 Book.objects.all().values("pk").aggregate(c=Sum("price")) 先values 在group by
x2 = Book.objects.aggregate(c=Sum("price"))
# 统计所有图书的平均,最高,最低价格 {'a': Decimal('92.'), 'max': Decimal('99.11'), 'min': Decimal('87.59')}
x3 = Book.objects.aggregate(a=Avg("price"), max=Max("price"), min=Min("price"))
# 统计每一本书作者个数
x4 = Book.objects.all().annotate(c=Count("auther__name")).values("name", "c")
# 如果以作者为group by 那就是以作者为分组, 打印的结果就是每本书的总数
x5 = Auther.objects.all().annotate(c=Count("book__name")).values("book__name", "c")
# 统计每一个出版社的最便宜的书
x6 = Publish.objects.all().annotate(min=Min("book__price")).values("name", "min")
# 统计每一本以py开头的书籍的作者个数
x7 = Book.objects.filter(name__startswith="py").annotate(c=Count("auther__name")).values("name", "c")
# 统计不止一个作者的图书:(作者数量大于一)
x8 = Auther.objects.annotate(c=Count("book__name")).filter(c__gt=1).values("name","c")
# 根据一本图书作者数量的多少对查询集 QuerySet进行排序
x9 = Auther.objects.annotate(c=Count("book__name")).order_by("name").values("name","c")
# 查询各个作者出的书的总价格并排序
x10 = Auther.objects.annotate(csum=Sum("book__price")).order_by("-csum").values("name", "csum")
# 查询每个出版社的名称和书籍个数
x11 = Publish.objects.annotate(pbook=Count("book__name")).values("name", "pbook")
from django.db.models import F, Q
F: F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值
# 阅读数大于评论数
b1 = Book.objects.filter(read_num__gt=F("commit_num")).values("name")
# SELECT `name` FROM `multipath_book` WHERE `read_num` > `commit_num`
# 全部阅读数加50
b2 = Book.objects.all().update(read_num=F("read_num")+50)
# UPDATE `multipath_book` SET `read_num` = (`multipath_book`.`read_num` + 50);
Q: 为了表示与 & ,或 |,非 ~
# 与&: 两个关系要同时满足 比如 5< 3 > 1 5大于3 并且 3大小1
# 或|: 两者满足一个即可 (1>2) | (2<3) 一个false,一个true
# 非~: 取反
# 取出价格大小80并且小于90的书
b3 = Book.objects.filter(Q(price__gt="80")&Q( price__lt="90")).values("name")
from django.db.models.functions import TruncMonth
# 按时间归档, 先切割时间 TruncMonth,分组之后 在进行统计
time_count = models.Article.objects.filter(blog=blog).annotate(t_m=TruncMonth('create_time')).values('t_m').annotate(
count_t_m=Count('t_m')).values('t_m', 'count_t_m').order_by('-t_m')
10 = Auther.objects.annotate(csum=Sum(“book__price”)).order_by("-csum").values(“name”, “csum”)
x11 = Publish.objects.annotate(pbook=Count("book__name")).values("name", "pbook")
### 5.5、F\Q
> from django.db.models import F, Q
- F: F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值
```python
# 阅读数大于评论数
b1 = Book.objects.filter(read_num__gt=F("commit_num")).values("name")
# SELECT `name` FROM `multipath_book` WHERE `read_num` > `commit_num`
# 全部阅读数加50
b2 = Book.objects.all().update(read_num=F("read_num")+50)
# UPDATE `multipath_book` SET `read_num` = (`multipath_book`.`read_num` + 50);
Q: 为了表示与 & ,或 |,非 ~
# 与&: 两个关系要同时满足 比如 5< 3 > 1 5大于3 并且 3大小1
# 或|: 两者满足一个即可 (1>2) | (2<3) 一个false,一个true
# 非~: 取反
# 取出价格大小80并且小于90的书
b3 = Book.objects.filter(Q(price__gt="80")&Q( price__lt="90")).values("name")
from django.db.models.functions import TruncMonth
# 按时间归档, 先切割时间 TruncMonth,分组之后 在进行统计
time_count = models.Article.objects.filter(blog=blog).annotate(t_m=TruncMonth('create_time')).values('t_m').annotate(
count_t_m=Count('t_m')).values('t_m', 'count_t_m').order_by('-t_m')