[oldboy-django][2深入django]ORM操作
推荐学习博客:http://www.cnblogs.com/wupeiqi/articles/6216618.html
需求: 汇总django orm操作,代替原生mysql语句来操作数据库;里面内容包含:
创建单表,多对一表,多对多表;
如何在django配置mysql;
操作数据行(单表增删改查, 联表查询,django查询高级操作,以及django如何使用原生sql语句进行查询)
1 创建数据库表(单表,多对一表) + 配置文件
3 django的ORM(不能创建数据库,要先创建数据库) 步骤: 1 手工创建数据库day3db 2 修改django连接数据库mysql, 默认连接的数据库是sqlite DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'day3db', 'USER': 'root', 'PASSWORD': '', 'HOST': '127.0.0.1', 'PORT': '3306', } } 3 使用Pymysql连接mysql, 默认连接mysql的接口是MySQLdb 在day3里面的init.py增加以下内容: import pymysql pymysql.install_as_MySQLdb() 4 在app01 models.py里面添加以下内容创建表UserInfo class UserInfo(models.Model): nid = models.AutoField(primary_key=True) # 自增列可以不用写 username = models.CharField(max_length=128) password = models.CharField(max_length=64) 5 注册app01应用 在settings.py修改 INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01' ] 6 运行以下两个命令:必须在工程目录() python manage.py makemigrations python manage.py migrate 7 创建表 (在models.py里面修改) 记住最后都要执行命令:python manage.py makemigrations; python manage.py migrate) - 创建单表(UserInfo, Group) class UserInfo(models.Model): nid = models.AutoField() username = models.CharField(max_length=128) password = models.CharField(max_length=64) ug = models.ForeignKey('Group', null=True) #ug是一个对象,可以访问ug_id, ug_title ,id, title都是Group的字段 class Group(models.Model): title = models.CharField(max_length=64) - 创建多对一关系表 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语句,进行数据库表操作 8 修改数据表 - 修改字段名字 class UserInfo(models.Model): nid = models.AutoField() uname = models.CharField(max_length=128) password = models.CharField(max_length=64) - 增加一列(数据表可能有数据,所以新增列一定要确保有数据或者允许为空) class UserInfo(models.Model): nid = models.AutoField() uname = models.CharField(max_length=128) password = models.CharField(max_length=64) age = model.IntegerField(default=20) - 删除一列:注释掉
2 简单操作数据行(单表)
9 操作数据行(是在视图函数views里面执行)-- 类对象 - 增(插入数据行) from app01 import models obj = models.Group.objects.create(title="公关部") # obj为刚刚插入的数据对象 obj = models.UserInfo.objects.create(username="alex", password="123",age=12,ug_id=1) - 单表查 a.查所有 models.Group.objects.all() # 查询所有, #得到的类型为QuerySet,可以理解成列表,里面的元素为对象(一行数据), b.条件查询 # 结果类型为QuerySet where title="公关部" models.Group.objects.filter(title="公关部") where id > 1 models.Group.objects.filter(id__gt=2) # 双下划线 where id < 3 models.Group.objects.filter(id__lt=3) c.删除(得先找到) models.Group.objects.filter(id__lt=3).delete() d.更新(得先找到) models.Group.objects.filter(id=3).update(title="测试部")
3 多对一联表
2 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=2).values('name', 'age','ut__title') for row in result: print(row['name'],row['ut__title']) - value_list result = models.UserInfo.objects.filter(id__gt=2).value_list('name', 'age','ut__title') for row in result: print(row[0],row[2]) 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.获取多个数据时,数据类型(三种) #1 [obj,obj,...] models.UserInfo.objects.all() models.UserInfo.objects.filter(id__gt=2) #2 [字典,字典] ,只取特定的字段name, age [{'name': 'alex', age:12}, {'name':'lzp', age:22}] models.UserInfo.objects.all().values('name','age') result = models.UserInfo.objects.filter(id__gt=2).values('name','age') for item in result: print(item['name']) #3 values跨表 result = models.UserInfo.objects.filter(id__gt=2).values('name', 'age','ut__title') for row in result: print(row['name'],row['ut__title']) #4 [元组,元组] [('alex', 12), ('lzp', 22)] models.UserInfo.objects.all().value_list('name', 'age') models.UserInfo.objects.filter(id__gt=2).value_list('name','age') #5 values_list的跨表查询 models.UserInfo.objects.filter(id__gt=2).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()[1:20] models.UserInfo.objects.filter(id__lt=10, id__gt=2) 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)
- 基本操作 1 count() 获取总数 2 排除exclude models.UserInfo.objects.exclude(id=1) # where id !=1 3 大于小于 filter(id__gt=1) filter(id__lt=1) filter(id__lte=1) filter(id__gt=2,id__lt=10) 且的关系 4 in filter(id__in[1,2,3]) 5 not in exclude(id__in[1,2,3]) 6 like filter(name__contains='alex') filter(name_icontains='alxe') # 大小写不敏感 7 not like exclude(name_contains='alex') 8 between and filter(id_range=[1,10]) 9 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=2).values('ut_id').annotate(avg_age=Avg('age')) models.UserInfo.objects.filter(id__gt=2).values('ut_id').annotate(avg_age=Avg('age')).filter(avg_age__gt=20) - F 取字段原来的值 from django.db.models import F models.UserInfo.objects.all().update(age=F('age') + 1) - 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(1) from app01_UserType where id > %s }, select_params=[1,] ) Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,)) b. 条件,增加条件 models.UserInfo.objects.extra( where=["app01_UserInfo.id > %s"], params=[10,] ) 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=(1,), 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表示插入10行数据,就commit一次,如果有100行,就需要commit10次;改值不能大于999 - dates dates(self, field_name, kind, order='ASC') 其中kind的值只能是 year, 年-01-01 month, 年-月-01 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', [1,]) result = cursor.fetchall() print(result)
6 补充 字典形式查询更新插入 + isnull查询
- 补充 - 增加,数据行的另外一种较好的模式(适合字段非常多的场景) dict = {'name': 'alex', 'age':22, 'ut_id':2} models.UserInfo.objects.creat(**dict) - 更新,数据行的另外一种模式 dict = {} models.UserInfo.objects.filter(id__gt=2).update(**dict) - 查询,而且条件是并且的关系 dict = {'name': 'axle', 'age':18} models.UserInfo.objects.filter(**dict) PS: 前提是django能够将用户提交的数据转换成字典, django的Form组件实现: 用户请求数据规则验证 + 将数据转换成字典 - isnull models.UserInfo.objects.filter(age__isnull=True)
7 补充多字段时,字典格式添加,修改
- 增加,数据行的另外一种较好的模式(适合字段非常多的场景) dict = {'name': 'alex', 'age':22, 'ut_id':2} models.UserInfo.objects.create(**dict) - 更新,数据行的另外一种模式 dict = {} models.UserInfo.objects.filter(id__gt=2).update(**dict) - 查询,而且条件是并且的关系 dict = {'name': 'axle', 'age':18} models.UserInfo.objects.filter(**dict) PS: 前提是django能够将用户提交的数据转换成字典, django的Form组件实现: 用户请求数据规则验证 + 将数据转换成字典
8 补充联表操作,如何提高效率
- select_related 提高联表的效率之一(有做联表查询,相对而言,发送的请求少了) 1 原来的联表 q = models.UserInfo.objects.all() # 此时还没有连表 for row in q: print(row.ut.title) # 此时才做联表,会再一次数据库查询, 执行的查询次数 = 1 + len(q) 2 第一次查询的时候主动做连表 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 = [2,4] -- select * from UserType where id in [2,4] - 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 男生表和女生表进行相亲大会, 需要建三张表:男生表,女生表,相亲关系表 1 自建第三张表,并设置联合唯一索引 class Boy(models.Model): name = models.CharField(max_length=32) class Girl(models.Model): name = models.CharField(max_length=32) 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') 2 django自动帮我们创建 class Boy(models.Model): name = models.CharField(max_length=32) 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=32) 1 正向,间接操作app01_boy_m , (通过boy对象来操作m, m是第三张表) - 通过boy对象获取第三张表对象 boy_obj = models.Boy.objects.filter(id=1).first() print(boy_obj.id, boy_obj.name, boy_obj.m) - 插入数据,此时boy_id = 1,因此只需要添加gril_id即可 boy_obj.m.add(2) boy_obj.m.add(2,4) boy_obj.m.add(*[1,2,3,4]) - 删除数据 boy_obj.m.remove(2) boy_obj.m.remove(2,4) boy_obj.m.remove(*[1,2,3,4]) - 修改数据 boy_obj.m.set([1,]) # 将原来和boy_id=1有关系的行全部删除,然后重新添加一行 gril_id=1 - 查询数据 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() 2 反向 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() 3 整合django自动创建 + 自己创建 class Boy(models.Model): name = models.CharField(max_length=32) m = models.ManyToManyField('Girl',through='Love', through_fields=('b', 'g')) class Girl(models.Model): name = models.CharField(max_length=32) 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字段
10 补充orm字段 - 类型: 1 字符串类: CharField() 2 数字类: IntegerField() DecimalField() 3 时间类: DateField() DateTimeField() 4 枚举类: color_list = ( (1, '黑色'), (2, '白色'), (3, '蓝色') ) color = models.IntegerField(choices=color_list) 应用场景: 选项固定,(和foreignkey不冲突,foreignkey是选项可扩展) ps: django-admin需要注意的字段类型(先做正则的验证) - email - IP - URL - UUID - 参数 - 给数据库用的参数(不考虑django-admin) 1 是否为空 null=True 2 默认值 default = 11 3 单列索引 db_index=True 4 单列唯一索引 unique=True 5 主键 primeKey=True 6 max_length 7 联合索引 - 联合唯一索引 class Meta: unique_together=( ('a', 'b'), ) - 联合索引 class Meta: unique_together=( ('a', 'b'), ) - 给django admin使用的参数 1 blank=True # admin页添加时,允许为空 2 verbose_name 3 error_message 4 validators
11补充自关联(相亲)
补充orm 自关联:(表A关联表A本身) 相亲里面,UserInfo表男生和UserInfo表女生关联(表自关联) - ForeinKey参数补充to, to_field class User(models.Model): username = models.CharField(max_length=32) ut = models.ForeinKey(to='UserType', to_field='title') class UserType(models.Model): title = models.CharField(max_length=64) - ForeignKey 自关联 #数据库表设置 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='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=3, gender=1).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=32) username = models.CharField(max_length=32) password = models.CharField(max_length=64) gender_choice = ( (1, '男'), (2, '女'), ) 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=3).first() result = lzp.m.all() # print(result) # result类型为[userInfo对象, userInfo对象,] for row in result: print(row.username) # 已知女生id,查相关男生名字 xzq = models.UserInfo.objects.filter(id=4).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