归纳总结的笔记:
day67 ORM 特殊的语法
一个简单的语法 --翻译成--> SQL语句 语法:
1. 操作数据库表 创建表、删除表、修改表 2. 操作数据库行 增、删、改、查 怎么连数据库:
需要手动创建数据库
手写一段代码
告诉Django连哪个数据库
告诉Django用pymysql代替默认的MySQLdb
发命令:
python manage.py makemigrations
python manage.py migrate 总结详细步骤:
1. 手动创建数据库
2. 在app/models.py里面写上一个类,必须继承models.Model这个类 (注意启动Django项目)
3. 在Django 项目的settings.py 里面 配置上数据库的相关信息
4. 在Django项目里的__init__.py里面写上 两句话 import pymysql pymysql.install_as_MySQLdb()
5. 给Django发布命令
1. python manage.py makemigrations # 相当于去你的models.py 里面看一下有没有改动
2. python manage.py migrate # 把改动翻译成SQL语句,然后去数据库执行 models.py --> Django翻译成SQL --> pymysql --> MySQL(4p) 哪吒老师ORM思想:
五步四部分 目前为止学到的特殊语法之操作数据表部分:
1. 通过建一个类(继承models.Model) --> 相当于创建了一个数据库中的表 --> 类 - 数据表
2. 字段:
1. models.AutoField(primary_key=True) --> int 自增的 主键
2. models.CharField(max_length=32, null=True) --> varchar(32)
3. models.IntegerField() # int 总结一下:
models.py MySQL
类 对应 数据表
类的属性 对应 数据库里面的字段(列)
对象 对应 一行数据(一条数据) ORM到底是什么? 对象关系映射 数据行的操作:
增:两种
1. models.User.objects.create(**{}) 直接创建并且提交到数据库
2. user = models.User(**{}) user.save() 删:
找到对象删除
models.User.objects.filter().delete() 改:
更新 两种
1. models.User.objects.filter().update(username="一枝花")
2. user = models.User.objects.get(id=1) user.username="梁志华" user.save() 查:
models.User.objects.all()
models.User.objects.get(条件) 不满足条件就报错|返回的是一个对象
models.User.objects.filter(条件) 不满足条件不报错,但是返回的结果是一个对象的列表
models.User.objects.filter().first()
models.User.objects.filter().last() models.User.objects.exclude(条件)
学生和班级的增删改查前后端交互,views后端函数使用orm实现的功能:
from django.shortcuts import render, HttpResponse, redirect
from . import models
from django.urls import reverse
# Create your views here. def users(request):
# 查出所有的user数据
user_list = models.User.objects.all() # 是一个user对象的列表
# print(user_list[0].id,user_list[0].name,user_list[0].age)
# try:
# user0 = models.User.objects.first() # 这里是得到一个对象
# print(user0.name)
# except Exception:
# print("wronge")
# try:
# user1 = models.User.objects.filter(name='alex') # 尽管查询的是一条数据也是一个列表
# print(user1[0].age)
# print(user1[0].name)
# print(user1[0].id)
# except Exception:
# print("wronge") user1 = models.User.objects.filter(name='alex', age=18)
# print(user1[0].name)
arg_dict = {"name": "alex", "age": 18}
user2 = models.User.objects.filter(**arg_dict)
# print(user2[0].age)
# user3 = models.User.objects.get(id=8) # 使用get的时候如果查不到这个数据会报错,连页面都不能查出来
# print(user3.name)
# user4 = models.User.objects.filter(id=8)
# if len(user4)>0:
# print(user4[0].name)
# else:
# print('nothing') user5 = models.User.objects.exclude(name='rock')
# print(user5[3].name) # 这里的exclude是不包含的意思,只要数据结果不包含我所设定的条件即可
# return HttpResponse("ok") # 创建记录
# 第一种生成数据并且提交到数据库
# new_user = models.User.objects.create(name='peter',age=30,)
# print(new_user.name,new_user.age) # 这样刷新页面就可以直接提交了
# 第二种方式 只是创建了,并没有项数据库提交
new_u = models.User(name='lin', age=90) # 这里只是把数据库创建了,并不会执行提交操作
# new_u.save() # 这里才是把数据提交到数据库里面 # 删除数据,根据条件去删除
# models.User.objects.filter(age=30).delete() # 改数据,第一种方式先根据条件去找到该数据才能对其进行修改
# models.User.objects.filter(age=18).update(name='sb')
# 改数据的第二种方式,
userl = models.User.objects.filter(age=18).first()
userl.name = '一坨粪'
# 这里是提价一下的意思,我们如果不在这里写上这一句的话我
# 们是无法保存数据的,也就是我们的数据不会提交给数据库做更改
userl.save() return render(request, "apptheone/user.html", {"user_li": user_list}) # 展示学生
def show_stu(request):
stul = models.Pupil.objects.all()
# print(stul[0].id, stul[0].sname, stul[0].age)
return render(request, 'hm_weekend/stu.html/', {"stu_li": stul}) # 添加学生
def add_stu(request):
if request.method == 'POST':
stu_name = request.POST.get("sname")
stu_age = request.POST.get("age")
models.Pupil.objects.create\
(
sname=stu_name,
age=stu_age
)
return redirect(reverse('stuli')) else: # 这里是把我们的学生的选课内容加到页面上去,暂时还没有讲,还不会做
# new_p2 = models.Course.objects.all()
# return render(request, 'hm_weekend/add_student.html', {'class_list': new_p2})
return render(request, 'hm_weekend/add_student.html') # 编辑学生
def edit_stu(request):
if request.method == 'POST':
st_id = request.POST.get("id")
st_age = request.POST.get("age")
st_name = request.POST.get("sname")
models.Pupil.objects.filter(id=st_id).update(age=st_age, sname=st_name) return redirect(reverse('stuli')) student_id = request.GET.get("student_id")
print(student_id)
# rl = models.Pupil.objects.get(id=student_id)
rl = models.Pupil.objects.filter(id=student_id)
r2 = rl[0]
# sli = models.Course.objects.all()
# return render(request, 'hm_weekend/edit_student.html', {"class_form": sli})
return render(request, 'hm_weekend/edit_student.html', {"student": r2})
# return render(request, 'hm_weekend/edit_student.html', {"student": rl[0]}) # 删除学生
def delete_stu(request):
sid = request.GET.get("student_id")
# print(sid)
models.Pupil.objects.filter(id=sid).delete()
return redirect(reverse('stuli')) # 展示课程
def course(request):
couli = models.Course.objects.all()
return render(request, 'hm_weekend/course_list.html', {"cou_li": couli}) # 删除课程
def del_cou(request):
c_id = request.GET.get("class_id")
models.Course.objects.filter(cid=c_id).delete()
return redirect(reverse("couli")) # 编辑课程
def edit_cou(request):
if request.method == 'POST':
cou_id = request.POST.get("cid")
cou_name = request.POST.get("cname")
models.Course.objects.filter(cid=cou_id).update(cname=cou_name)
return redirect(reverse('couli')) course_id = request.GET.get("class_id")
ret = models.Course.objects.filter(cid=course_id)
# re2 = ret[0]
# ret = models.Course.objects.get(cid=course_id) # 使用get方法直接就可以得到一个对象,直接放到我们下面的键对值里面
# return render(request, 'hm_weekend/edit_course.html', {'class': ret})
return render(request, 'hm_weekend/edit_course.html', {'class': ret[0]}) # {"class": re2} # 添加课程
def add_cou(request):
if request.method == "POST":
cou_name = request.POST.get("cname") # 这里的get后面的内容是我们的数据库里面的字段名cname
models.Course.objects.create(cname=cou_name)
return redirect(reverse("couli"))
else:
return render(request, 'hm_weekend/add_course.html')
Django之ORM
O-->object
R--->relational
M--->mapping
orm介绍
orm概念
对象映射关系模式是一种为了解决面相对象与关系数据库存在的互不匹配的现象的技术
,简单来说orm是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动化持久关旭数据库中.orm在业务逻辑层和数据层之间充当了桥梁的作用
我们的数据库操作相对比较复杂,我们在django框架的基础上,使用的这个orm就可以在不使用数据库的基础上,对数据库里面的信息进行操作,执行起来更加的简单,便捷,
orm由来
我们的所有软件开发过程中都是会涉及到对象和关系数据库在用户层面和业务逻辑层面我们是面相对象的,当对象的信息发生变化的时候我们需要把对象的信息保存到关系数据库中
按照之前的方法来进行开发就会出现程序员自己的业务逻辑代码中夹杂着很多的sql语句来增加,读取,修改和删除相关数据,而这些代码通常都是重复的,
orm的优势
它主要问题是解决关系的映射,它通常把一个类和一个表一一对应
类的每个实例对应表中的一条数据,类的每个属性对应表中的每个字段
orm提供了对数据库的映射,不用直接编写sql语句,只需要像操作对象一样从数据库里操作数据,让发开发人员专注于开发自己的业务逻辑处理,提高了开发效率
orm的劣势
orm的缺点是会在一定程度上牺牲程序员的执行效率,而且用得多了以后我们就会忘记sql语句如何写了,关系数据库的相关技能会退化...所有的自动化机制都是这样的,把人力从繁杂的劳动中解放出来然后相关的技能呢就会退化,就会更加的依赖于机器,或者更加便捷的程序,就像我们的机器人总动员一样,剧中最后船长甚至没有自由行走的能力都是因为过度依赖于过分便捷的机器导致的.
orm总结,
orm只是一种工具,工具确实能够解决一些重复简单的劳动,这是不可否认的.
但我们不能指望某个工具一劳永逸地解决所有问题,一些特殊问题还是需要特殊处理的,
但是在整个软件开发过程中需要特殊处理的情况应该都是很少的,否则所谓的工具也就失去了它存在的意义.
model
model是django中数据的单一明确的信息来源,它包含了你所存储的数据的重要字段,和行为,通常一个模型model映射到一个数据表格
基本情况:
每个模型都是一个python类,它是django.db.model的子类
模型的每个属性都代表了一个数据库字段
综上所述,django为您提供了一个自动生成的数据库访问api,
下面我们来举例
from django.db import models
class Person(models.Model):
first_name=models.CharField(max_length=30)
last_name=models.CharField(max_length=20)
我们的数据库是需要自己手动去到命令行里面或者其他的mysql相关的软件里面去建立的,然后我们就可以使用orm工具来操作我们的数据库了,上面定义的class类即是一个数据库表格,表格的名字就是类名
first_name就是字段名,后面赋值的就是字段的类型和长度,charfield是char类型,models是orm工具的关键字,包括我们的类所继承的models.Model也是固定写法,orm的语法要求就是这样的,
我们的字段名在这个类里面就是一个类属性,每个类属性都映射到一个数据库的列
把上面的语句翻译成sql语句即是这样的:
CREATE TABLE myapp_person (
"id" serial NOT NULL PRIMARY KEY,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(30) NOT NULL
);
表格的名字是自动生成的,如果你要自定义表名,需要在model的Meta类中定义db_table参数,一般会写成小写的表名,格式就是在我们定义好的类和类属性下面,如下所示:
class Pupil(models.Model):
id = models.AutoField(primary_key=True)
sname = models.CharField(max_length=30, null=True)
age = models.IntegerField(null=True)
# cid = models.IntegerField()
class Meta:
db_table = 'pupil' id字段是自动添加的,如果你想要指定自定义主键,只需要在其中一个字段中指定primary_key=True
如果django发现你已经明确设置了field.primary_key=True,它将不会添加自动id列
django支持mysql5.5以及更高的版本
字段:
常用格式的字段类型:
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)
- 二进制类型 字段相关内容
自定义字段:
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)', 自定义一个无符号整数字段
自定义char类型字段:
class FixedCharField(models.Field):
"""
自定义的char类型的字段类
"""
def __init__(self, max_length, *args, **kwargs):
self.max_length = max_length
super(FixedCharField, self).__init__(max_length=max_length, *args, **kwargs) def db_type(self, connection):
"""
限定生成数据库表的字段类型为char,长度为max_length指定的值
"""
return 'char(%s)' % self.max_length class Class(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=25)
# 使用自定义的char类型的字段
cname = FixedCharField(max_length=25)
注意事项:
1.触发Model中的验证和错误提示有两种方式:
a. Django Admin中的错误信息会优先根据Admiin内部的ModelForm错误信息提示,如果都成功,才来检查Model的字段并显示指定错误信息
b. 使用ModelForm
c. 调用Model对象的 clean_fields 方法,如:
# models.py
class UserInfo(models.Model):
nid = models.AutoField(primary_key=True)
username = models.CharField(max_length=32) email = models.EmailField(error_messages={'invalid': '格式错了.'}) # views.py
def index(request):
obj = models.UserInfo(username='', email='uu')
try:
print(obj.clean_fields())
except Exception as e:
print(e)
return HttpResponse('ok') # Model的clean方法是一个钩子,可用于定制操作,如:上述的异常处理。 2.Admin中修改错误提示
# admin.py
from django.contrib import admin
from model_club import models
from django import forms class UserInfoForm(forms.ModelForm):
age = forms.IntegerField(initial=1, error_messages={'required': '请输入数值.', 'invalid': '年龄必须为数值.'}) class Meta:
model = models.UserInfo
# fields = ('username',)
fields = "__all__"
exclude = ['title']
labels = { 'name':'Writer', }
help_texts = {'name':'some useful help text.',}
error_messages={ 'name':{'max_length':"this writer name is too long"} }
widgets={'name':Textarea(attrs={'cols':80,'rows':20})} class UserInfoAdmin(admin.ModelAdmin):
form = UserInfoForm admin.site.register(models.UserInfo, UserInfoAdmin) 注意事项
字段参数:
null 数据库中字段是否可以为空
db_column 数据库中字段的列名
default 数据库中字段的默认值
primary_key 数据库中字段是否为主键
db_index 数据库中字段是否可以建立索引
unique 数据库中字段是否可以建立唯一索引
unique_for_date 数据库中字段【日期】部分是否可以建立唯一索引
unique_for_month 数据库中字段【月】部分是否可以建立唯一索引
unique_for_year 数据库中字段【年】部分是否可以建立唯一索引 verbose_name Admin中显示的字段名称
blank Admin中是否允许用户输入为空
editable Admin中是否可以编辑
help_text Admin中该字段的提示信息
choices Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1) error_messages 自定义错误信息(字典类型),从而定制想要显示的错误信息;
字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
如:{'null': "不能为空.", 'invalid': '格式错误'} validators 自定义错误验证(列表类型),从而定制想要的验证规则
from django.core.validators import RegexValidator
from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\
MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
如:
test = models.CharField(
max_length=32,
error_messages={
'c1': '优先错信息1',
'c2': '优先错信息2',
'c3': '优先错信息3',
},
validators=[
RegexValidator(regex='root_\d+', message='错误了', code='c1'),
RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'),
EmailValidator(message='又错误了', code='c3'), ]
) 字段参数
元信息:
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 元信息
多表关系和参数:
ForeignKey(ForeignObject) # ForeignObject(RelatedField)
to, # 要进行关联的表名
to_field=None, # 要关联的表中的字段名称
on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为
- 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(可执行对象) def func():
return 10 class MyModel(models.Model):
user = models.ForeignKey(
to="User",
to_field="id"
on_delete=models.SET(func),)
related_name=None, # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件:
# 如:
- limit_choices_to={'nid__gt': 5}
- limit_choices_to=lambda : {'nid__gt': 5} from django.db.models import Q
- limit_choices_to=Q(nid__gt=10)
- limit_choices_to=Q(nid=8) | Q(nid__gt=10)
- limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
db_constraint=True # 是否在数据库中创建外键约束
parent_link=False # 在Admin中是否显示关联数据 OneToOneField(ForeignKey)
to, # 要进行关联的表名
to_field=None # 要关联的表中的字段名称
on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为 ###### 对于一对一 ######
# 1. 一对一其实就是 一对多 + 唯一索引
# 2.当两个类之间有继承关系时,默认会创建一个一对一字段
# 如下会在A表中额外增加一个c_ptr_id列且唯一:
class C(models.Model):
nid = models.AutoField(primary_key=True)
part = models.CharField(max_length=12) class A(C):
id = models.AutoField(primary_key=True)
code = models.CharField(max_length=1) ManyToManyField(RelatedField)
to, # 要进行关联的表名
related_name=None, # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件:
# 如:
- limit_choices_to={'nid__gt': 5}
- limit_choices_to=lambda : {'nid__gt': 5} from django.db.models import Q
- limit_choices_to=Q(nid__gt=10)
- limit_choices_to=Q(nid=8) | Q(nid__gt=10)
- limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
symmetrical=None, # 仅用于多对多自关联时,symmetrical用于指定内部是否创建反向操作的字段
# 做如下操作时,不同的symmetrical会有不同的可选字段
models.BB.objects.filter(...) # 可选字段有:code, id, m1
class BB(models.Model): code = models.CharField(max_length=12)
m1 = models.ManyToManyField('self',symmetrical=True) # 可选字段有: bb, code, id, m1
class BB(models.Model): code = models.CharField(max_length=12)
m1 = models.ManyToManyField('self',symmetrical=False) through=None, # 自定义第三张表时,使用字段用于指定关系表
through_fields=None, # 自定义第三张表时,使用字段用于指定关系表中那些字段做多对多关系表
from django.db import models class Person(models.Model):
name = models.CharField(max_length=50) class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through='Membership',
through_fields=('group', 'person'),
) class Membership(models.Model):
group = models.ForeignKey(Group, on_delete=models.CASCADE)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
inviter = models.ForeignKey(
Person,
on_delete=models.CASCADE,
related_name="membership_invites",
)
invite_reason = models.CharField(max_length=64)
db_constraint=True, # 是否在数据库中创建外键约束
db_table=None, # 默认创建第三张表时,数据库中表的名称 多表关系和参数
orm操作:
# 增
models.Tb1.objects.create(c1='xx', c2='oo') # 增加一条数据,可以接受字典类型数据 **kwargs
obj = models.Tb1(c1='xx', c2='oo')
obj.save() # 查
models.Tb1.objects.get(id=123) # 获取单条数据,不存在则报错(不建议)
models.Tb1.objects.all() # 获取全部
models.Tb1.objects.filter(name='seven') # 获取指定条件的数据
models.Tb1.objects.exclude(name='seven') # 去除指定条件的数据 # 删
# models.Tb1.objects.filter(name='seven').delete() # 删除指定条件的数据 # 改
models.Tb1.objects.filter(name='seven').update(gender='') # 将指定条件的数据更新,均支持 **kwargs
obj = models.Tb1.objects.get(id=1)
obj.c1 = ''
obj.save() # 修改单条数据
进阶操作:
# 获取个数
#
# models.Tb1.objects.filter(name='seven').count() # 大于,小于
#
# models.Tb1.objects.filter(id__gt=1) # 获取id大于1的值
# models.Tb1.objects.filter(id__gte=1) # 获取id大于等于1的值
# models.Tb1.objects.filter(id__lt=10) # 获取id小于10的值
# models.Tb1.objects.filter(id__lte=10) # 获取id小于10的值
# models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值 # in
#
# models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据
# models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in # isnull
# Entry.objects.filter(pub_date__isnull=True) # contains
#
# models.Tb1.objects.filter(name__contains="ven")
# models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
# models.Tb1.objects.exclude(name__icontains="ven") # range
#
# models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and # 其他类似
#
# startswith,istartswith, endswith, iendswith, # order by
#
# models.Tb1.objects.filter(name='seven').order_by('id') # asc
# models.Tb1.objects.filter(name='seven').order_by('-id') # desc # group by
#
# from django.db.models import Count, Min, Max, Sum
# models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))
# SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id" # limit 、offset
#
# models.Tb1.objects.all()[10:20] # regex正则匹配,iregex 不区分大小写
#
# Entry.objects.get(title__regex=r'^(An?|The) +')
# Entry.objects.get(title__iregex=r'^(an?|the) +') # date
#
# Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
# Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1)) # year
#
# Entry.objects.filter(pub_date__year=2005)
# Entry.objects.filter(pub_date__year__gte=2005) # month
#
# Entry.objects.filter(pub_date__month=12)
# Entry.objects.filter(pub_date__month__gte=6) # day
#
# Entry.objects.filter(pub_date__day=3)
# Entry.objects.filter(pub_date__day__gte=3) # week_day
#
# Entry.objects.filter(pub_date__week_day=2)
# Entry.objects.filter(pub_date__week_day__gte=2) # hour
#
# Event.objects.filter(timestamp__hour=23)
# Event.objects.filter(time__hour=5)
# Event.objects.filter(timestamp__hour__gte=12) # minute
#
# Event.objects.filter(timestamp__minute=29)
# Event.objects.filter(time__minute=46)
# Event.objects.filter(timestamp__minute__gte=29) # second
#
# Event.objects.filter(timestamp__second=31)
# Event.objects.filter(time__second=2)
# Event.objects.filter(timestamp__second__gte=31) 进阶操作
高级操作:
# extra
#
# extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
# Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
# Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
# Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
# Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid']) # F
#
# from django.db.models import F
# models.Tb1.objects.update(num=F('num')+1) # Q
#
# 方式一:
# Q(nid__gt=10)
# Q(nid=8) | Q(nid__gt=10)
# Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root') # 方式二:
# con = Q()
# q1 = Q()
# q1.connector = 'OR'
# q1.children.append(('id', 1))
# q1.children.append(('id', 10))
# q1.children.append(('id', 9))
# q2 = Q()
# q2.connector = 'OR'
# q2.children.append(('c1', 1))
# q2.children.append(('c1', 10))
# q2.children.append(('c1', 9))
# con.add(q1, 'AND')
# con.add(q2, 'AND')
#
# models.Tb1.objects.filter(con) # 执行原生SQL
#
# from django.db import connection, connections
# cursor = connection.cursor() # cursor = connections['default'].cursor()
# cursor.execute("""SELECT * from auth_user where id = %s""", [1])
# row = cursor.fetchone() 高级操作
##################################################################
# PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET #
################################################################## def all(self)
# 获取所有的数据对象 def filter(self, *args, **kwargs)
# 条件查询
# 条件可以是:参数,字典,Q def exclude(self, *args, **kwargs)
# 条件查询
# 条件可以是:参数,字典,Q def select_related(self, *fields)
性能相关:表之间进行join连表操作,一次性获取关联的数据。
model.tb.objects.all().select_related()
model.tb.objects.all().select_related('外键字段')
model.tb.objects.all().select_related('外键字段__外键字段') def prefetch_related(self, *lookups)
性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。
# 获取所有用户表
# 获取用户类型表where id in (用户表中的查到的所有用户ID)
models.UserInfo.objects.prefetch_related('外键字段') from django.db.models import Count, Case, When, IntegerField
Article.objects.annotate(
numviews=Count(Case(
When(readership__what_time__lt=treshold, then=1),
output_field=CharField(),
))
) students = Student.objects.all().annotate(num_excused_absences=models.Sum(
models.Case(
models.When(absence__type='Excused', then=1),
default=0,
output_field=models.IntegerField()
))) def annotate(self, *args, **kwargs)
# 用于实现聚合group by查询 from django.db.models import Count, Avg, Max, Min, Sum v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id'))
# SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1)
# SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1 v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1)
# SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1 def distinct(self, *field_names)
# 用于distinct去重
models.UserInfo.objects.values('nid').distinct()
# select distinct nid from userinfo 注:只有在PostgreSQL中才能使用distinct进行去重 def order_by(self, *field_names)
# 用于排序
models.UserInfo.objects.all().order_by('-id','age') def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
# 构造额外的查询条件或者映射,如:子查询 Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid']) def reverse(self):
# 倒序
models.UserInfo.objects.all().order_by('-nid').reverse()
# 注:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序 def defer(self, *fields):
models.UserInfo.objects.defer('username','id')
或
models.UserInfo.objects.filter(...).defer('username','id')
#映射中排除某列数据 def only(self, *fields):
#仅取某个表中的数据
models.UserInfo.objects.only('username','id')
或
models.UserInfo.objects.filter(...).only('username','id') def using(self, alias):
指定使用的数据库,参数为别名(setting中的设置) ##################################################
# PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
################################################## def raw(self, raw_query, params=None, translations=None, using=None):
# 执行原生SQL
models.UserInfo.objects.raw('select * from userinfo') # 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名
models.UserInfo.objects.raw('select id as nid from 其他表') # 为原生SQL设置参数
models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,]) # 将获取的到列名转换为指定列名
name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
Person.objects.raw('SELECT * FROM some_other_table', translations=name_map) # 指定数据库
models.UserInfo.objects.raw('select * from userinfo', using="default") ################### 原生SQL ###################
from django.db import connection, connections
cursor = connection.cursor() # cursor = connections['default'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1])
row = cursor.fetchone() # fetchall()/fetchmany(..) def values(self, *fields):
# 获取每行数据为字典格式 def values_list(self, *fields, **kwargs):
# 获取每行数据为元祖 def dates(self, field_name, kind, order='ASC'):
# 根据时间进行某一部分进行去重查找并截取指定内容
# kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)
# order只能是:"ASC" "DESC"
# 并获取转换后的时间
- year : 年-01-01
- month: 年-月-01
- day : 年-月-日 models.DatePlus.objects.dates('ctime','day','DESC') def datetimes(self, field_name, kind, order='ASC', tzinfo=None):
# 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间
# kind只能是 "year", "month", "day", "hour", "minute", "second"
# order只能是:"ASC" "DESC"
# tzinfo时区对象
models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)
models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai')) """
pip3 install pytz
import pytz
pytz.all_timezones
pytz.timezone(‘Asia/Shanghai’)
""" def none(self):
# 空QuerySet对象 ####################################
# METHODS THAT DO DATABASE QUERIES #
#################################### def aggregate(self, *args, **kwargs):
# 聚合函数,获取字典类型聚合结果
from django.db.models import Count, Avg, Max, Min, Sum
result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid'))
===> {'k': 3, 'n': 4} def count(self):
# 获取个数 def get(self, *args, **kwargs):
# 获取单个对象 def create(self, **kwargs):
# 创建对象 def bulk_create(self, objs, batch_size=None):
# 批量插入
# batch_size表示一次插入的个数
objs = [
models.DDD(name='r11'),
models.DDD(name='r22')
]
models.DDD.objects.bulk_create(objs, 10) def get_or_create(self, defaults=None, **kwargs):
# 如果存在,则获取,否则,创建
# defaults 指定创建时,其他字段的值
obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '','u_id': 2, 't_id': 2}) def update_or_create(self, defaults=None, **kwargs):
# 如果存在,则更新,否则,创建
# defaults 指定创建时或更新时的其他字段
obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '','u_id': 2, 't_id': 1}) def first(self):
# 获取第一个 def last(self):
# 获取最后一个 def in_bulk(self, id_list=None):
# 根据主键ID进行查找
id_list = [11,21,31]
models.DDD.objects.in_bulk(id_list) def delete(self):
# 删除 def update(self, **kwargs):
# 更新 def exists(self):
# 是否有结果 其他操作
执行原生sql语句:
from django.db import connection def my_custom_sql(self):
with connection.cursor() as cursor:
cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz])
cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz])
row = cursor.fetchone() return row
数字和字符串:
# 数字自增
from django.db.models import F
models.UserInfo.objects.update(num=F('num') + 1) # 字符串更新
from django.db.models.functions import Concat
from django.db.models import Value models.UserInfo.objects.update(name=Concat('name', 'pwd'))
models.UserInfo.objects.update(name=Concat('name', Value(''))) 数字自增和字符串更新
这里是性能优化,在联表查询的时候我们应用到以下的方法,可以减少数据库的压力
def select_related(self, *fields)
性能相关:表之间进行join连表操作,一次性获取关联的数据。
model.tb.objects.all().select_related()
model.tb.objects.all().select_related('外键字段')
model.tb.objects.all().select_related('外键字段__外键字段') 官网示例:
from django.db import models class City(models.Model):
# ...
pass class Person(models.Model):
# ...
hometown = models.ForeignKey(
City,
on_delete=models.SET_NULL,
blank=True,
null=True,
) class Book(models.Model):
# ...
author = models.ForeignKey(Person, on_delete=models.CASCADE)
… then a call to Book.objects.select_related('author__hometown').get(id=4)
will cache the related Person
and the related City
:
# Hits the database with joins to the author and hometown tables.
b = Book.objects.select_related('author__hometown').get(id=4) # 这里是一次性把关联的外键字段都取出来了,
p = b.author # Doesn't hit the database. # 然后在这里把Book关联的author取出来
c = p.hometown # Doesn't hit the database. # 再把跟person关联的hometown取出来 # Without select_related()...
b = Book.objects.get(id=4) # Hits the database.
p = b.author # Hits the database.
c = p.hometown # Hits the database.
def prefetch_related(self, *lookups)
性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。
# 获取所有用户表
# 获取用户类型表where id in (用户表中的查到的所有用户ID)
models.UserInfo.objects.prefetch_related('外键字段') from django.db.models import Count, Case, When, IntegerField
Article.objects.annotate(
numviews=Count(Case(
When(readership__what_time__lt=treshold, then=1),
output_field=CharField(),
))
) students = Student.objects.all().annotate(num_excused_absences=models.Sum(
models.Case(
models.When(absence__type='Excused', then=1),
default=0,
output_field=models.IntegerField()
)))
练习示例:
这里的练习题是我们的qimi博客里面的那些题,都做出来整理好了http://www.cnblogs.com/liwenzhou/articles/8337352.html
题目(所需要的表格,models类以及属性)
from django.db import models # Create your models here. class Publisher(models.Model):
name = models.CharField(max_length=32)
city = models.CharField(max_length=20) def __str__(self):
return self.name class AuthorDetail(models.Model):
addr = models.CharField(max_length=30)
email = models.EmailField() class Author(models.Model):
name = models.CharField(max_length=30)
age = models.IntegerField()
phone = models.IntegerField()
detail = models.OneToOneField(to='AuthorDetail') def __str__(self):
return self.name class Book(models.Model):
title = models.CharField(max_length=30)
publish_date = models.DateField(auto_now_add=True) # 这里是我们把这个参数加上之后,在表格里面,
# 我们添加数据的时候不加这一栏数据系统会根据这个字段里面的参数去给我们自动按照当前日期时间给我们添加上
price = models.DecimalField(max_digits=5, decimal_places=2)
# 创建外键,关联publish
publisher = models.ForeignKey(to='Publisher')
author = models.ManyToManyField(to='Author')
memo = models.CharField(max_length=50, null=True)
keep_num = models.IntegerField()
buy_num = models.IntegerField() def __str__(self):
return self.title
答案:
Qimi 博客地址,多对多,多对一,一对一查询练习题 http://www.cnblogs.com/liwenzhou/articles/8337352.html
day67 F查询和Q查询,双下线方法,多表查以及单表查操作示例
博客更新提示(例子都试过之后整理到这里面来然后再最终放到我博客里贴好) 查找所有书名里包含番茄的书
models.Book.objects.filter(title__contains='番茄') 查找出版日期是2017年的书
models.Book.objects.filter(publisher_date__year=2017) 查找出版日期是2017年的书名
models.Book.objects.filter(publisher_date__year=2017).values('title') 查找价格大于10元的书
models.Book.objects.filter(price__gt=10) 查找价格大于10元的书名和价格
models.Book.objects.filter(price__gt=10).values_list('title', 'price') 查找memo字段是空的书
models.Book.objects.filter(memo__isnull=True)
<QuerySet [<Book: 自由>]> 查找在北京的出版社
查找名字以沙河开头的出版社
models.Publisher.objects.filter(name__startswith='沙河')
<QuerySet [<Publisher: 沙河出版社>]> 查找作者名字里面带“小”字的作者
models.Author.objets.filter(name__contains='小') 查找年龄大于30岁的作者
models.Author.objects.filter(age__gt=14) 查找手机号是155开头的作者
models.Author.objects.filter(phone__startswith=123)
<QuerySet [<Author: 小魔女>]> 查找手机号是123开头的作者的姓名和年龄
models.Author.objects.filter(phone__startswith=123).values_list('name','age')
<QuerySet [('小魔女', 19)]> 查找书名是“番茄物语”的书的出版社
models.Book.objects.get(title='番茄物语').publisher
我们的objects对象才有.字段名的方法,这里要用到get而不是filter, queryset集合没有这个.字段名的方法,
<Publisher: 老男孩出版社> 查找书名是“番茄物语”的书的出版社所在的城市
models.Book.objects.get(title='番茄物语').publisher.city
我们这里的结果是根据上面的结果拿到的,在objects对象的后面使用.字段名就可以拿到结果
'广州' 查找书名是“番茄物语”的书的出版社的名称
models.Book.objects.get(title='番茄物语').publisher.name
'老男孩出版社' 查找书名是“番茄物语”的书的所有作者
models.Book.objects.get(title='番茄物语').author.values('name')
<QuerySet [{'name': '小可爱'}, {'name': '小天使'}]> 查找书名是“番茄物语”的书的作者的年龄
models.Book.objects.get(title='番茄物语').author.values_list('name','age')
<QuerySet [('小可爱', 16), ('小天使', 14)]> 查找书名是“番茄物语”的书的作者的手机号码
models.Book.objects.get(title='番茄物语').author.values('phone')
<QuerySet [{'phone': 1346664}, {'phone': 576543}]> 查找书名是“番茄物语”的书的作者的地址
models.Book.objects.get(title='番茄物语').author.all().values('detail__addr')
<QuerySet [{'detail__addr': '北京'}, {'detail__addr': '深圳'}]> 查找书名是“番茄物语”的书的作者的邮箱
models.Book.objects.get(title='番茄物语').author.all().values('detail__email')
<QuerySet [{'detail__email': '[email protected]'}, {'detail__email': '[email protected]'}]> 查询id为1的书的出版社所在的城市
models.Book.objects.get(id=1).publisher.city
'广州' 查询小精灵作者的邮箱
models.Author.objects.get(name='小仙女').detail.email
'[email protected]' 查询所有addr="稻城"的作者姓名,年龄,电话
models.AuthorDetail.objects.filter(addr='稻城').values_list('author__name','author__age','author__phone')
<QuerySet [('小天使', 14, 576543), ('小仙女', 18, 98765)]> 查询番茄物语所有作者的姓名和邮箱(正向查找)
a = models.Book.objects.get(title='番茄物语').author.all()
for obj in a:
print(obj.name,obj.detail.email) 小可爱 [email protected]
小天使 [email protected] 查询小仙女出版的所有书籍
a=models.Author.objects.get(name='小仙女').book_set.all()
for i in a:
print(i.title) 小辣椒物语
百年孤独 ======================================================================================================双下划线方法: [应用场景就是在跨表查询的时候使用我们的双下线方法效率会高出很多,比起我们写原生sql语句的时候那些联表查询效率要高出太多了] 查询沙河出版社出版的所有书籍的名称和价格
models.Book.objects.filter(publisher__name='沙河出版社').values_list('title','price') -->正向查询
<QuerySet [('萝卜物语', Decimal('34.80')), ('生蚝物语', Decimal('21.87'))]> models.Publisher.objects.get(name='沙河出版社').book_set.values_list('title','price') -->反向查询
<QuerySet [('萝卜物语', Decimal('34.80')), ('生蚝物语', Decimal('21.87'))]> 双下线的反向查找方法:
models.Publisher.objects.filter(name='沙河出版社').values_list('book__title','book__price') 查询小仙女出版的所有书籍的名称
双下线方法查找
models.Author.objects.filter(name='小仙女').values_list('book__title')
<QuerySet [('小辣椒物语',), ('百年孤独',)]> models.Book.objects.filter(author__name='小仙女').values_list('title') -->正向查询
<QuerySet [('小辣椒物语',), ('百年孤独',)]>
models.Author.objects.get(name='小仙女').book_set.all().values_list('title') --->反向查询
<QuerySet [('小辣椒物语',), ('百年孤独',)]> 查询所有沙河出版社出版的所有书籍的名字和作者姓名
我们先取到沙河出版社出版的所有书籍,然后再在此基础上去找到我们的values_list(),然后去找到title,和跨表找作者名字
正向查询:
models.Book.objects.filter(publisher__name='沙河出版社').values_list('title','author__name') models.Publisher.objects.get(name='沙河出版社').book_set.all().values_list('title','author__name') --->反向查询
<QuerySet [('萝卜物语', '小魔女'), ('生蚝物语', '小天使')]> 双下线跨表查询方法,跨三张表格:
models.Publisher.objects.filter(name='沙河出版社').values_list('book__title','book__author__name')
<QuerySet [('萝卜物语', '小魔女'), ('生蚝物语', '小天使')]> 查询memo为空的所有书
>>> models.Book.objects.filter(memo__isnull=True) from booklist import models
from django.db.models import Avg,Count,Max,Min,Sum 下面就是聚合函数使用方法:
关键字aggregate() 里面可以加多个参数
示例:查看所有书籍的平均价格
models.Book.objects.all().aggregate(Avg('price'))
{'price__avg': 27.066667}
我们在查询平均价格的时候,可以给这个查询结果一个变量名,我们这里可以比对出来,上下两句话的执行结果的区别,一个名字是price后面拼接了avg,另一个是我们自己自定义的而不是系统自动生成的,average_price这个是我们自定义的
models.Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 27.066667} 我们在aggregate里面传了三个参数,[aggregate里面可以传多个参数]
models.Book.objects.all().aggregate(Avg("price")),Max('price'),Min('price')
({'price__avg': 27.066667}, Max(F(price)), Min(F(price))) 分组示例:
我们的分组关键字annotate() 就相当于我们的sql里面的group by
统计每一本书的作者的个数:
book_list=models.Book.objects.all().annotate(author_num=Count('author')) # 这里把计数的结果赋值给一个变量,然后for循环这个变量,取值
for obj in book_list:
print(obj.author_num) 2
1
1
1
1
1
上面我们用到的annotate是分组专用名词 统计每个出版社卖的最便宜的书的价格
al = models.Publisher.objects.annotate(min_price=Min('book__price'))
# 我们按照出版社去查询,根据出版社进行分组找到每个出
# 版社最便宜的书的价格
for i in al:
print(i.min_price) 12.98
21.87
12.12 models.Book.objects.values('publisher__name').annotate(min_price=Min('price')) -->这里是根据书去反向查找出版社,跟上面比较反而多了一步操作
<QuerySet [{'publisher__name': '老男孩出版社', 'min_price': Decimal('12.98')}, {'publisher__name': '沙河出版社', 'min_price': Decimal('21.87')}, {'publisher__name': '中信出版社', 'min_price': Decimal('12.12')}]> 示例3:统计不止一个作者的图书
models.Book.objects.values('publisher__name').annotate(min_price=Min('price'))
<QuerySet [{'publisher__name': '老男孩出版社', 'min_price': Decimal('12.98')}, {'publisher__name': '沙河出版社', 'min_price': Decimal('21.87')}, {'publisher__name': '中信出版社', 'min_price': Decimal('12.12')}]> 示例5:查询各个作者出的书的总价格
models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price')
# sum_price是我们自己定义的一个变量,values里面的name是字段名,然后我们的Sum聚合函数里面的跨表查询的book的结果赋值给了sum_price这个变量,一并查询出来,
<QuerySet [{'name': '小可爱', 'sum_price': Decimal('12.98')}, {'name': '小天使', 'sum_price': Decimal('34.85')}, {'name': '小魔女', 'sum_price': Decimal('46.92')}, {'name': '小仙女', 'sum_price': Decimal('80.63')}]> F查询:
F()的示例可以在查询中引用字段,来比较同一个model实例中两个不同字段的值, from booklist import models
from django.db.models import F
models.Book.objects.filter(keep_num__lt=F('buy_num'))
# 我们这里是取到了book表格里面的两个字段然后进行比较,前面的字段值小于后面的字段值,那么就满足条件,然后就取到满足条件的值,
<QuerySet [<Book: 百年孤独>]>
models.Book.objects.filter(buy_num__lt=F('keep_num'))
<QuerySet [<Book: 番茄物语>, <Book: 萝卜物语>, <Book: 小辣椒物语>, <Book: 生蚝物语>, <Book: 自由>]> #这里是修改操作,我们把book表格里面的price字段通过update进行修改
models.Book.objects.all().update(price=F("price")+30) from django.db.models import Value
from django.db.models.functions import Concat
models.Book.objects.all().update(title=Concat(F("title"),Value('#$%'),Value("num one"),Value('#$%')))
6
这里我们把title字段过滤出来,然后我们把它进行字符串拼接,
得到的结果就是这样的:
< 自由#$%num one#$% > Q查询方法
filter()等方法中的关键字参数查询都是一起进行'and'的,如果你需要执行更加复杂的查询,例如逻辑语句里面的or查询,你就可以使用q对象 查询作者名是小仙女或者是小魔女的
models.Book.objects.filter(Q(author__name='小仙女')|Q(author__name='小魔女'))
<QuerySet [<Book: 萝卜物语#$%num one#$% >, <Book: 自由#$%num one#$% >, <Book: 小辣椒物语#$%num one#$% >, <Book: 百年孤独#$%num one#$% >]>
就是在filter里面,加上两个条件,跨表格查询就使用 '表名'__'字段名'
我们在filter里面是条件可以在此基础上进行更加复杂的操作,比如使用and%, |或 ,操作符或者再加上括号用分组来一起用,Q对象还可以用~来取反查询, 查询作者名是小仙女并且不是2018年出版的书的书名
models.Book.objects.filter(Q(author__name='小仙女')&~Q(publish_date__year=2018)).values_list('title')
<QuerySet [('小辣椒物语#$%num one#$% ',)]>
第一个条件是Q作者名是小仙女,
第二个条件是并且,要同时满足两个条件,&
第三个条件是~不是2018年出版的书
要同时满足 是 并且 不是 三个条件 Q&~Q 用来表示这些逻辑 查询我们的出版年份是2017或者是2018年,书名包含'物语'两个字的书
models.Book.objects.filter(Q(publish_date__year=2018)|Q(publish_date__year=2017),title__icontains='物语')
<QuerySet [<Book: 番茄物语#$%num one#$% >, <Book: 萝卜物语#$%num one#$% >, <Book: 小辣椒物语#$%num one#$% >]>
Q第一个条件要满足出版年份是2017,
| 或者第二个逻辑语句关键字
Q 第二个条件是要满足出版年份是2018
第三个是书名包含'物语'两个字的书,
这里是基于sql子查询得到的orm:
我们这里是一对一表格查询,正/反查
一对一查询,
反向查询,通过表名author
b=models.AuthorDetail.objects.filter(addr='北京').first()
print(b.author.name)
结果: '小可爱'
正向查询,通过字段detail
a = models.Author.objects.filter(name='小仙女').first()
print(a.detail.email)
结果: '[email protected]'
一对多关系
正向查询
a=models.Book.objects.filter(title='python').first()
bc=a.publish.all() # 得到所有的数据,要拿出来就使用for循环遍历每一对象
for obj in bc:
print(obj.city,obj.name)
结果: '北京' '沙河出版社'
''凤凰'' '老男孩出版社'
反向查询
w = models.Publish.objects.filter(name='老男孩出版社').first() # 找到番茄物语这本书
q=w.Book_set.all() # 这里是我们找到书的作者,因为是正向查询所以我们直接在这里使用字段名即可得到结果,然后.all() 拿出所有的结果,再使用for循环去遍历它
for i in q:
print(i.title,i.price) # 取出每一个all()里面的结果,然后我们使用.name得到它的字符串结果
结果:
'python' '78.3'
'Linux' '65.8'
多对多关系查询:
正向:
b=models.Book.objects.filter(title='番茄物语').first()
ad=b.author.all()
for obj in ad:
print(obj.name,obj.age)
结果:
' 小可爱' '15'
'小天使' '16'
反向:
a = models.Author.objects.filter(name='小仙女').first()
qw=a.book_set.all()
for obj in qw:
print(obj.title,obj.price)
结果:
'' 小辣椒物语'' '32.2'
''百年孤独'' '21.2'
基于sql联表查询得到的orm:
我们的values的用法在这里内部是实现了一个for循环,
'''
Publish.objects.filter(name='北京出版社').values('book__title') # 这一句是完整的orm查询语句
publish_list=Publish.objects.filter(name="北京出版社") # 这里是得到一个queryset对象,找到出版社名字是北京出版社的所有出版社
temp=[] # 定义一个空列表
for obj in publish_list: # 遍历上面得到的出版社的queryset对象,有一个出版社的queryset对象就遍历一次,有两个就遍历两次,
book_list=obj.book_set.all() # 然后拿到queryset里面的每一个objects对象,反向查询通过出版社去查所有书,使用表名_set.all()就取到了所有的书对象也是一个queryset对象集合,再接着在所有书的对象上继续循环
for book in book_list: # 同上有一个书籍的queryset对象就遍历一次,有两个就遍历两次,
d={} # 定义一个空的字典
d["book__title"]=book.title # 我们的键就是上面的values里面输入的内容,然后值就是我们的book表名.字段名的值
temp.append(d) # 我们上面有两层遍历,每一层遍历后的结果都放进来,有几个出版社的对象,每个出版社对象里面有几个书籍的对象,就放进来几个字典,最后返回的结果是列表里面含着字典的结构,
return temp # 以上就是我们的values的所有内部实现结果了
'''
一对一关系
双下方法
反查
models.AuthorDetail.objects.filter(addr="稻城").values("author__name", 'author__age')
结果:
<QuerySet [{'author__name': '小天使', 'author__age': 14}, {'author__name': '小仙女', 'author__age': 18}]>
正查,
models.Author.objects.filter(name='小仙女').values("detail__addr","detail__email") # 得到的是一个queryset对象里面是一个中括号里面包裹着字典,如果是values_list得到的是一个queryset对象里面包裹着元组,元组里面是上面的字典里面的键值对的键
结果:
<QuerySet [('稻城', '[email protected]')]>
models.Author.objects.filter(detail__addr='稻城').values('name','age','phone')
<QuerySet [{'name': '小天使', 'age': 14, 'phone': 576543}, {'name': '小仙女', 'age': 18, 'phone': 98765}]>
一对多关系
反向查询
models.Publisher.objects.filter(name=''老男孩出版社'').values('book__title','book__price')
结果:<QuerySet [{'book__title': '番茄物语', 'book__price': Decimal('32.98')}, {'book__title': '小辣椒物语', 'book__price': Decimal('44.40')}]>
models.Publisher.objects.filter(book__title='百年孤独').values('name')
结果: <QuerySet [{'name': '中信出版社'}]>
正向查询
models.Book.objects.filter(title='百年孤独').values("publisher__name")
结果: <QuerySet [{'publisher__name': '中信出版社'}]>
多对多关系
正向查询
models.Book.objects.filter(title='番茄物语').values_list('author__name','author__age')
结果: <QuerySet [('小可爱', 16), ('小天使', 14)]>
反向查询
models.Author.objects.filter(name='小仙女').values('book__title')
结果: <QuerySet [{'title': '小辣椒物语'}, {'title': '百年孤独'}]>
models.Book.objects.filter(author__name='小仙女').values_list('title')
<QuerySet [('小辣椒物语',), ('百年孤独',)]>
我们的orm里面是有基于sql子查询,还有sql联表查询,
然后在我们的orm里面还有正向查询和反向查询的,所以我们的一句查询语句,在逻辑通顺的情况下,是可以写出四种orm的语句出来的.所以我们都需要熟练掌握
我们的联表查询里面有通过values去联表过滤,还有通过filter去联表过滤的方法,这是两种方式
都是联表查询,生成的sql语句都是一样的,效率不存在高低之分