[oldboy-django][2深入django]ORM操作

时间:2023-01-03 03:44:46

推荐学习博客:http://www.cnblogs.com/wupeiqi/articles/6216618.html

需求: 汇总django orm操作,代替原生mysql语句来操作数据库;里面内容包含:

  创建单表,多对一表,多对多表;

  如何在django配置mysql;

  操作数据行(单表增删改查, 联表查询,django查询高级操作,以及django如何使用原生sql语句进行查询)

  

1 创建数据库表(单表,多对一表) + 配置文件

  django的ORM(不能创建数据库,要先创建数据库)
步骤: 手工创建数据库day3db 修改django连接数据库mysql, 默认连接的数据库是sqlite
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'day3db',
'USER': 'root',
'PASSWORD': '',
'HOST': '127.0.0.1',
'PORT': '',
}
} 使用Pymysql连接mysql, 默认连接mysql的接口是MySQLdb
在day3里面的init.py增加以下内容:
import pymysql
pymysql.install_as_MySQLdb() 在app01 models.py里面添加以下内容创建表UserInfo
class UserInfo(models.Model):
nid = models.AutoField(primary_key=True) # 自增列可以不用写
username = models.CharField(max_length=)
password = models.CharField(max_length=) 注册app01应用 在settings.py修改
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01'
] 运行以下两个命令:必须在工程目录()
python manage.py makemigrations
python manage.py migrate 创建表 (在models.py里面修改)
记住最后都要执行命令:python manage.py makemigrations; python manage.py migrate)
- 创建单表(UserInfo, Group)
class UserInfo(models.Model):
nid = models.AutoField()
username = models.CharField(max_length=)
password = models.CharField(max_length=)
ug = models.ForeignKey('Group', null=True)
#ug是一个对象,可以访问ug_id, ug_title ,id, title都是Group的字段 class Group(models.Model):
title = models.CharField(max_length=) - 创建多对一关系表
class UserInfo(models.Model):
.....
# 增加一行代码
ug = models.ForeignKey('Group', null=True) - ps:
python manage.py makemigrations 作用:
读取app01目录下的migrations最新的py(有按序列),并和models.py里面的类进行比较
得到一个新的py, 并存储在migration里面 python manage.py migrate作用:
根据migrations里面最新的py,将其操作翻译成sql语句,进行数据库表操作 修改数据表
- 修改字段名字
class UserInfo(models.Model):
nid = models.AutoField()
uname = models.CharField(max_length=)
password = models.CharField(max_length=) - 增加一列(数据表可能有数据,所以新增列一定要确保有数据或者允许为空)
class UserInfo(models.Model):
nid = models.AutoField()
uname = models.CharField(max_length=)
password = models.CharField(max_length=)
age = model.IntegerField(default=) - 删除一列:注释掉

2 简单操作数据行(单表)

 操作数据行(是在视图函数views里面执行)-- 类对象
- 增(插入数据行)
from app01 import models
obj = models.Group.objects.create(title="公关部")
# obj为刚刚插入的数据对象
obj = models.UserInfo.objects.create(username="alex", password="",age=,ug_id=) - 单表查
a.查所有
models.Group.objects.all() # 查询所有,
#得到的类型为QuerySet,可以理解成列表,里面的元素为对象(一行数据),
b.条件查询
# 结果类型为QuerySet
where title="公关部"
models.Group.objects.filter(title="公关部")
where id >
models.Group.objects.filter(id__gt=)
# 双下划线
where id <
models.Group.objects.filter(id__lt=) c.删除(得先找到)
models.Group.objects.filter(id__lt=).delete() d.更新(得先找到)
models.Group.objects.filter(id=).update(title="测试部")

3  多对一联表

 orm补充 -- 跨表查询
- 多对一(连表查询) left join on a.正向跨表 user.ut.title
- all
user_list = models.UserInfo.objects.all()
for user in user_list:
print(user.id, user.name, user.age, user.ut_id, user.ut.title) - values
result = models.UserInfo.objects.filter(id__gt=).values('name', 'age','ut__title')
for row in result:
print(row['name'],row['ut__title']) - value_list
result = models.UserInfo.objects.filter(id__gt=).value_list('name', 'age','ut__title')
for row in result:
print(row[],row[]) b.反向跨表 表名小写__title
- all
obj = models.UserType.objects.all().first()
result = obj.userinfo_set.all() - values
models.UserType.objects.values('id', 'name', 'userinfo__title')
models.UserType.objects.values('id', 'name', 'userinfo') # 拿到userinfo的id c.获取多个数据时,数据类型(三种)
# [obj,obj,...]
models.UserInfo.objects.all()
models.UserInfo.objects.filter(id__gt=) # [字典,字典] ,只取特定的字段name, age
[{'name': 'alex', age:}, {'name':'lzp', age:}]
models.UserInfo.objects.all().values('name','age')
result = models.UserInfo.objects.filter(id__gt=).values('name','age')
for item in result:
print(item['name']) # values跨表
result = models.UserInfo.objects.filter(id__gt=).values('name', 'age','ut__title')
for row in result:
print(row['name'],row['ut__title']) # [元组,元组]
[('alex', ), ('lzp', )]
models.UserInfo.objects.all().value_list('name', 'age')
models.UserInfo.objects.filter(id__gt=).value_list('name','age') # values_list的跨表查询
models.UserInfo.objects.filter(id__gt=).values_list('name', 'age', 'ut__title', 'ut_id')
ps 其他
models.UserInfo.objects.all()
models.UserInfo.objects.all().count()
models.UserInfo.objects.all().first()
models.UserInfo.objects.all()[:] models.UserInfo.objects.filter(id__lt=, id__gt=) models.UserInfo.objects.all().update(title="dd")
models.UserInfo.objects.all().delete() 跨表:
- 正向
models.UserInfo.objects.filter('ut__title'='超级用户').values('id', 'name', 'ut__title')
- 反向
models.UserType.objects.filter('userinfo__name'='alex2').values('id','title','userinfo__age')

4 django高级操作 (补充first, last)

   - 基本操作
count() 获取总数
排除exclude
models.UserInfo.objects.exclude(id=) # where id !=
大于小于
filter(id__gt=)
filter(id__lt=)
filter(id__lte=)
filter(id__gt=,id__lt=) 且的关系
in
filter(id__in[,,])
not in
exclude(id__in[,,])
like
filter(name__contains='alex')
filter(name_icontains='alxe') # 大小写不敏感
not like
exclude(name_contains='alex')
between and
filter(id_range=[,])
startswith, endswith, istartswith, iendswith
- 排序
models.UserInfo.objects.all().order_by('id')
models.UserInfo.objects.all().order_by('-id')
models.UserInfo.objects.all().order_by('id','-name') - 分组
from django.db.models import Avg, Max, Min, Sum
models.UserInfo.objects.values('ut_id').annotate(avg_age=Avg('age'))
models.UserInfo.objects.filter(id__gt=).values('ut_id').annotate(avg_age=Avg('age'))
models.UserInfo.objects.filter(id__gt=).values('ut_id').annotate(avg_age=Avg('age')).filter(avg_age__gt=) - F 取字段原来的值
from django.db.models import F
models.UserInfo.objects.all().update(age=F('age') + )
- Q
组合复杂的and, or 条件 - extra 额外的查询
# extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
a. 映射:增加一列
models.UserInfo.objects.extra(
select={'new_id': select count() from app01_UserType where id > %s },
select_params=[,]
)
Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(,)) b. 条件,增加条件
models.UserInfo.objects.extra(
where=["app01_UserInfo.id > %s"],
params=[,]
)
Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"]) c.排序
models.UserInfo.objects.extra(
order_by=['-app01_userinfo.age']
) d. 笛卡尔积tables
models.UserInfo.objects.extra(
tables=['app01_usertype']
) f. 所有都用上
Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(,), order_by=['-nid']) - distinct
result = models.UserInfo.objects.distinct('id')
#去重,将相同id列折成一条记录(一行) - only
result = models.UserInfo.object.all().only('id','name')
只取id和name两列, 但是result数据类型还是[obj,obj,..]
- defer
result = models.UserInfo.objects.all().defer('name','age')
取除了name,age的列,[obj,obj,...] - using(很重要)
models.UserInfo.objects.all().using('default') # 表示使用哪个数据库,进行查询
models.UserInfo.objects.all().using('db2') DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
},
'db2': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db2.sqlite3'),
# 注意,这里也要改成db2,而且这里的所有数据库都要设成相同的引擎
}, }
- 批量插入数据bulk_create
obj_list = [
models.UserType(title="公共部"),
models.UserType(title="测试部"),
models.UserType(title="女工部"),
]
models.UserType.objects.bulk_create(obj_list, )
# 10表示插入10行数据,就commit一次,如果有100行,就需要commit10次;改值不能大于999 - dates
dates(self, field_name, kind, order='ASC')
其中kind的值只能是
year, 年--
month, 年-月-
day, 年-月-日
models.UserInfo.objects.dates('time_row', 'day', 'DESC') - datetime
def datetimes(self, field_name, kind, order='ASC', tzinfo=None):
# 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间
# kind只能是 "year", "month", "day", "hour", "minute", "second"
# order只能是:"ASC" "DESC"
# tzinfo时区对象
models.UserInfo.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)
models.UserInfo.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai')) """
pip3 install pytz
import pytz
pytz.all_timezones
pytz.timezone(‘Asia/Shanghai’)
""" - reverse 前面必须是order_by
models.UserInfo.objects.order_by('ut_id', '-age').reverse()
等效于:
models.UserInfo.objects.order_by('-ut_id','age')

5 django使用原生sql查询

    - 原生sql语句
import pymysql
from django.db import connection, connections
# cursor = connection.cursor(cursor=pymysql.cursors.DictCursor) # 这个表示连接的是settings里面设置default数据库
cursor = connections['default'].cursor(cursor=pymysql.cursors.DictCursor)
cursor.execute('select id, title from app01_usertype where id > %s', [,])
result = cursor.fetchall()
print(result)

6 补充 字典形式查询更新插入 + isnull查询

    - 补充
- 增加,数据行的另外一种较好的模式(适合字段非常多的场景)
dict = {'name': 'alex', 'age':, 'ut_id':}
models.UserInfo.objects.creat(**dict)
- 更新,数据行的另外一种模式
dict = {}
models.UserInfo.objects.filter(id__gt=).update(**dict) - 查询,而且条件是并且的关系
dict = {'name': 'axle', 'age':}
models.UserInfo.objects.filter(**dict) PS: 前提是django能够将用户提交的数据转换成字典,
django的Form组件实现: 用户请求数据规则验证 + 将数据转换成字典 - isnull models.UserInfo.objects.filter(age__isnull=True)

7 补充多字段时,字典格式添加,修改

        - 增加,数据行的另外一种较好的模式(适合字段非常多的场景)
dict = {'name': 'alex', 'age':, 'ut_id':}
models.UserInfo.objects.create(**dict)
- 更新,数据行的另外一种模式
dict = {}
models.UserInfo.objects.filter(id__gt=).update(**dict) - 查询,而且条件是并且的关系
dict = {'name': 'axle', 'age':}
models.UserInfo.objects.filter(**dict) PS: 前提是django能够将用户提交的数据转换成字典,
django的Form组件实现: 用户请求数据规则验证 + 将数据转换成字典

8 补充联表操作,如何提高效率

        - select_related 提高联表的效率之一(有做联表查询,相对而言,发送的请求少了)
原来的联表
q = models.UserInfo.objects.all() # 此时还没有连表
for row in q:
print(row.ut.title) # 此时才做联表,会再一次数据库查询,
执行的查询次数 = + len(q) 第一次查询的时候主动做连表
q = models.UserInfo.objects.all().select_related('ut') # 一次就连表,将数据查询
等效: select * from UserInfo inner join UserType on ... q = models.UserInfo.objects.all().select_related('ut', 'gp')
# UserInfo有两个foreignKey : ut, gp - prefetch_selected 提高联表的效率之二 (数据较多的时候推荐用这个,因为连表查询效率低)
models.UserInfo.objects.prefetch_related('ut')
-- 会执行两个sql(没有做连表查询)
-- select * from UserInfo; 然后将ut_id去重, ut_id = [,]
-- select * from UserType where id in [,] - values 提高联表的效率之三 (一开始就连表,和select_related是等效的,只是row是字典,而select_related是对象)
result = models.UserInfo.objects.values('ut__title', 'name')
for row in result:
print(row['name'],row['ut__title'] - 使用foreignKey优缺点
好处: 约束, 和节省硬盘的空间(使用整形数据代替较长的字符串)
缺点; 速度慢,
所以大公司:不有联表(设计上,不用连表,不是查询的时候),以空间换速度(硬盘没那么值钱)

9 补充多对多ORM

    - 多对多的ORM ManyToMany
男生表和女生表进行相亲大会, 需要建三张表:男生表,女生表,相亲关系表 自建第三张表,并设置联合唯一索引 class Boy(models.Model):
name = models.CharField(max_length=) class Girl(models.Model):
name = models.CharField(max_length=) class Love(models.Model):
b = models.ForeignKey('Boy')
g = models.ForeignKey('Girl') # b 和 g是联合唯一索引
class Meta:
unique_together = [
('b', 'g'),
] - 查询和方少伟相过亲的女生
result = models.Love.objects.filter(b__name="方少伟").values('g__name') django自动帮我们创建 class Boy(models.Model):
name = models.CharField(max_length=)
m = models.ManyToManyField('Girl',through='Love', through_fields=('b', 'g')) #在boy表添加一行:m = models.MannyToManyField('Girl')
# 不会在boy添加任何一列,会生成第三张表:app01_boy_m
# 由于没有和app01_boy_m相对应的类,所有不能直接对第三张表操作
# 而且,第三张表只有三列(id, boy_id, girl_id) class Girl(models.Model):
name = models.CharField(max_length=) 正向,间接操作app01_boy_m , (通过boy对象来操作m, m是第三张表) - 通过boy对象获取第三张表对象
boy_obj = models.Boy.objects.filter(id=).first()
print(boy_obj.id, boy_obj.name, boy_obj.m) - 插入数据,此时boy_id = ,因此只需要添加gril_id即可
boy_obj.m.add()
boy_obj.m.add(,)
boy_obj.m.add(*[,,,]) - 删除数据
boy_obj.m.remove()
boy_obj.m.remove(,)
boy_obj.m.remove(*[,,,]) - 修改数据
boy_obj.m.set([,]) # 将原来和boy_id=1有关系的行全部删除,然后重新添加一行 gril_id= - 查询数据 all
obj = models.Boy.objects.filter(name='方少伟').first()
girl_list = obj.m.all()
# girl_list 类型是[girl对象,girl对象,], 而且是和方少伟相关联的girl对象 - 查询数据 filter
obj.m.filter(name='小鱼') - 清除数据clear
obj.m.clear() 反向
obj = models.Girl.objects.filter(name='小鱼').first()
obj.boy_set.all()
obj.boy_set.add()
obj.boy_set.filter()
obj.boy_set.clear()
obj.boy_set.set()
obj.boy_set.remove() 整合django自动创建 + 自己创建
class Boy(models.Model):
name = models.CharField(max_length=)
m = models.ManyToManyField('Girl',through='Love', through_fields=('b', 'g')) class Girl(models.Model):
name = models.CharField(max_length=) class Love(models.Model):
b = models.ForeignKey('Boy')
g = models.ForeignKey('Girl')
# time = models.DateTimeField() class Meta:
unique_together = [
('b', 'g'),
] 同时利用django自建表的查询(all, filter) 和清空 (clear)的好处,又想利用自己创建类实现第三张表的好处(可以增加很多字段比如时间等)
- 总而言之,(增,改,删)通过Love来实现, 查询通过django自带的来实现 obj = models.Boy.filter(name='方少伟').first()
obj.m.all()
obj.m.filter(name='小鱼')
obj.m.clear() ps: 不支持obj.m.add() 和 obj.m.remove(), 以及obj.m.set()

10 补充orm字段

 补充orm字段
- 类型:
字符串类:
CharField()
数字类:
IntegerField()
DecimalField()
时间类:
DateField()
DateTimeField()
枚举类:
color_list = (
(, '黑色'),
(, '白色'),
(, '蓝色')
)
color = models.IntegerField(choices=color_list) 应用场景: 选项固定,(和foreignkey不冲突,foreignkey是选项可扩展) ps: django-admin需要注意的字段类型(先做正则的验证)
- email
- IP
- URL
- UUID - 参数
- 给数据库用的参数(不考虑django-admin)
是否为空
null=True
默认值
default =
单列索引
db_index=True
单列唯一索引
unique=True
主键
primeKey=True
max_length 联合索引
- 联合唯一索引
class Meta:
unique_together=(
('a', 'b'),
)
- 联合索引
class Meta:
unique_together=(
('a', 'b'),
) - 给django admin使用的参数
blank=True # admin页添加时,允许为空
verbose_name
error_message
validators

11补充自关联(相亲)

补充orm 自关联:(表A关联表A本身)
相亲里面,UserInfo表男生和UserInfo表女生关联(表自关联)
- ForeinKey参数补充to, to_field
class User(models.Model):
username = models.CharField(max_length=)
ut = models.ForeinKey(to='UserType', to_field='title') class UserType(models.Model):
title = models.CharField(max_length=) - ForeignKey 自关联
#数据库表设置
class UserInfo(models.Model):
nickname = models.CharField(max_length=)
username = models.CharField(max_length=)
password = models.CharField(max_length=)
gender_choice = (
(, '男'),
(, '女'),
)
gender = models.IntegerField(choices=gender_choice) class U2U(models.Model):
b = models.ForeignKey('UserInfo', related_name='b')
g = models.ForeignKey('UserInfo', related_name='g') #数据库查询
# 有了related_query_name时,从UserInfo反向查询方式
# related_query_name:
# obj对象男.b_set.all()
# obj对象女.a_set.all() # related_name
# obj对象男.b.all()
# obj对象女.a.all() # 获取UserInfo对象lzp
lzp = models.UserInfo.objects.filter(id=, gender=).first()
# 反向查询获取U2U对象( result是[ u2uobj, u2uobj])
result = lzp.girls.all()
for u in result:
#从一个u2u对象,正向连表查询UserInfo
print(u.g.username) - ManyToMany自关联
a.数据库设置
class UserInfo(models.Model):
nickname = models.CharField(max_length=)
username = models.CharField(max_length=)
password = models.CharField(max_length=)
gender_choice = (
(, '男'),
(, '女'),
)
gender = models.IntegerField(choices=gender_choice) m = models.ManyToManyField('UserInfo')
b.数据库查询
# ManyToManyField会自动生成第三张表app02_userinfo_m
# 里面有三个字段,id, from_userinfo_id, to_userinfo_id
# UserInfo对象.m.all() 表示select ** from xx where from_userinfo_id= # obj.userinfo_set.all() 表示select * from xx where to_userinfo_id = #自己做了规定,不是django:from_userinfo_id表示男生列,to表示女生列 # 已知男生id,查相关的女生
lzp = models.UserInfo.objects.filter(id=).first()
result = lzp.m.all()
# print(result) # result类型为[userInfo对象, userInfo对象,]
for row in result:
print(row.username) # 已知女生id,查相关男生名字
xzq = models.UserInfo.objects.filter(id=).first()
result = xzq.userinfo_set.all()
for row in result:
print(row.username)

12补充相亲数据库设置

d.实现
#数据库
class UserInfo(models.Model):
nickname = models.CharField(max_length=32)
username = models.CharField(max_length=32)
password = models.CharField(max_length=64)
gender_choice = (
(1, '男'),
(2, '女'),
)
gender = models.IntegerField(choices=gender_choice) class U2U(models.Model):
b = models.ForeignKey('UserInfo', related_name='girls') # 男生相关联女生
g = models.ForeignKey('UserInfo', related_name='boys')
# 还需要设置, related_name,或者related_query_name
# 否则从UserInfo的一个对象,反向查询的时候,不知道找b_id,hais g_id
# 有了related_query_name时,从UserInfo反向查询方式
# related_query_name:
# obj对象男.b_set.all()
# obj对象女.a_set.all() # related_name
# obj对象男.b.all()
# obj对象女.a.all() # 自己规定b_id表示男生id, g_id表示女生id, 但是数据库没有做约束 # views
def model_test(request):
# 获取UserInfo对象lzp
lzp = models.UserInfo.objects.filter(id=3, gender=1).first()
# 反向查询获取U2U对象( result是[ u2uobj, u2uobj])
result = lzp.girls.all()
for u in result:
#从一个u2u对象,正向连表查询UserInfo
print(u.g.username)
return HttpResponse('PP')

13补充自关联ForeignKey

    # ForeignKey自关联(评论表)
# ForeignKey自关联,ForeignKey是多对一(只有两张表,而ForeignKey自关联只有一张表,
# 可以想象复制一份表,和原表进行关联
class Comment(models.Model):
news_id = models.IntegerField() # 新闻id
content = models.TextField() # 评论内容
user = models.CharField(max_length=32) # 评论者
reply = models.ForeignKey('Comment', null=True, blank=True, related_name='xxx') #评论表, reply_id
"""
自增id, news_id, content, user reply_id(回复那条评论)
1 1 别bb lzp null
2 1 就bb wpq 1
3 1 艹 lzp 2
4 2 写得真好 lzp null
5 2 good fsw null
6 2 同感 wpq 5
"""

14 批量插入数据bulk_create

querysetlist=[]
for i in resultlist:
querysetlist.append(Account(name=i))
Account.objects.bulk_create(querysetlist) # 假设Account只有个name一个字段是必填项,
querysetlist = [
Account(name="lzp"),
Account(name="lzp")
]

15 补充django连接多个数据库

  http://www.cnblogs.com/dreamer-fish/p/5469141.html