1 .什么是ORM (Object Relational Mapping )
它的作用是在关系型数据库和业务实体对象做一个映射,我们在操作具体业务对象的时候就可以省去了和SQL语句打交道,只需要简单的操作对象的属性和方法。
我们在model这个模块写的类名相当于数据库中的表名
根据这个类创建的对象相当于数据库表中的一个字段
字段名.id ,字段名2.name 表示这个字段对对应的数据
模型与模型之间的关系
1.多对一关系
一对多
举个列子一个知识对应多个流程,这里就是一对多的关系,通过相应的表来来建立一个联系,用ForeignKey来实现。在流程表引入知识对象。
class Knowledge(BaseModel):
# 知识对象
knowledge_name = models.CharField(u'知识名称', max_length=100)
user_id = models.ForeignKey(User, null=True, on_delete=models.CASCADE)
class WorkFlow(BaseModel):
# 知识流程审核
process_status = models.CharField(u'流程状态', max_length=36)
business_id = models.ForeignKey(Knowledge, on_delete=CASCADE)
通过知识表查process_status时结束的知识,
查询方式(表名称全小写__字段名称)
know=Knowledge.objects.filter(workflow__ process_status ='结束') #查询语句查询
process_status=know.workflow_set.filter(process_status ='结束') # 对象查询
补充:
know.workflow_set.all() #查询当前知识对象的所有流程状态对象
know.workflow_set.values('process_status') # 查询当前知识对象的所有流程状态和值(字典形式)
多对一
多个知识对应一个用户,通过相应的表来来建立一个联系,用ForeignKey来实现,知识表内存用户对象。
class Knowledge(BaseModel):
# 知识对象
knowledge_name = models.CharField(u'知识名称', max_length=100)
user_id = models.ForeignKey(User, null=True, on_delete=models.CASCADE)
class User(AbstractUser):
#用户
name = models.CharField(max_length=90, blank=True, null=True, verbose_name='用户真实姓名'))
查name,查询方式(外键关联字段名称__字段名称)
Knowledge.objects.filter(user_id__name=‘毛毛’)
2.一对一
3.多对多
class Knowledge(BaseModel):
# 知识对象
knowledge_name = models.CharField(u'知识名称', max_length=100)
user_id = models.ForeignKey(User, null=True, on_delete=models.CASCADE)
class KnowledgeTag(BaseModel):
# 知识标签
tag_name = models.CharField(u'标签名称', max_length=200)
class KnowledgeTagToKnowledge(BaseModel):
# 知识对知识标签中间表
tag_id = models.ForeignKey(KnowledgeTag, on_delete=CASCADE)
knowledge_id = models.ForeignKey(Knowledge, on_delete=CASCADE)
查 tag_name,查询方式(表名称全小写__字段名称__字段名称)
查询语句:
knowledges = Knowledge.objects.filter(
workflow__process_status='结束',
knowledgeindexconfigure__organization_name=site_name,
user_id__name='毛毛',
knowledgetagtoknowledge__tag_id__ tag_name='毛毛',
**condition).order_by(
'-create_time')
总结:
- 在当前表内的外键,查询方式:外键关联字段名称__字段名称(查指示表用户姓名:user_id__name=‘毛毛’)
- 当前表的外键在其他表里,查询方式:表名称全小写__字段名称(查流程结束的知识:workflow__process_status=‘结束’)
- 两者可以混着用。即获取对象的方式,通过当前表查询,在当前表中的外键,可通过外键关联字段名称直接获取;当前表在其他表中作为外键关联,则需其他表名称全小写,即可获取其他表的对象。
一对多:
1.知识id在流程表中,外键关联,查状态是’‘结束’'的知识
表:
class Knowledge(BaseModel):
# 知识对象
knowledge_name = models.CharField(u'知识名称', max_length=100)
user_id = models.ForeignKey(User, null=True, on_delete=models.CASCADE)
class WorkFlow(BaseModel):
# 知识流程审核
process_status = models.CharField(u'流程状态', max_length=36)
business_id = models.ForeignKey(Knowledge, on_delete=CASCADE)
查询方式(外键关联查询:’‘表名称全小写__字段名称’’)
knowledges = Knowledge.objects.filter(
workflow__process_status='结束',
)
2.不同区域关联部分知识
class KnowledgeIndexConfigure(BaseModel):
# 区域首页推荐文档配置
knowledge_id = models.ForeignKey(Knowledge, on_delete=CASCADE)
organization_name = models.CharField(u'专用站点名称', max_length=100, null=True, blank=True)
查询方式(外键关联查询:’‘表名称全小写__字段名称’’)
区域关联的文档:
knowledges = Knowledge.objects.filter(
knowledgeindexconfigure__organization_name=site_name
)
区域未关联的文档:
knowledges = Knowledge.objects.exclude(
knowledgeindexconfigure__organization_name=site_name
)
3.知识关联的用户姓名
表:
class Knowledge(BaseModel):
# 知识对象
knowledge_name = models.CharField(u'知识名称', max_length=100)
user_id = models.ForeignKey(User, null=True, on_delete=models.CASCADE)
class User(AbstractUser):
#用户
name = models.CharField(max_length=90, blank=True, null=True, verbose_name='用户真实姓名'))
查询方式(外键关联字段名称__字段名称)
Knowledge.objects.filter(user_id__name__icontains=keyword)
源自
knowledges_keyword = Knowledge.objects.filter(Q(knowledge_number__icontains=keyword)
| Q(knowledge_name__icontains=keyword)
| Q(user_id__name__icontains=keyword),
workflow__process_status='结束')
4.查询用户创建的所有知识对象
查询方式(表名称全小写_set.all())
user为用户对象
user. knowledge_set.all()
#后可以直接用方法:user. knowledge_set.all().values('id')
也可再次过滤:knowledges=user.knowledge_set.filter(article_type=0)
if knowledges:
info=knowledges.values('id')[0].get('id')
或者反过来,查询知识的用户姓名:
know为知识对象
know.user_id.name
查询问题的所有原因对象
class DocumentProgrammer(BaseModel):
# 文档问题对象
issue_number = models.CharField(u'问题编号', max_length=200) #格式为:iss-20190821001
issue_name = models.CharField(u'问题名称', max_length=200)
class IssueCause(BaseModel):
# 问题原因
issue_id = models.ForeignKey(DocumentProgrammer, on_delete=CASCADE)
#issue为问题对象
issue.issuecause_set.all()
反过来查询,查原因对应的问题name:
cause为原因对象
cause.issue_id.issue_name
5.多条件查询
condition = {}
# 时间
line1 = ''
line2 = ''
if start_end != '':
line1 = start_end.split('*')[0] + ' 00:00:00'
line2 = start_end.split('*')[1] + ' 23:59:59'
if line1 or line2:
# condition.update(
# {'workflow__process_end_time__gte': datetime.datetime.strptime(line1, '%Y/%m/%d %H:%M:%S')})
# condition.update(
# {'workflow__process_end_time__lte': datetime.datetime.strptime(line2, '%Y/%m/%d %H:%M:%S')})
condition.update({'create_time__gte': datetime.datetime.strptime(line1, '%Y/%m/%d %H:%M:%S')})
condition.update({'create_time__lte': datetime.datetime.strptime(line2, '%Y/%m/%d %H:%M:%S')})
组织
organization_codes_list = organization_codes.split(',')
organization_ids = tuple(organization_codes_list)
if len(organization_ids) == 1:
organization_ids = "'" + organization_ids[0] + "'"
sql = """select a.c_org_code from t_orgination a
where a.C_STATUS='1' start with a.c_org_code in %s
connect by prior a.c_org_code = a.c_org_parent_code""" % (organization_ids,)
knowledges_org = exe_sql_all(sql, db_name=RDKM_DB)
for k2 in knowledges_org:
org_list.append(str(k2[0]))
if organization_id_list:
condition.update({'organization_id__in': organization_id_list})
表
class Knowledge(BaseModel):
# 知识对象
knowledge_name = models.CharField(u'知识名称', max_length=100)
user_id = models.ForeignKey(User, null=True, on_delete=models.CASCADE)
class KnowledgeTag(BaseModel):
# 知识标签
tag_name = models.CharField(u'标签名称', max_length=200)
class KnowledgeTagToKnowledge(BaseModel):
# 知识对知识标签中间表
tag_id = models.ForeignKey(KnowledgeTag, on_delete=CASCADE)
knowledge_id = models.ForeignKey(Knowledge, on_delete=CASCADE)
查询方式(表名称全小写__字段名称__字段名称)
Knowledge.objects.filter(knowledgetagtoknowledge__tag_id__tag_name=keyword)
查询
all_know = Knowledge.objects.filter(Q(knowledge_number__icontains=keyword)
| Q(knowledge_name__icontains=keyword)
| Q(user_id__name__icontains=keyword)
| Q(knowledgetagtoknowledge__tag_id__tag_name=keyword),
workflow__process_status='结束',
**condition
).order_by('-create_time')
6.查询问题的原因的方案
表:
class DocumentProgrammer(BaseModel):
# 文档问题对象
issue_number = models.CharField(u'问题编号', max_length=200) #格式为:iss-20190821001
issue_name = models.CharField(u'问题名称', max_length=200)
issue_desc = models.CharField(u'问题描述', max_length=500)
class IssueCause(BaseModel):
# 问题原因
issue_id = models.ForeignKey(DocumentProgrammer, on_delete=CASCADE)
cause_number = models.CharField(u'原因编号', max_length=100)
cause_description = models.TextField(u'原因描述')
class IssueSolution(BaseModel):
# 问题解决方案
solution_number = models.CharField(u'解决方案编号', unique=True, max_length=50)
solution_description = models.TextField(u'解决方案描述')
cause_id = models.ForeignKey(IssueCause, on_delete=CASCADE)
查询(表名称全小写__表名称全小写____字段名称)
DocumentProgrammer.objects.filter(issuecause__issuesolution__solution_description__contains=keyword)
源自
issues = DocumentProgrammer.objects.filter(Q(issue_number__contains=keyword)
| Q(issue_name__contains=keyword)
| Q(create_time__contains=keyword)
| Q(user_id__name__contains=keyword)
| Q(issue_desc__contains=keyword)
| Q(issue_description__contains=keyword)
| Q(issuecause__cause_description__contains=keyword)
|Q(issuecause__issuesolution__solution_description__contains=keyword),
Q(Q(issue_change=''),
workflow__process_status='结束')
).order_by('create_time')
total = len(issuelist)
issues = Paginate(page=current_page, count=page_count).data( issues)
关系数据库本身没多多对多的关系,Django是在数据库中新建了一张表专门用来存储多对多的关系。
记得引用的ForeignKey和ManyToManyField一定要在Author的后面,如果不在后面要将参数写成字符串形式(‘Author’)。并且ForeignKey和ManyToManyField这段关系在两个类里面都可以声明,作用是一样的,django会自动为我们把另外一半关系给对称过去。
聚合查询
aggregate()是QuerySet 的一个终止子句,它返回一个包含一些键值对的字典。
键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。
from django.db.models import Avg, Sum, Max, Min, Count
Integral_source.objects.all().aggregate(Avg("a"))
#{'price__avg': 13.233333}
ret = Integral_source.objects.aggregate(Sum("a"))
#{'price__sum': Decimal('13.10')
ret =Integral_source.objects.aggregate(total_price=Sum("a"))
#{'total_price': Decimal('13.10')}
ret = Integral_source.objects.aggregate(avg_price=Avg("a"), max_price=Max("a"), min_price=Min("price"))
#{'avg_price': 4.366667, 'max_price': Decimal('12.00'), 'min_price': Decimal('0.10')}
分组查询
单表查询分组:
一、统计每个人的总积分
SELECT create_user_id, sum(a) as a
FROM g_Integral_source
GROUP BY create_user_id order by a desc
orm查询:
users1 = Integral_source.objects.values('create_user_id').annotate(
total=Sum('a')).order_by('-total').values('create_user_id','create_user_id__name','total')
result['data']=list(users1)
二、求按年度分组目标
sql:
select effective_time from t_objectives
where user_id=%s
group by effective_time ORDER BY effective_time
orm
Objectives.objects.filter(user_id=user_id).values('effective_time').annotate(
count=Count('effective_time'))
连表查询的分组:按照部门分组求平均工资
select dept.name,AVG(salary) from employee inner join dept on (employee.dept_id=dept.id) group by dept_id;
ORM查询:
from django.db.models import Avg
Dept.objects.annotate(avg=Avg("employee__salary")).values("name", "avg")
Employee.objects.values("dept__name").annotate(avg=Avg("salary"))
#<QuerySet [{'dept__name': '垃圾部', 'avg': 221.0}, {'dept__name': '保安部', 'avg': 21.0}, {'dept__name': '教学部', 'avg': 999.0}]>
Employee.objects.values("dept__name").annotate(avg=Avg("salary")).values('dept', "avg")
#<QuerySet [{'dept__name': '垃圾部', 'avg': 221.0}, {'dept__name': '保安部', 'avg': 21.0}, {'dept__name': '教学部', 'avg': 999.0}]>
Employee.objects.values("dept__name")
#<QuerySet [{'dept__name': '垃圾部'}, {'dept__name': '保安部'}, {'dept__name': '教学部'}]>
F查询
F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。
商品表结构:
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=6, decimal_places=2)
# 库存数
keep = models.IntegerField()
# 卖出数
sale = models.IntegerField()
def __str__(self):
return "{}:{}:{}:{}".format(self.name, self.price, self.keep, self.sale)
示例:
from django.db.models import F
1.查询出卖出数大于库存数的商品
models.Product.objects.filter(sale__gt=F("keep"))
#<QuerySet [<Product: 跟哪吒学诗歌:59.00:50:10000>, <Product: 跟苑局学三不:55.00:100:200>]>
2.Django支持F()对象之间以及F()对象和常数之间的加减乘除和取模的操作
models.Product.objects.filter(sale__gt=F('keep')*2)
#<QuerySet [<Product: 跟哪吒学诗歌:59.00:50:10000>]>
3.修改操作也可以使用F函数:比如将每个产品的价格提高50元
models.Product.objects.all().update(price=F("price")+50)
4.把所有商品名后面加上"新款"
from django.db.models.functions import Concat
from django.db.models import Value
models.Product.objects.all().update(name=Concat(F("name"), Value("新款")))
Q查询
filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果需要执行更复杂的查询(例如OR语句),可以使用Q对象
#卖出数大于100 并且 价格大于100块的
models.Product.objects.filter(sale__gt=100, price__gt=100)
#<QuerySet [<Product: 跟哪吒学诗歌新款:259.00:50:10000>, <Product: 跟苑局学三不新款:255.00:100:200>]>
示例:
from django.db.models import Q
1.查询卖出数大于100或者价格小于100的
models.Product.objects.filter(Q(sale__gt=100)|Q(price__lt=100)) #|:或
#<QuerySet [<Product: 跟哪吒学诗歌新款:259.00:50:10000>, <Product: 跟苑局学三不新款:255.00:100:200>]>
2.查询库存数是100并且卖出数不是0的产品
models.Product.objects.filter(Q(keep=100)&~Q(sale=0)) #&:与,~:非
models.Product.objects.filter(Q(kucun=100),~Q(maichu=0))
#<QuerySet [<Product: 跟苑局学三不新款:255.00:100:200>]>
models.Product.objects.filter(Q(kucun=100)&~Q(maichu=0)).values('name')
#<QuerySet [{'name': '跟苑局学三不新款'}]>
models.Product.objects.filter(Q(kucun=100)&~Q(maichu=0)).values_list('name')
#<QuerySet [('跟苑局学三不新款',)]>
查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面
#查询产品名包含新款, 并且库存数大于60或者价格小于100的产品
models.Product.objects.filter(Q(keep__gt=60)|Q(price__lt=100), name__contains="新款")
#<QuerySet [<Product: 跟苑局学三不新款:255.00:100:200>]>
12.3215 事务
开启一个事务可以包含一些sql语句,这些sql语句要么同时成功,要么都不成功,称之为事务的原子性 作用:事务用于将某些操作的多个SQL作为原子性操作,一旦有某一个出现错误,即可回滚到原来的状态,从而保证数据库数据完整性。
import os
if __name__ == '__main__':
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings")
import django
django.setup()
import datetime
from app01 import models
try:
from django.db import transaction
with transaction.atomic(): #开启事务
new_publisher = models.Publisher.objects.create(name="火星出版社")
models.Book.objects.create(title="橘子物语", publish_date=datetime.date.today(), publisher_id=10)
# 指定一个不存在的出版社id,上一行创建一条出版社数据被回滚,数据库并未创建新数据
except Exception as e:
print(str(e))
12.3216 Django ORM执行原生SQL
很多情况下我们不需要将查询结果映射成模型,或者我们需要执行DELETE、 INSERT以及UPDATE操作,在这些情况下,我们可以直接访问数据库,完全避开模型层。我们可以直接从django提供的接口中获取数据库连接,然后像使用pymysql模块一样操作数据库
from django.db import connection, connections
cursor = connection.cursor() # cursor = connections['default'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1])
ret = cursor.fetchone()
ORM 中的CRUD操作
Create:
方法一:
task = {'a':'b'}
Task.object.create(**task)
这种方法的好处是可以直接把字典存到数据库里面
方法二:
task = Task(title='明天吃饭吗')
task.save()
Retrieve(检索)
res = Task.objects.all()
res = Task.objects.filter(id=0) #对数据进行过滤,可以查询某一段数据
for row in res:
print(row.id,row.title) #不要直接将取到的所有数据进行for遍历查找用到,比如用到filter过滤,否则数据大服务器会蹦掉
filter(查询条件)
res = Task.objects.filter(id__gt=2) #查询id大于2
all_knowledges = Knowledge.objects.filter(id__in=knowledges_id_list).order_by(
'-create_time') #id在某个id列表内
使用filter进行登录验证
user= UserInfo.objects.filter(username=v,userpwd=p).first()
if not user:
print('登录失败')
else:
print('登录成功')
含义 后缀
大于 __gt
小于 __lt
大于等于 __gte
小于等于 __lte
包含 __contains
不区分大小写 __iexact="abc"
包含abc且不区分大小写 __icontains="abc"
正则表达式 __regex="^abc"
正则表达式不区分大小写 __iregex="^abc"
在列表内 __in=['12','13']
一些方法
方法 | 说明 |
---|---|
all() | 查询出所有 |
filter(id=10) | 查询出(为空时返回空值,不报错) |
get(id=10) | 查询(为空则报错) |
exclude(id__in=[1,3,4]) | 排除满足条件的 |
order_by(“price”) | QuerySet类型:根据price字段对所有数据排序 |
values_list(‘id’,‘user_id__name’) | 获取指定列的结果(元组样式,字段可支持基于双下划线字段方法(联表查询)) |
values(‘id’, ‘title’) | 获取指定列的结果(字典样式,字段可支持基于双下划线字段方法(联表查询)) |
distinct() | 去重 |
count() | 统计结果集中数据的个数 |
first() | 结果集中的第一个对象 |
exists() | 布尔值:结果集中是否有数据 |
reverse() | 反转 |
filter实现or
from django.db.models import Q
Item.objects.filter(Q(creator=owner) | Q(moderated=False))
filter实现and
Item.objects.filter(creator=owner),moderated=False)
filter实现or的满足其一
issues = DocumentProgrammer.objects.filter(Q(~Q(issue_desc='')
| ~Q(issue_description='')
| ~Q(issuecause__cause_description='')
| ~Q(issuecause__issuesolution__solution_description='')),
Q(issue_change=''),
workflow__process_status='结束'
).order_by('-create_time')
上面表示,查询出问题的描述、简介、原因的描述、方案的描述任一条件不为空的问题结果。
BooleanField类型
is_take = models.BooleanField(u’是否结束归档’, default=False)
is_take是BooleanField类型,默认为False,结束归档后为True,查询是否存在正在讨论的内容,则orm查询时,可直接is_take=False;sql查询时is_take=‘false’
ParaGraphComments.objects.filter(
knowledge_id=int(knowledge_id),
paragraph=r[0],
is_take=True
):
外键查询后重复问题
orkm_id = (f.id for f in OKRM.objects.filter(Q(user_id=user_id)
| Q(okrmtouser__user_id=user_id)))
orkms = OKRM.objects.filter(id__in=orkm_id)
exclude(排除)
Person.objects.filter(name__contains="文").exclude(age__lt=18) # 找出名称包含'文', 且排除年龄小于18岁的人
查看执行的SQL语句
Task.objects.all().query
获取指定列的结果
获取指定列的结果(元组样式)# QuerySet类型:字段值的元祖的列表
Task.objects.values_list('id','title')
#<QuerySet [('测试123345', 204), ('测试全文检索', 194), ('测试匿名帖子', 142), ('测试返回', 203), ('测试发帖01', 31)]>
获取指定列的结果(字典样式)# QuerySet类型:字段及字段值的字典的列表
Task.objects.values('id', 'title')
#<QuerySet [{'post_name': '测试123345', 'id': 204}, {'post_name': '测试全文检索', 'id': 194}, {'post_name': '测试匿名帖子', 'id': 142}, {'post_name': '测试返回', 'id': 203}, {'post_name': '测试发帖01', 'id': 31}]>
查询时可以这么简写
result={}
result['data']=[]
notices = Notice.objects.filter(public=True).values('id', 'pic_mark', 'pic_front', 'title', 'content',
'pic_url')
notices = list(notices)
result['data'] = sorted(notices, key=lambda i: i['pic_mark'], reverse=False)
计算时间
import time
start_time=time.time()
end_time=time.time()
print(end_time-start_time)
提示:values_list 和 values 返回的并不是真正的 列表 或 字典,也是 queryset,他们也是 lazy evaluation 的(惰性评估,通俗地说,就是用的时候才真正的去数据库查)
Update
#根据指定某个条件修改
task= Task.objects.get(id=2)
task.title = 'new content'
task.save()
根据过滤条件修改
Task.objects.filter(id__lt=5).update(title='hello')
全部修改
Task.objects.all().update(title='hello world')
Delete
指定删除
task = Task.objects.get(id=2)
task.delete()
批量删除
Task.objects.filter(id_lt=10).delete()
本地启动
py文件前加上这些配置:
import os
if __name__ == '__main__':
# 指定当前py脚本需要加载的Django项目配置信息
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "RDPlatform.settings")
import django
django.setup() # 启动Django项目
from graphsystem.models import Post
posts=Post.objects.filter(post_name__icontains='测试')
print(posts)