1.准备工作(django连接数据库)
1.本机电脑下载好mysql数据库
2.打开django,修改setting.py中的DATABASES配置项
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'python',
'USER': 'root',
'PASSWORD': 'zy199909237412',
'HOST': '127.0.0.1',
'POST': '3306',
}
}
3.在pycharm的右侧栏点击database或者做下角点击database,连接数据库,如果都没有,则去pluging里面寻找是否装了database插件!!
或者
4.输入需要连接的数据库
5.这里没有下载驱动的需要先下载,不然连接不上
6.在django项目中的__init__.py中导入pymysql,告诉django使用pymysql连接数据库,而不是mysqldb模块
import pymysql
pymysql.install_as_MySQLdb()
7.简单使用pycharm操作数据库
2.django操作数据库(ORM)
2.1 ORM简介
对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。ORM在业务逻辑层和数据库层之间充当了桥梁的作用
简单来说,ORM就是使用面向对象的方式来操作数据库!
(1)ORM的优势:
1.将表和类一一对应,类的每个实例对应表中的一条记录,类的每个属性对应表中的每个字段。
2.ORM提供了对数据库的映射,不用直接编写SQL代码,只需操作对象就能对数据库操作数据,提高了工作效率
(2)ORM的劣势
1.一些查询操作完成不了
2.一定程度上牺牲了程序的执行效率
3.用久了会忘SQL语言
2.2 创建表和字段
from django.db import models # 1.导入models模块
# 2.定义一个类继承models.Model类(创建一张项目名(应用名)_类名的表)
class Book(models.Model):
# 3.定义类属性title和price(对应是创建字段名、字段类型和字段选项)
title = models.CharField('书名', max_length=50, default='')
price = models.DecimalField('价格', max_digits=7, decimal_places=2)
class 模型类名(models.Model):
字段名 = models.字段类型(字段选项)
在类里面不指定主键字段,django会帮您自动创建一个id主键!
注意:对于数据库的增删改查之后都要进行数据库迁移
1.执行python3 manage.py makemigration---将应用下的model.py文件生成一个中间件文件,并保存在migrations文件中
2.执行python3 manage.py migrate ----将每个应用下的migrations目录中的中间文件同步回数据库
2.3 字段的增删改查
1.增加字段
直接在对应的类里面添加字段、字段类型和字段选项
注意增加的字段都需要指定default = '' 或者null = True,如下添加info字段:
info = models.CharField(max_length=32,default='',null=True)
2.修改字段
直接在类里面修改对应字段就行!
然后执行数据库迁移命令!
3.字段的删除
直接在类里面将需要删的字段注释掉就行!
然后执行数据库迁移命令!
注意:执行完毕了,数据库对应的数据就没有了!慎重!!!
2.4 单表数据的增删改查
每个继承model.Model的模型类,都有一个objects对象被同样继承下来,这个对象叫管理器对象,数据库的增删改查都是通过模型的管理器实现。
对于数据的增删改查,其实最主要的都是在views.py的视图函数里完成的!因此,我们需要在视图函数里进行数据库的增删改查操作!
2.4.1单表数据的查询
1.filter()查询方法
from app01 import models # 1.导入自己创建的模型类
# 2.filter查询方法,语法是:models.类名.objects.filter(),filter内可以查询多个参数,默认是and连接,等于SQL语法的where方法!,不传参时代表查所有
res = models.MyModle.objects.filter(username=username)
# 该方法有一个返回值,返回的是一个queryset对象,该对象可以看成是列表套字典的形式,列表里面套着一个个数据对象,形式为:[数据对象1,数据对象2]
# queryset列表也支持索引,切片操作,但是不支持负数索引,可以当成列表套字典的形式for循环
user_obj = res[0] # 得到的是具体的数据对象,但是官方不推荐使用索引的方式取到具体的数据对象,它推荐的是res.first()方法取到列表里的第一个数据对象!
print(user_obj.username) # 通过点大法,即.属性的方法得到具体的值
2.all()查询所有方法
# all方法查询User表的所有数据,返回的是一个queryset对象列表,
user_queryset = models.User.objects.all()
2.4.2单表数据的增加
1.create()方法
# create增加数据方法,语法是:models.类名.objects.create()
res = models.MyModle.objects.create(username=username,password=password)
# 该方法也有一个返回值,返回的是当前这个数据对象
print(res.username,res.password) # 可以通过点属性的方法,查看到对应属性的值
2.save()方法
obj = models.User(username=username,password=password)
obj.save() # 保存数据到数据库
2.4.3单表数据的修改
1.upadte()方法:先查出来,在更新
# 查出id为什么的对象,然后进行批量更新。filter可以查所有,也可以查具体
models.User.objects.filter(id=edit_id).update(username=username,password=password)
2.赋值+save()方法
obj = models.User.objects.filter(id=edit_id).first() #拿到待修改的对象
obj.username = username # 采用给该对象属性重新赋值的方法修改数据
obj.password = password
obj.save() # 最后赋值完记得需咬保存
2.4.4单表数据删除
1.delete()方法:用于批量删除
# 先filter查出需要删除的对象,然后.delete()方法
models.User.objects.filter(id=delete_id).delete() # 这里是把filter查出来的queryset对象里全部删除了,有几个删几个。
# 这里的id可以改写成pk,用了pk就不需要知道表的主键是id还是其他什么了!
2.单一删除
res = models.User.objects.filter(id=delete_id).first()
res.delete() # 单一删除
2.4.5补充13条单表查询
# 1.all() 查询所有数据
# 2.filter() 带有过滤条件的查询,拿到的是一个queryset对象列表
# 3.get() 直接拿数据对象 但是条件不存在直接报错
# 4.first() 拿queryset里面第一个元素
# res = models.User.objects.all().first()
# print(res) # 拿到queryset对象列表里的第一个数据对象
# 5.last()
# res = models.User.objects.all().last()
# print(res) # 同上,拿到的是最好一个
# 6.values() 可以指定获取的数据字段 select name,age from ...
# res = models.User.objects.values('name','age')
# print(res) # 结果为:列表套字典<QuerySet [{'name': 'jason', 'age': 18}, {'name': 'egonPPP', 'age': 84}]>
# 7.values_list()
# res = models.User.objects.values_list('name','age')
# print(res) # 结果为:列表套元祖,<QuerySet [('jason', 18), ('egonPPP', 84)]>
# 8.query
# print(res.query) # 查看内部封装的sql语句
# 上述查看sql语句的方式 只能用于queryset对象
# 只有queryset对象才能够点击query查看内部的sql语句
# 9.distinct() 去重
# res = models.User.objects.values('name','age').distinct()
# print(res)
"""
去重一定要是一模一样的数据
如果带有主键那么肯定不一样 你在往后的查询中一定不要忽略主键
"""
# 10.order_by()
# res = models.User.objects.order_by('age') # 默认升序
# res = models.User.objects.order_by('-age') # 降序
# print(res)
# 10.reverse() 反转的前提是 数据已经排过序了 order_by()
# res = models.User.objects.all()
# res1 = models.User.objects.order_by('age').reverse()
# print(res,res1)
# 11.count() 统计当前数据的个数
# res = models.User.objects.count()
# print(res)
# 12.exclude() 排除在外
# res = models.User.objects.exclude(name='jason')
# print(res)
# 13.exists() #基本用不到因为数据本身就自带布尔值 返回的是布尔值
# res = models.User.objects.filter(pk=10).exists() # 判读主键为10的是否存在,返回是布尔值
# print(res)
2.4.6 神奇的双下划线查询
__exact:等值查询
__contains:包含指定值--区分大小写 a2=Book.objects.filter(name____contains=‘n’) 查询出名字里包含n的
__icontains:包含指定值--忽略大小写
__startwith:以xxx开始
__endwith:以xxx结尾
__gt:大于指定值,例如:a2=Book.objects.filter(id__gt=3)
__gte:大于等于
__it:小于
__ite:小于等于
__in:查找数据是否在指定范围内 a2=Book.objects.filter(id__in=[1,3,5])
__range:查询数据是否在指定区间范围内 a2=Book.objects.filter(id__range=[1,5]) 查询出id在1-5的收尾都要
a2=Book.objects.filter(register_time__month='1'):查询出月份是1月的数据
a2=Book.objects.filter(register_time__year='2022'):查询出年份在2022的数据
2.5 多表数据操作
2.5.1 orm创建表关系
表的关系有三种,分别是:一对一、一对多、多对多
判断表和表之间的关系:换位思考法
具体创建表关系语法:
"""
图书和出版社:一对多关系,外键建在多的一方
图书和作者:多对多关系,外键建在任何一方,但是推荐建在查询频率高的一方
作者和作者详情:一对一关系,外键建在任何一方,但是推荐建在查询频率高的一方
"""
class Book(models.Model):
title = models.CharField(verbose_name='书名',max_length=32)
price = models.DecimalField(max_digits=8,decimal_places=2)
create_time = models.DateTimeField(auto_now_add=True)
# 一对多外键建在多的一方,to='是需要建立外键的那一个类名publish'
# 注意:在django2、3里面需要指定级联删除参数on_delete=models.CASCADE
publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE)
# 多对多外键建在查询频率高的一方,多对多在sql语句中需要自己手动建第三张表,但是在django中,django遇到会自动帮你创建第三张表!
"""
多对多创建第三张表有三种方式:
1.全自动:无法添加字段,扩展性不高!
author = models.ManyToManyField(to='Author')
2.手动:自己创建第三方表,然后关联两个外键,需要写的代码多,复杂!!
class Book2Author(models.Model):
book_id = models.ForeignKey(to='Book')
author_id = models.ForeignKey(to='Author')
3.半自动(推荐,扩展性高)
author = models.ManyToManyField(to='Author',through='Book2Author',through_fields=('author','book'))
"""
"""
through_fields字段先后顺序
判断的本质:
通过第三张表查询对应的表 需要用到哪个字段就把哪个字段放前面
你也可以简化判断
当前表是谁 就把对应的关联字段放前面
半自动:可以使用orm的正反向查询 但是没法使用add,set,remove,clear这四个方法
"""
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=64)
email = models.EmailField()
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
# 一对一外键建在查询频率高的一方,需要指定on_delete
author_detail = models.OneToOneField(to='AuthorDetail',on_delete=models.CASCADE)
class AuthorDetail(models.Model):
phone = models.BigIntegerField()
addr = models.CharField(max_length=64)
2.5.2 一对多关系的增删改
from app01 import models
# 1.增
# 法1:直接写book表里面的外键的实际字段名,然后指定关联publish的id就行
models.Book.objects.create(title='活着',price=33,publish_id=1)
# 法2:虚拟字段,传入一个具体的publish的数据对象就行
publish_obj = models.Publish.objects.filter(pk=2).first()
# 注意这里需要.first()一下,因为不点拿到的是queryset对象,点一下拿到queryset里的具体数据对象
models.Book.objects.create(title='我',price=555,publish=publish_obj)
# 2.删
models.Publish.objects.filter(pk=2).delete() # 将book里关联id=2的也全部删除
# 3.改
# 法1:update里面传具体需要修改的外键字段名和值
models.Book.objects.filter(pk=2).update(publish_id=2) # 将id为2的书的关联publish_id改成2
# 法2:update里面传入虚拟字段publish=需要关联的publish数据对象,同增的法2
2.5.3 多对多关系的增删改
# 1.增
# 一本书对应多个作者,先查出这个书的对象,在通过对象.多对多字段名.add()方法关联作者id
# add()方法里面可以法数字1,2,3,表示关联作者的主键值;还可以放具体的作者数据对象!!
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.author.add(1,2)
author_obj = models.Author.objects.filter(pk=1).first()
book_obj.author.add(author_obj)
# 2.删
# 删除这边书关联的作者id为1和2的,和add方法一样也支持里面放具体的作者数据对象!!
book_obj.author.remove(1,2)
# 3.改
# set方法修改该书关联的作者id为1和3,set方法和add方法一样也支持里面放具体的作者数据对象!!
# 注意:set方法里面必须放一个可迭代对象,比如列表!
book_obj.author.set([1,3])
# 4.清空当前书和作者的对应关系
book_obj.author.clear()
2.5.4 多表的查询操作
查询的时候,分正方向查询,外键字段在我手上,我查你就是正向查询;反之,不在我手上,我查你就是反向查询。
查询口诀:正向查询按字段,反向查询按表名小写;
1.基于对象的多表查询
from app01 import models
# 1.查询书籍主键为1的出版社名称 ----正向一对多查询
book_obj = models.Book.objects.filter(pk=1).first() # 先得到主键为1的书籍对象
res = book_obj.publish # 正向查询按字段,Book表里面有一个publish字段,返回一个与主键1关联的出版社对象
print(res)
print(res.name) # 对象.的方法查询具体的字段属性
print(res.addr)
# 2.查询书籍主键为1的作者----正向多对多查询
book_obj1 = models.Book.objects.filter(pk=1).first()
res = book_obj1.author.all() # 不.all()返回的是一个app01.Author.None
print(res) # .all()返回的是<QuerySet [<Author: Author object (1)>, <Author: Author object (2)>]>,书籍id为1对应两个作者
print(res.first().name) # 通过.first().name 方式获取作者的具体信息
# 3.查询作者王的电话 ----正向一对一查询
author_obj = models.Author.objects.filter(name='王').first()
res = author_obj.author_detail
print(res) # 返回的是AuthorDetail object (1),是一个作者详情对象
print(res.phone) # .字段查出对应作者的详情信息
"""
总结:正向查询时,当你的结果可能是多个的时候,就需要加.all(),如果是一个直接拿到数据对象
"""
# 4.查询出版社是东风出版社的书----反向一对多查询
publish_obj = models.Publish.objects.filter(name='东风').first()
res = publish_obj.book_set.all()
print(res)
# 5.查询作者是王写过的书----反向多对多查询
author_obj = models.Author.objects.filter(name='王').first()
res = author_obj.book_set.all()
print(res)
# 6.查询电话号码是122324233的作者
author_detail_obj = models.AuthorDetail.objects.filter(phone=122324233).first()
res = author_detail_obj.author
print(res.name)
"""
总结:反向查询的时候,如果结果为多个,就需要加_set.all();如果是一对一结果就一个,就不用!
"""
2.基于双下划线__的多表查询
from app01 import models
# 1.查询王这个作者的年龄和手机号--正向一对一查询
# 先得到王这个对象,然后.values,里面放需要查询的字段名,正向则直接'字段名';反向则'表名小写__字段名'
res = models.Author.objects.filter(name='王').values('age','author_detail__phone')
print(res) # 得到的是一个queryset对象(列表里套了一个字典)
dict = res.first() # .first()方法取到该字典对象
print(dict['age']) # 字典方式取到具体需要的值
# 反向查询
res = models.AuthorDetail.objects.filter(author__name='王').values('phone','author__age')
print(res)
# 2.查询书籍主键为1的出版社名字和书的价格---正向一对多查询
res = models.Book.objects.filter(pk=1).values('price','publish__name')
print(res)
# 反向查询
res = models.Publish.objects.filter(book__id=1).values('name','book__price')
print(res)
# 3.查询书籍主键为1的作者姓名和书籍名称
res = models.Book.objects.filter(pk=1).values('title','author__name')
print(res)
# 反向查询
res = models.Author.objects.filter(book__id=1).values('name','book__title')
print(res)
# 4.终极大招:查询书籍主键为1的作者的电话号码!!!---跨了book、author、author_detail三张表查询
res =models.Book.objects.filter(pk=1).values('author__author_detail__phone')
print(res)
# 反向查询
res = models.Author.objects.filter(book__id=1).values('author_detail__phone')
print(res)
2.6 字段类型与字段选项
from django.db import models
# Create your models here.
class MyBook(models.Model):
# 1.字符串字段类型CharField,必须传的字段选项是max_length=指定最大字符数,verbose_name=''指定该字段在django后台管理中的描述名
name = models.CharField(max_length=32,verbose_name='姓名')
# 2.数字字段类型IntegerField
age = models.IntegerField()
# 3.日期时间字段类型DateTimeField
register_time = models.DateTimeField(auto_now_add=True)
# 4.日期字段类型
register_time = models.DateField(auto_now_add=True)
# 针对这两个字段类型,有两个关键性参数
# auto_now:每次操作数据的时候,该字段会自动将当前时间更新
# auto_now_add:在创建数据的时候会自动将当前时间记录下来,以后只要不认为修改就一直不变
# 5.邮箱字段
email = models.EmailField()
# 6.大数字字段类型
phone = models.BigIntegerField()
# 7.小数字段,有两个字段选项max_digits=8,表示连小数一共8位;decimal_places=2,表示小数部分2位。
price = models.DecimalField(max_digits=8,decimal_places=2)
# 8.布尔型字段,传入参数是FalseTrue,在数据库中对应01
boolean = models.BooleanField(False)
# 9.文本字段类型,没有字数限制,大文本
text = models.TextField()
# 10.文件字段类型,upload_to=''参数:给该字段传一个文件对象,会自动将该文件保存在/data目录下,如何把该文件的路径传到数据库中
file = models.FileField(upload_to='/data')
# 11.choices字段参数
只要某个字段的可能性是可以列举完全的,那么一般情况下都会采用choices参数
gender_choices = (
(1,'男'),
(2,'女'),
(3,'其他'), #可以存数字1,2,也可以存字母等等对应
)
gender = models.IntegerField(choices=gender_choices)
"""
该gender字段存的还是数字 但是如果存的数字在上面元祖列举的范围之内
那么可以非常轻松的通过固定写法 get_字段名_display()方法获取到数字对应的真正的内容;
如果不在范围之内,存的时候 没有列举出来的数字也能存,取的时候还是取出存的时候的数字。
print(user_obj.get_gender_display())
"""
# 12 级联关系
# 作者没了,详情也没:on_delete=models.CASCADE
# 出版社没了,书还是那个出版社出版:on_delete=models.DO_NOTHING
# 部门没了,员工没有部门(空不能):null=True, on_delete=models.SET_NULL
# 部门没了,员工进入默认部门(默认值):default=0, on_delete=models.SET_DEFAULT
# 13 db_constraint = False
# db_constraint = False表示逻辑上的关联,实际上没有外键联系,增删不会受外键影响,orm查询也不受影响,
# 不设置该参数,你想删出版社是不行的,因为出版社和书有关系;当删了出版社,书表中可能就会出现脏数据,就是书对应的出版社信息值不存在
# 14 related_name字段
基于对象的跨表查询反向操作时,使用的字段名,用于代替原代替原分向查询的‘表名_set’。
# 15 related_query_name字段
基于双下划线的查询反向操作时...
自定义字段暂略
2.7聚合查询
聚合函数通常是和分组一起使用的,关键字.aggregate()方法
# 先导入五个聚合函数
"""
小技巧:只要跟数据库相关的模块,基本上都在django.db.models里面
如果没有则可能在django.db里面
"""
from django.db.models import Sum,Avg,Min,Max,Count
# 1.求书这个表里面的价格平均值和总合和最大值...单独使用需要使用.aggregate方法
res = models.Book.objects.aggregate(Avg('price'),Max('price'),Sum('price'))
print(res)
返回的格式是:
2.8分组查询
分组查询关键字annotate
# 1.统计每本书的作者个数
res = models.Book.objects.annotate(author_num=Count('author')).values('author_num')
print(res)
"""
说明:
1.分组查询的关键字是annotate
2.models后面点什么,就是按什么分的组
3.author_num是自己自定义的字段,用来存统计出来的每本书对应的个数
4.count里面的author指的是统计作者的个数
5.values('author_num')是取出统计出来的作者个数
"""
# 2.统计每个出版社卖的最便宜书的价格
res = models.Publish.objects.annotate(book_price=Min('book__price')).values('name','book_price')
print(res)
print('=========')
# 3.统计作者个数不止一个的图书
# 先按图书分组,求出图书对应的作者个数;再filter过滤出作者个数大于1的
res = models.Book.objects.annotate(author_num=Count('author')).filter(author_num__gt=1).values('title','author_num')
print(res)
# 4.查询每个作者出的书的总价格
res = models.Author.objects.annotate(book_price=Sum('book__price')).values('name','book_price')
print(res)
"""
那么如何按照字段分组呢?
models.Book.objects.values('price').annotate()
# 如果annotate前面出现了values,则它将不在按照book分组,而是按照values分组
"""
2.9F与Q查询
2.9.1F查询
# F查询
# 1.查询卖出数大于库存数的书籍
# F查询
"""
能够帮助你直接获取到表中某个字段对应的数据
"""
from django.db.models import F # 导入f模块
# res = models.Book.objects.filter(maichu__gt=F('kucun')) #f括号里放的是对应的字段名
# print(res)
# 2.将所有书籍的价格提升500块
# models.Book.objects.update(price=F('price') + 500)
# 3.将所有书的名称后面加上爆款两个字
"""
在操作字符类型的数据的时候 F不能够直接做到字符串的拼接
"""
from django.db.models.functions import Concat
from django.db.models import Value # 先导入Concat和Value模块
models.Book.objects.update(title=Concat(F('title'), Value('爆款')))
# models.Book.objects.update(title=F('title') + '爆款') # 所有的名称会全部变成空白
2.9.2Q查询
# Q查询
# 1.查询卖出数大于100或者价格小于600的书籍
# res = models.Book.objects.filter(maichu__gt=100,price__lt=600)
"""filter括号内多个参数是and关系,并不能得到结果"""
from django.db.models import Q # 导入q模块,实现或和not功能
# res = models.Book.objects.filter(Q(maichu__gt=100),Q(price__lt=600)) # Q包裹逗号分割 还是and关系
# res = models.Book.objects.filter(Q(maichu__gt=100)|Q(price__lt=600)) # | or关系
# res = models.Book.objects.filter(~Q(maichu__gt=100)|Q(price__lt=600)) # ~ not关系
# print(res) # <QuerySet []>
# Q的高阶用法 能够将查询条件的左边也变成字符串的形式
q = Q()
q.connector = 'or'
q.children.append(('maichu__gt',100))
q.children.append(('price__lt',600))
res = models.Book.objects.filter(q) # 默认还是and关系
print(res)
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:django中操作mysql数据库 - Python技术站