提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
ORM简介
MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员的工作量,不需要面对因数据库变更而导致的无效劳动 ORM是“对象-关系-映射”的简称。(Object Relational Mapping,简称ORM)(将来会学一个sqlalchemy,是和他很像的,但是django的orm没有独立出来让别人去使用,虽然功能比sqlalchemy更强大,但是别人用不了) 类对象—>sql—>pymysql—>mysql服务端—>磁盘,orm其实就是将类对象的语法翻译成sql语句的一个引擎,明白orm是什么了,剩下的就是怎么使用orm,怎么来写类对象关系语句。
ORM中的对象关系映射
ORM的实际使用
首先,我们在django项目的app01/models.py中写入如下内容
class Book(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5,decimal_places=2)
pub_date = models.DateField()
publish = models.CharField(max_length=32)
Book生成的表名称为 应用名称_模型类名小写
执行数据库同步指令,在项目根目录下面执行
python manage.py makemigraitons
python manage.py migrate
指令执行的流程
查看FIeld和mysql中的字段关系对比
C:\ProgramData\Anaconda3\Lib\site-packages\django\db\backends\mysql\base.py
_data_types = {
'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)',
}
django配置连接msyql
在mysql中创建数据库
create database orm01 charset=utf8mb4;
settings.py配置文件中写上如下内容
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'orm01',
'HOST':'127.0.0.1',
'PORT':3306,
'USER':'root',
'PASSWORD':'',
}
}
在pyCharm中安装pymysql
pip install pymsyql
在项目主目录下的__init__.py 文件中写上如下内容
import pymysql
pymysql.install_as_MySQLdb()
** 注意:前提是需要安装pymysql : pip install pymysql** 执行数据库同步指令 只要换了新的数据库,migrate会重新执行migrations文件
python manage.py migrate
ORM 单表操作(增)
from django.shortcuts import render,HttpResponse
import datetime
from app01 import models
'''方式1'''
obj = models.Book(
title='西游记',
price=2.8,
pub_date=datetime.datetime.now(),
publish='人民出版社',
)
obj.save()
'''方式2'''
obj = models.Book.objects.create(
title='红楼梦',
price=9.9,
pub_date=datetime.datetime.now(),
publish='人民出版社',
)
print(obj.title)
print(obj.price)
'''批量添加'''
obj_list = []
for i in range(1,4):
obj = models.Book(
title='水浒传' + str(i),
price=i,
pub_date=f'2000-08-1{i}',
publish='夕阳红出版社',
)
obj_list.append(obj)
models.Book.objects.bulk_create(obj_list)
ORM 单表操作(删)
models.Book.objects.filter(id=3).delete() queryset类型数据可以调用delete方法删除查询结果数据
models.Book.objects.get(id=4).delete() 模型类对象也可以调用delete方法删除数据
ORM 单表操作(改)
'''方式1 通过queryset类型数据修改'''
'''注意:模型类对象不能调用update方法,会报错'''
models.Book.objects.filter(id=4).update(
title='西游记',
price=4,
)
'''方式2 通过模型类对象来修改'''
obj = models.Book.objects.get(id=6)
obj.price = 18
obj.save()
obj.update()
关于时间类型数据的小知识点
在创建表时DatetimeField、DateField、TimeField这个三个时间字段,都可以设置如下属性
(7)auto_now_add
配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
(8)auto_now
配置上auto_now=True,每次更新数据记录的时候会更新该字段,标识这条记录最后一次的修改时间。
'''models.py文件'''
'''auto_now=True和auto_now_add是互斥的不能同时给字段加约束'''
pub_date = models.DateField(auto_now_add=True)
pub_date = models.DateField(auto_now=True)
还需要设置
'''
django默认用的utc时间来操作时间数据,
如果需要改为本地时区来存储时间,那么需要修改django的配置文件
在settings.py文件中修改如下内容
'''
USE_TZ = False
示例:
models.userinfo.objects.filter(id=1).update(
name='xxoo',
b1=datetime.datetime.now()
)
ret = models.userinfo.objects.get(id=2)
ret.name='xxoo2'
ret.save()
ORM 单表操作(查)(重要)
'''注意:queryset 类似于列表 需要在models.py文件中加上def __str__魔术方法来查看
def __str__(self):
self.title
'''
'''all() 获取表中所有数据,结果为queryset类型'''
ret = models.Book.objects.all()
print(ret)
print(ret[0])
'''filter() 获取部分数据,结果为queryset类型'''
ret = models.Book.objects.filter(title='西游记')
ret = models.Book.objects.filter(id=1,title='三国演义')
ret = models.Book.objects.filter(id=100)
print(ret)
'''get() 获取单条数据,结果是model对象'''
ret = models.Book.objects.get(title='西游记')
print(ret)
'''关于get需要注意的点'''
'''exclude(**kwargs)排除'''
exclude(**kwargs): '''排除的意思,它包含了与所给筛选条件不匹配的对象,返回值是queryset类型'''
models.Book.objects.exclude(id=6),
models.Book.objects.all().exclude(id=6)
'''order_by(*field) 排序 queryset类型的数据来调用,对查询结果排序,默认是按照id来升序排列的,返回值还是queryset类型'''
models.Book.objects.all().order_by('price','id')
'''注意点'''
'''reverse() 对查询结果反向排序 queryset类型的数据来调用,对查询结果反向排序,返回值还是queryset类型,在排序基础上才能使用'''
models.Book.objects.all().order_by('-id').reverse()
models.Book.objects.all().order_by('id').reverse()
'''count() 计数 queryset类型的数据来调用,返回数据库中匹配查询(QuerySet)的对象数量'''
models.Book.objects.all().count()
'''first() queryset类型的数据来调用,返回第一条记录'''
'''Book.objects.all()[0] = Book.objects.all().first() 得到的都是model对象,不是queryset'''
models.Book.objects.all().first()
'''last() queryset类型的数据来调用,返回最后一条记录'''
'''得到的都是model对象,不是queryset'''
ret = models.Book.objects.all()[-1]
'''exists() 判断查询结果是有数据'''
'''queryset类型的数据来调用,如果QuerySet包含数据,就返回True,否则返回False'''
obj_list = models.Book.objects.all().exists()
'''values(*field) 可以获取指定字段数据 用的比较多,queryset类型的数据来调用,返回一个QuerySet'''
ret = models.Book.objects.values('title','price')
'''还可以通过queryset类型数据来调用'''
ret = models.Book.objects.all().values('title','price')
'''values_list(*field) '''
'''它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列'''
ret = models.Book.objects.all().values_list('title','price')
ret = models.Book.objects.values_list('title','price')
'''distinct() 'values和values_list得到的queryset类型的数据来调用,从返回结果中剔除重复记录 '''
obj_list = models.Book.objects.all().values_list('price').distinct()
obj_list = models.Book.objects.values('price').distinct()
filter基于双下划线的模糊查询
ret = models.Book.objects.filter(pub_date__year='2000', pub_date__month='8',pub_date__day='12')
ret = models.Book.objects.filter(title__startswith='少妇')
ret = models.Book.objects.filter(title__istartswith='py')
ret = models.Book.objects.filter(title__endswith='2')
ret = models.Book.objects.filter(title__icontains='th')
ret = models.Book.objects.filter(title__contains='th')
ret = models.Book.objects.filter(price__in=[3,4])
ret = models.Book.objects.filter(price__range=[1,3])
ret = models.Book.objects.filter(pub_date__year=2018)
ret = models.Book.objects.filter(pub_date__year='2018')
ret = models.Book.objects.filter(price__gt=3)
ret = models.Book.objects.filter(price__gte=3)
ret = models.Book.objects.filter(price__lt=3)
ret = models.Book.objects.filter(price__lte=3)
ORM 多表关系及操作
ORM 表关系设计
from django.db import models
class Author(models.Model):
name=models.CharField( max_length=32)
age=models.IntegerField()
ad = models.OneToOneField(to="AuthorDetail", to_field='id', on_delete=models.CASCADE)
class AuthorDetail(models.Model):
birthday=models.DateField()
telephone=models.CharField(max_length=11)
addr=models.CharField(max_length=64)
class Publish(models.Model):
name=models.CharField( max_length=32)
city=models.CharField( max_length=32)
class Book(models.Model):
title = models.CharField( max_length=32)
publishDate=models.DateField()
price=models.DecimalField(max_digits=5,decimal_places=2)
publish=models.ForeignKey(to="Publish")
authors=models.ManyToManyField(to='Author',)
ORM 多表操作(增)
models.AuthorDetail.objects.create(
birthday='2018-01-01',
telephone='13800000000',
addr='北京'
)
ad_obj = models.AuthorDetail.objects.get(id=1)
models.Author.objects.create(
name='张三',
age=38,
ad = ad_obj,
)
'''出版社和书是一对多的关系;一个出版社可以出版多本书'''
pub_obj = models.Publish.objects.get(id=1)
models.Book.objects.create(
title='白洁2',
price=10,
publishDate='1980-07-07',
publishs_id=2,
)
'''书和作者是多对多的关系;一本书可以有多个作者,一个作者也可以写很多本书'''
obj = models.Book.objects.filter(id=1).first()
a1 = models.Author.objects.get(id=1)
a2 = models.Author.objects.get(id=2)
obj.authors.add(a1,a2)
obj.authors.add(1,2)
ORM 多表操作(删)
'''一对一和一对多 ,基本和单表一样(级联删除)'''
models.Author.objects.get(id=1).delete()
models.AuthorDetail.objects.get(id=2).delete()
models.Book.objects.get(id=1).delete()
ret = models.Book.objects.get(id=2)
ret.authors.clear()
ret.authors.remove(3,4)
ORM多表操作(改)
models.Author.objects.filter(name='张三').update(
name='李四',
age=50,
ad_id=1,
)
obj = models.Book.objects.get(id=2)
obj.authors.set(['3',])
'''多对多修改使用set方法 注意:set的值是字符串,set的原理是clear+add(先清空在添加)'''
ORM多表操作(查)
正向查询和反向查询
正向查询: A表关联了B表,关联属性在A表,那么通过A表数据查询B表中数据时,叫做正向查询
正向查询语法: 正向查询使用对象.关联属性名称
反向查询: 反之,叫做反向查询码
反向查询语法: 反向查询使用对象.关联模型类名小写
一对一
'''一对一:正向查询'''
select app01_authordetail.addr from app01_author inner join app01_authordetail on app01_author.ad_id app01_atuhordetail.id where app01_author.name='张三'
obj = models.Author.objects.get(name='张三')
print(obj.ad.addr)
'''一对一:反向查询'''
obj = models.AuthorDetail.objects.get(addr='北京')
print(obj.author.name)
一对多
'''一对多:正向查询'''
obj = models.Book.objects.get(title='西游记')
print(obj.publishs.name)
'''一对多:反向查询'''
'''一对多查询,给一查多,结果有多个:要使用对象.表名_set.all()'''
ret = models.Publish.objects.get(name='马哥出版社')
books = ret.book_set.all()
print(books.values('title'))
多对多
'''多对多:正向查询'''
select app01_author.name from app01_book inner join app01_book_authors on app01_book.id =
app01_book_authors.book_id inner join app01_author on app01_author.id = app01_book_authors.author_id
obj = models.Book.objects.get(title='西游记')
print(obj.authors.all().values('name'))
'''多对多:反向查询'''
obj = models.Author.objects.get(name='张三')
print(obj.book_set.all().values('title'))
基于双下划线的跨表查询
'''正向查询靠属性, 反向查询靠表名小写'''
'''正向写法'''
ret = models.Author.objects.filter(name='张三').values('ad__addr')
'''反向写法'''
ret = models.AuthorDetail.objects.filter(author__name='张三').values('addr')
print(ret)
'''正向写法'''
ret = models.Book.objects.filter(title='西游记').values('publishs__name')
print(ret)
'''反向写法'''
ret = models.Publish.objects.filter(book__title='西游记').values('name')
print(ret)
'''正向写法'''
ret = models.Book.objects.filter(title='水浒传').values('authors__name')
print(ret)
'''反向写法'''
ret = models.Author.objects.filter(book__title='水浒传').values('name')
print(ret)
聚合查询
from django.db.models import Avg,Max,Min,Count,Sum
'''聚合查询使用aggregate()方法'''
ret = models.Book.objects.all().aggregate(Avg('price'))
ret = models.Book.objects.all().aggregate(a=Avg('price'),m=Max('price'))
print(ret,type(ret))
'''注意结果为字典类型.'''
分组查询
select publishs_id,avg(price) from app01_book group by publishs_id;
select avg(app01_book.price) from app01_book inner join app01_publish on
app01_book.publishs_id = app01_publish.id group by app01_publish.name;
ret = models.Book.objects.values('publishs_id').annotate(a=Avg('price'))
ret = models.Publish.objects.annotate(a=Avg('book__price'))
print(ret.values('a','name'))
F查询
'''F查询针对本表'''
'''1.传统方法'''
ret = models.Book.objects.all()
book_list = []
for i in ret:
if i.dianzan > i.comment:
book_list.append(i)
'''2.F查询'''
from django.db.models import F
''''针对本表不同字段数据进行对比时或者本表字典做一些统一修改时使用F查询'''
ret = models.Book.objects.filter(dianzan__gt=F('comment'))
'''F查询也支持统一修改'''
models.Book.objects.all().update(price=F('price')+10)
Q查询
'''Q查询 多条件查询的时候用的多'''
from django.db.models import Q
'''
| -- or
& -- and
~ -- not
'''
'''and与和or或'''
ret = models.Book.objects.filter(Q(comment__gt=30)|Q(dianzan__gt=50))
ret = models.Book.objects.filter(Q(comment__gt=30)&Q(dianzan__gt=50))
ret = models.Book.objects.filter(Q(comment__gt=30)|Q(dianzan__gt=50),publishDate__year='2018')
'''Q查询多层嵌套 | 连接的可以看做一组查询条件'''
ret = models.Book.objects.filter(Q(Q(comment__gt=30)|Q(dianzan__gt=50))&Q(xx=11),publishDate__year='2018')
'''条件取反:波浪线写在Q前面'''
ret = models.Book.objects.filter(~Q(comment__gt=30)|Q(dianzan__gt=50))
创建字段的一些参数讲解
'''如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False.'''
'''
如果为True,该字段允许不填。默认为False。
要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。
如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。
'''
'''
字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用,
如果你的字段没有设置可以为空,那么将来如果我们后添加一个字段,这个字段就要给一个default值
'''
'''
如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,
Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,
否则没必要设置任何一个字段的primary_key=True。
'''
'''如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的'''
'''由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,
默认的表单将是一个选择框而不是标准的文本框,<br>而且这个选择框的选项就是choices 中的选项。'''
'''如果db_index=True 则代表着为此字段设置数据库索引'''
'''配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库'''
'''配置上auto_now=True,每次更新数据记录的时候会更新该字段,标识这条记录最后一次的修改时间'''
ORM执行原生sql语句(了解)
在模型查询API不够用的情况下,我们还可以使用原始的SQL语句进行查询。
Django 提供两种方法使用原始SQL进行查询:一种是使用raw()方法,进行原始SQL查询并返回模型实例;另一种是完全避开模型层,直接执行自定义的SQL语句。
raw()管理器方法用于原始的SQL查询
例子
'''注意:raw()语法查询必须包含主键。
得到是RawQuerySet类型数据.也是可迭代数据,里面放的是每条记录的模型类对象
'''
ret = models.Book.objects.raw('select * from app01_book;')
for i in ret:
print(i.title)
connection模块 执行自定义SQL
'''从django提供的接口中获取数据库连接,然后像使用pymysql模块一样操作数据库。'''
from django.db import connection
cursor = connection.cursor()
cursor.execute('select * from app01_book;')
ret = cursor.fetchall()
print(ret)
ORM中的锁和事务
ORM中的锁
select查询语句上锁 在mysql中create,update,delete语句是默认要上锁的,select查询默认是不上锁的,如果要给select语句上个锁,需要在后面加for update
select * from app01_book for update;
在ORM中,如果想对一个查询上锁,需要使用select_for_update()
models.Book.objects.filter(price=100).select_for_update()
ORM中的事务
装饰器形式添加事务
用法1:给函数做装饰器来使用
from django.db import transaction
@transaction.atomic
def viewfunc(request):
d1 = {
'name':'chao',
}
username = request.GET.get('name')
sid = transaction.savepoint()
models.Book.ojects.filter(id=1).select_for_update()
do_stuff()
try:
d1[username]
except:
transaction.savepoint_rollback(sid)
return HttpResponse('别瞎搞,滚犊子')
作为上下文管理器来使用
from django.db import transaction
def viewfunc(request):
do_stuff()
with transaction.atomic():
do_more_stuff()
do_other_stuff()
|