单表
1. model.py中创建模型
class Employee(models.Model): id = models.AutoField(primary_key=True) # 不写自动创建 name = models.CharField(max_length=16) gender = models.BooleanField(default=1) # 0女,1男 birth = models.DateField(auto_now_add=True) department = models.CharField(max_length=32) salary = models.DecimalField(max_digits=10, decimal_places=1)
def __str__(self):
return self.name
2. django连接mysql
settings.py文件
# 删除\注释掉原来的DATABASES配置项,新增下述配置 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', # 使用mysql数据库 'NAME': 'db1', # 要连接的数据库 'USER': 'root', # 链接数据库的用于名 'PASSWORD': '', # 链接数据库的用于名 'HOST': '127.0.0.1', # mysql服务监听的ip 'PORT': 3306, # mysql服务监听的端口 'ATOMIC_REQUEST': True, #设置为True代表同一个http请求所对应的所有sql都放在一个事务中执行 #(要么所有都成功,要么所有都失败),这是全局性的配置,如果要对某个 #http请求放水(然后自定义事务),可以用non_atomic_requests修饰器 'OPTIONS': { "init_command": "SET storage_engine=INNODB", #设置创建表的存储引擎为INNODB } } }
3. 连接数据库之前首先创建好数据库
mysql> create database db1;
4. pymysql连接数据库mysql
django的orm底层操作数据库的python模块默认是mysqldb而非pymysql,
然而对于解释器而言,python2.x解释器支持的操作数据库的模块是mysqldb,
而python3.x解释器支持的操作数据库的模块则是pymysql
5. 执行数据库迁移命令, 会在指定的数据库中db1创建表
python manage.py makemigrations
python manage.py migrate
注意:
1. makemigrations只是生成一个数据库迁移记录的文件, migrate才是将数据提交到数据库中执行
2. 数据库迁移文件记录在app01下的migrations文件夹中
5.1 在django中执行表的迁移命令的时候报错: 某个表已经存在
解决方法: 在Django中contrib/admin找到migrations.py文件删除里面的表迁移记录即可
6.想打印orm转换过程中的SQL, 需要在settings.py文件中配置日志
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } }
7. 在表生成后, 如果需要添加, 删除, 修改表中字段
# 1. 增加字段 在模型类Employee里直接新增字段,强调:对于orm来说,新增的字段必须用default指定默认值 如: publish = models.CharField(max_length=12,default='人民出版社',null=True) 重新执行那两条数据库迁移命令 # 2. 删除字段 直接注释掉字段 重新执行那两条数据库迁移命令 # 3. 修改字段 将模型类中字段修改 重新执行那两条数据库迁移命令
增删改查
1. 增
方式一
# 1、用模型类创建一个对象,一个对象对应数据库表中的一条记录 obj = Employee(name="zsh", gender=0, birth='1997-01-27', department="开发部", salary=100.1) # 2、调用对象下的save方法,即可以将一条记录插入数据库 obj.save()
方式二
# 每个模型表下都有一个objects管理器,用于对该表中的记录进行增删改查操作,其中增加操作如下所示 obj = Employee.objects.create(name="zsh", gender=0, birth='1997-01-27', department="开发部", salary=100.1) # 对于注册的用户使用create_user, 用来对password进行加密 obj = Employee.objects.create_user(name="zsh", gender=0, birth='1997-01-27', department="开发部", salary=100.1, password="123")
2. 查
2.1 基础的查询
get, first, last
count, exists
filter, exclude, all, order_by, values, values_list, reverse, distinct
返回的为Employee的对象(除count之外)
# get(**kwargs) obj = Employee.objects.get(id=1) print(obj.name, obj.salary) # first() 查询的第一个结果 obj = Employee.objects.first() print(obj.name)
# last 返回查询的最后一个对象
obj = models.Book.objects.last()
# count() 对查询的数据进行计数, 结果是数字
res = models.Book.objects.count()
返回的为QuerySet对象, 存放查询出来的多个对象
# filter(**kwargs) QuerySet对象中包含了符合过滤条件的多个记录对象 queryset_res=Employee.objects.filter(department='技术部') # exclude(**kwargs) # QuerySet对象中包含了不符合过滤条件的多个记录对象 queryset_res=Employee.objects.exclude(department='技术部') # all() # QuerySet对象中包含了查询出的所有记录对象 queryset_res = Employee.objects.all() # 查询出表中所有的记录对象 # order_by(*field) # 参数为排序字段,可以指定多个字段,在字段1相同的情况下,可以按照字段2进行排序 queryset_res = Employee.objects.order_by("salary","-id") # 先按照salary字段升序排,如果salary相同则按照id字段降序排 # values(*field) # 参数为字段名,可以指定多个字段. QuerySet对象中包含的并不是一个个的记录对象,而上多个字典,字典的key即我们传入的字段名 queryset_res = Employee.objects.values('id','name') # 结果<QuerySet [{'id': 1, 'name': 'zsh'}, {'id': 2, 'name': 'Bon'}, ......]> print(queryset_res[0]['name']) # values_list(*field) # 参数为字段名,可以指定多个字段.QuerySet对象中包含的并不是一个个的记录对象,而上多个小元组,字典的key即我们传入的字段名 queryset_res = Employee.objects.values_list('id','name') # 结果: <QuerySet [(1, 'zsh'), (2, 'Bon'),), ......]> print(queryset_res[0][1]) # reverse() # 对排序的结果取反,返回值为QuerySet对象 queryset_res = Employee.objects.order_by("salary", "-id").reverse() # exists() # 返回值为布尔值,如果QuerySet包含数据,就返回True,否则返回False res = Employee.objects.filter(id=100).exists() # distinct() # 从values或values_list的返回结果中剔除重复的记录对象,返回值为QuerySet对象 res = Employee.objects.filter(name='Egon').values('name', 'salary').distinct()
django的ORM支持链式操作
res=Employee.objects.filter(gender=1).order_by('-id').values_list('id','name') print(res) # 输出:<QuerySet [(6, 'zsh'), (5, 'Bon'), (4, 'Tom'), (2, 'hg')]>
2.2 基于双下划线的模糊查询
# id__in=[1,3,5] 包含某个值, 查询id为1,3,5的 Employee.objects.filter(id__in=[1,3,5]) # id__gt=3 查询id大于3的 Employee.objects.filter(id__gt=3) # id__lt=3 查询id小于3的 Employee.objects.filter(id__lt=3) # id__gte=3 查询id大于等于3的 Employee.objects.filter(id__gte=3) # id__lte=3 查询id小于等于3的 Employee.objects.filter(id__lte=3) # id__range=[1,3] 查新id1到3的 Employee.objects.filter(id__range=[1,3]) # name__contains="z" 查询name中有z的, 区别大小写 Employee.objects.filter(name__contains="z") # name__icontains="z" 查询name中含有z, 不区别大小写 Employee.objects.filter(name__icontains="z") # name__startswith="z" 查询name是以'z'开头的, 区别大小写 Employee.objects.filter(name__startswith="z") # name__istartswith="z" 查询name是以'z'开头的, 不区别大小写 Employee.objects.filter(name__istartswith="z") # date 可以根据年月日进行过滤 Employee.objects.filter(birth__year=1995) # 查询出生在1995年的 Employee.objects.filter(birth__month=2) # 查询出生在2月的
2.3 F与Q查询
2.3.1 F查询
我们在进行条件过滤时, 都只是用某个字段与某个具体的值做比较
如果我们要对两个字段的值做比较, 该如何比较?
Django提供F()来做比较. F()的实列可以在查询中引用字段, 来比较两个不同字段的值
# 一张书籍表中包含字段:评论数commentNum、收藏数keepNum,要求查询:评论数大于收藏数的书籍 from django.db.models import F Book.objects.filter(commnetNum__lt=F('keepNum'))
Django支持F()对象之间以及F()对象的常数之间的加减乘除和取模的操作
# 查询评论数大于收藏数2倍的书籍 from django.db.models import F Book.objects.filter(commnetNum__lt=F('keepNum')*2)
修改操作也可以使用F函数, 比如将每一本书的价格提高20元
Book.objects.all().update(price=F("price")+30)
更新字段等于字段本身在拼接一个字符串应该这样做
from djiango.db.models.functions import Concat from django.db.models import Value Employee.objects.filter(nid__lte=3).update(name=Concat(F('name'), Value('_sb)))
2.3.2 Q查询
filter()等方法中逗号隔开的多个关键字参数都是逻辑与的关系.
如果我们需要使用逻辑或(OR)来连接多个条件,就用到了Django的Q对象
可以将条件传给类Q实例化出一个对象, Q的对象可以使用&和|操作符组合起来
&等同于and, |等同于or
form django.db.models import Q # 查找id大于5或者name='zsh'的 Employee.objects.filter(Q(id__gt=5) | Q(name="zsh"))
Q对象可以使用~操作符取反, 相等于not
from django.db.models import Q Employee.objects.filter(~Q(id__gt=5) | Q(name='zsh'))
当我们的过滤条件中既有or又有and, 则需要混合使用Q对象与关键字参数,
单Q对象必须位于所有关键字参数的前面
from django.db.modes import Q Employee.objects.filter(Q(id__gt=5) | Q(name='zsh'), salary__lt=100) # 等同于sql select * from app01_employee where (id>5 or name='zsh') and salary<100;
2.4 聚合查询
聚合查询aggregate()是把所有查询出的记录对象整体当做一个组,
我们可以搭配聚合函数来对整体进行一个聚合操作
from django.db.models import Avg, Max, Sum, Min, Count # 1. 调用objects下的aggregate()方法, 会把表中所有记录对象当做一组进行聚合 res = Employee.objects.aggregate(Avg("salary")) # select avg(salary) as salary_avg from app01_employee; # 2. aggregate()会把QuerySet对象中包含的所有记录对象当成一组进行聚合 res2=Employee.objects.all().aggregate(Avg("salary")) # select avg(salary) as salary__avg from app01_employee; res3=Employee.objects.filter(id__gt=3).aggregate(Avg("salary")) # select avg(salary) as salary__avg from app01_employee where id > 3; print(res3) # 输出:{'salary__avg': 71.0}
aggregate()的返回值为字典类型, 字典的key是有"聚合字段的名称__聚合函数的名称"和成的
Avg("salary") 合成的名字为 'salary__avg'
若我们想定制字典的key名, 我们可以指定关键参数, 如下:
res1=Employee.objects.all().aggregate(avg_sal=Avg('salary')) # select avg(salary) as avg_sal from app01_employee; print(res1) # 输出:{'avg_sal': 70.73} # 关键字参数名就会被当做字典的key
如果我们想要得到多个聚合结果, 那就需要为aggregate传入多个参数
res1=Employee.objects.all().aggregate(nums=Count('id'),avg_sal=Avg('salary'),max_sal=Max('salary')) # 相当于SQL:select count(id) as nums,avg(salary) as avg_sal,max(salary) as max_sal from app01_employee; print(res1) # 输出:{'nums': 10, 'avg_sal': 70.73, 'max_sal': Decimal('200.3')}
2.5 分组查询
分组查询annotate()相当于SQL语句中的group_by, 是在分组后, 对每个组进行单独的聚合,
需要强调的是, 在进行单表查询时, annotate()必须搭建values()使用: values("分组字段").annotate(聚合函数), 如下:
# 查询每个部门下的员工数 res = Employee.objects.values('department').annotate(num=Count('id')) # 相当于SQL: # select department, count(id) as num from app01_employee group_by department; print(res) # 结果: <QuerySet [{'department': '财务部', 'num': 2}, {'department': '技术部', 'num': 3}, {'department': '运营部', 'num': 2}]
跟在annotate前的values方法是用来指定分组字段, 及group_by后的字段,
而跟在annotate后的values方法,则是用来指定分组后要查询的字段,即select 后跟的字段
res=Employee.objects.values('department').annotate(num=Count('id')).values('num') # 相当于sql: # select count(id) as num from app01_employee group by department; print(res) # 输出:<QuerySet [{'num': 2}, {'num': 3}, {'num': 2}]>
跟在annotate前的filter方法表示where条件,跟在annotate后的filter方法表示having条件,如下
# 查询男员工数超过2人的部门名 res=Employee.objects.filter(gender=1).values('department').annotate(male_count=Count("id")).filter(male_count__gt=2).values('department') print(res) # 输出:<QuerySet [{'department': '技术部'}]> # 解析: # 1、跟在annotate前的filter(gender=1) 相当于 where gender = 1,先过滤出所有男员工信息 # 2、values('department').annotate(male_count=Count("id")) 相当于group by department,对过滤出的男员工按照部门分组,然后聚合出每个部门内的男员工数赋值给字段male_count # 3、跟在annotate后的filter(male_count__gt=2) 相当于 having male_count > 2,会过滤出男员工数超过2人的部门 # 4、最后的values('department')代表从最终的结果中只取部门名
总结:
1、values()在annotate()前表示group by的字段,在后表示取值
1、filter()在annotate()前表示where条件,在后表示having
注意:
如果我们在annotate前没有指定values(),那默认用表中的id字段作为分组依据,而id各不相同,如此分组是没有意义的,如下
res=Employee.objects.annotate(Count('name')) # 每条记录都是一个分组 res=Employee.objects.all().annotate(Count('name')) # 同上
3. 修改记录
3.1 修改单条记录
# 1、获取记录对象 obj=Employee.objects.filter(name='Egon').first() # 2、修改记录对象属性的值 obj.name='zsh' obj.gender=1 # 3、重新保存 obj.save()
3.2 修改QuerySet中的所有记录对象
queryset_obj=Employee.objects.filter(id__gt=5) rows=queryset_obj.update(name='EGON',gender=1)
4. 删除记录
4.1 直接删除单条记录
obj=Employee.objects.first()
obj.delete()
4.2 删除QuerySet中的所有对象
queryset_obj=Employee.objects.filter(id__gt=5)
rows=queryset_obj.delete()
强调: 在工作中并不会真正的删除一条数据, 只是修改一种的一个字段
管理objects下并没有delete方法,这是一种保护机制,是为了避免意外地调用 Employee.objects.delete() 方法导致所有的记录被误删除从而跑路。
但如果你确认要删除所有的记录,那么你必须显式地调用管理器下的all方法,拿到一个QuerySet对象后才能调用delete方法删除所有
Employee.objects.all().delete()