Django - ORM操作

目录 ORM介绍 创建ORM类 增删改查 进阶查询 类的字段和参数 ORM连表的几种类型 ORM连表操作 浅谈ORM查询性能 Django自带ContentType表 其他小技巧 参考博客 ORM介绍 ORM的两种方式 db first 先连接数据库 -> ... code first 先创建类 -> sqlachemy、Django、大多数都是 Django ORM ORM:Object Relational Mapping(关系对象映射) 类名 ->> 数据库中的表名 类属性 ->> 数据库里的字段 类实例 ->> 数据库表里的一行数据 obj.name..... ->> 类实例对象的属性 Django orm的优势:Django的orm操作本质上会根据对接的数据库引擎,翻译成对应的sql语句;所有使用Django开发的项目无需关心程序底层使用的是MySQL、Oracle、sqlite....,如果数据库迁移,只需要更换Django的数据库引擎即可 QuerySet数据类型介绍 QuerySet特点: 可迭代的 可切片 惰性计算:等于一个生成器,.objects.all()或者.filter()等都只是返回了一个QuerySet的查询结果集对象,它并不会马上执行sql,而是当调用QuerySet的时候才执行。 缓存机制:每一次数据库查询结果QuerySet都会对应一块缓存,再次使用该QuerySet时,不会发生新的SQL操作 这样减小了频繁操作数据库给数据库带来的压力 但是有时候取出来的数据量太大会撑爆缓存,可以使用迭代器解决这个问题: models.Publish.objects.all().iterator() 创建ORM类 1. 在models里创建表的类 /app/models.py from django.db import models # 表名为app01_userinfo class UserInfo(models.Model): # 自动创建id列,自增,主键 # 列名,字符串类型,指定长度 username = models.CharField(max_length=32) password = models.CharField(max_length=64) email = models.EmailField(max_length=19) 类的字段和参数详见字段和参数 2. 注册APP /./settings.py INSTALLED_APPS = [ ..., 'app01', ] 3. 执行命令,每次更改表结构都要重复一遍 python manage.py makemigrations -> 生成表结构的缓存 python manage.py migrate -> 创建表结构 4. 默认使用sqlite3数据库,可修改为mysql /./settings.py -> DATABASES ********** 注意 *********** Django默认使用MySQLdb模块链接MySQL 主动修改为pymysql,在project同名文件夹下的__init__文件中添加如下代码即可: import pymysql pymysql.install_as_MySQLdb() 增删改查 1.增 /app/views.py from app01 import models def orm(request): # 直接传入参数 models.UserInfo.objects.create(username='root',password='123') # 传入字典 dic = {'username': 'eric', 'password': '666'} models.UserInfo.objects.create(**dic) # 另一种增加方式 obj = models.UserInfo(username='alex',password='123') obj.save() 2.查 result = models.UserInfo.objects.all() result = models.UserInfo.objects.filter(user='root',psd='123') -> filter传入字典也可 **dic => QuerySet, Django的一种列表, [], 内部元素是.obj => [obj(id,username),obj] # 转化为字典输出 .all().values('id','caption') -> [{'id:1,'username':'alex'},{},{}] # 转化为tuple输出 .all().values_list('id','caption') -> [(1,'alex'),(),()] # 取第一个obj .filter(xxx).first() -> 不存在返回None => 用get取单条数据,如果不存在,直接报错 => models.UserInfo.objects.get(id=nid) # 计数 .filter(name='seven').count() # 切片 .all()[10:20] .all()[::2] .all()[6] # 索引 # 去重 .distinct() # 排序 .filter(name='seven').order_by('id') -> asc .filter(name='seven').order_by('-id') -> desc 3.删 models.UserInfo.objects.filter(username="alex").delete() 4.改 models.UserInfo.objects.filter(id=3).update(password="69") # 可添加**kwargs形式 # 或者先查找对象再修改保存 obj = models.tb.objects.get(id=1) obj.c1 = '111' obj.save() # 修改单条数据 特殊的判断语句(神奇的双下划线1) # 大于小于 .filter(id__gt=1) -> > 1 .filter(id=1) -> = 1 .filter(id__lt=1) -> < 1 .filter(id__lte=1) -> <= 1 .filter(id__gte=1) -> >= 1 .exclude(id__gt=1) -> != 1 exclude 除了...与filter相反 .filter(id__gt=1, id__lt=10) -> 1< x <10 # 范围range .filter(id__range=[1,3]) -> [1~3] bettwen + and # 范围in .filter(id__in=[1,2,3]) -> in [1,2,3] .exclude(id__in=[1,2,3]) -> in [1,2,3] # 是否为空 .filter(name__isnull=True) # 包含、开头、结尾 __startswith, istartswith, endswith, iendswith .filter(name__contains="ven") .filter(name__icontains="ven") # i 忽略大小写 # regex正则匹配,iregex 不区分大小写 .get(title__regex=r'^(An?|The) +') .get(title__iregex=r'^(an?|the) +') # date .filter(pub_date__date=datetime.date(2005, 1, 1)) .filter(pub_date__date__gt=datetime.date(2005, 1, 1)) # year、month、day、week_day .filter(pub_date__year=2005) .filter(pub_date__year__gte=2005) # hour、minute、second .filter(timestamp__hour=23) .filter(time__hour=5) .filter(timestamp__hour__gte=12) 进阶查询 F模块,用于获取对象中的某一字段(列)的值,并且对其进行操作; from django.db.models import F # 首先导入F模块 models.Book.objects.all().update(price=F('price')+1) # 每一本书的价格上调1块钱 Q模块,用于构造复杂的查询条件,使用逻辑关系(&与、|或、~非)组合进行多条件查询; 虽然filter中可以使用 , 隔开表示关系与,但没法表示或非的关系 from django.db.models import Q # 导入Q模块 # 方式一: .filter( Q(id__gt=10) ) -> .filter( Q(id=8) | Q(id__gt=10) ) -> or .filter( Q( Q(id=8) | Q(id__gt=10) ) & Q(caption='root') ) -> and, or # 方式二: # 可以组合嵌套 # q1里面的条件都是or的关系 q1 = Q() q1.connector = 'OR' q1.children.append(('id', 1)) q1.children.append(('id', 10)) q1.children.append(('id', 9)) # q2里面的条件都是or的关系 q2 = Q() q2.connector = 'OR' q2.children.append(('c1', 1)) q2.children.append(('c1', 10)) q2.children.append(('c1', 9)) # con通过and的条件把q1和q2联系到一块 con = Q() con.add(q1, 'AND') con.add(q2, 'AND') models.tb.objects.filter(con) 实例:查询作者姓名中包含 方/少/伟/3字,书名不包含伟,并且出版社地址以山西开头的书 book=models.Book.objects.filter( Q( Q(author__name__contains='方') | Q(author__name__contains='少') | Q(author__name__contains='伟') | Q(title__icontains='伟') ) & Q(publish__addr__contains='山西') ).values('title') 注意:Q查询和非Q查询混合使用,非Q查询一定要放在Q查询后面 extra方法 对不同的数据库引擎可能存在移植问题(因为你在显式的书写SQL语句),尽量避免使用extra a.映射 - select={'new_id':select count(1) from app01_usertype where id>%s'} - select_params=[1,] # 例: models.UserInfo.objects.all().extra( select={ 'n':"select count(1) from app01_utype WHERE id=%s or id=%s", 'm':"select count(1) from app01_uinfo WHERE id=%s or id=%s", }, select_params=[1,2,3,4] ) b.条件 - where=["foo='a' OR bar = 'a'", "baz = '%s'"], - params=['Lennon',] c.表 - tables=["app01_usertype"] d.排序 - order_by = ['-id'] # 例1: models.UserInfo.objects.extra( select={'new_id':select count(1) from app01_usertype where id>%s'}, select_params=[1,], where=['age>%s'], params=[18,], order_by=['-age'], tables=["app01_usertype'] ) -> 相当于: ''' select app01_userinfo.id, (select count(1) from app01_usertype where id>1) as new_id from app01_userinfo, app01_usertype where app01_userinfo.age>18 order by app01_userinfo.age desc ''' # 例2: current_user = models.UserInfo.objects.filter(username=username).first() # 当前用户 1、models.Article.objects.all() # 查出每一篇文章 2、models.Article.objects.all().filter(user=current_user) # 查出当前用户的所有文章 3、models.Article.objects.all().filter(user=current_user).extra(select={"filter_create_date":"strftime(‘%%Y/%%m‘,create_time)"}).values_list("filter_create_date") # 查出当前用户的所有文章的create_time,并且只取出年份和月份 执行原生SQL的三种方式 1.使用extra方法 结果集修改器,一种提供额外查询参数的机制 依赖model模型 2.使用raw方法 执行原始sql并返回模型 依赖model多用于查询 book = Book.objects.raw("select * from hello_book") for item in book: print(item.title) 3.使用cursor游标 不依赖model from django.db import connection, connections cursor = connection.cursor() # 或cursor = connections['default'].cursor() # 其中'default'是django数据库配置的default,也可取别的值 cursor.execute("""SELECT * from auth_user where id = %s""", [1]) row = cursor.fetchone() 类的字段和参数 字段:字符串、数字、时间、二进制 AutoField(Field) -> 自定义自增列(必须加primary_key=True) IntegerField(Field) -> 整数列 BooleanField(Field) -> 布尔 GenericIPAddressField(Field) -> IP验证(仅限django admin) URLField(CharField) -> url验证(仅限django admin) # Django里有很多的字段类型在数据库中都是Char类型,只是用于django admin便于区分 更多详见:武沛齐的博客 - Django 字段的参数: null -> db中是否可以为空 default='' -> 默认值 primary_key -> 是否主键 db_column -> 列名 db_index -> 是否可索引 unique -> 是否可唯一索引 unique_for_date -> 【日期】部分是否可索引 unique_for_month -> 【月】部分是否可索引 unique_for_year -> 【年】部分是否可索引 auto_now_add -> 创建时,自动生成时间 auto_now -> 更新时,自动更新为当前时间 # update方式不生效,先获取再更改才生效 ctime = models.DateTimeField(auto_now_add=True) UserGroup.objects.filter(id=1).update(caption='CEO') -> 不生效 obj = UserGroup.objects.filter(id=1).first() obj.caption = "CEO" -> 生效,自动更新更改时间 obj.save() # django admin中才生效的字段 blank -> django admin是否可以为空 verbose_name='' -> django admin显示字段中文 editable -> django admin是否可以被编辑 help_text -> django admin帮助提示 choices=[] -> django admin中显示下拉框 # 可避免连表查询,提高效率,一般用于基本不变的选项 user_type_choices = ( (1, '超级用户'), (2, '普通用户'), (3, '普普通用户'), ) user_type_id = models.IntegerField(choices=user_type_choices,default=1) error_messages -> 自定义错误信息(字典类型) # 字典的键:null, blank, invalid, invalid_choice, unique, unique_for_date # 例:error_messages = {'null': "不能为空", 'invalid': '格式错误'} validators -> django form ,自定义错误信息(列表类型) # 例: from django.core.validators import RegexValidator from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\ MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator error_messages={ 'c1': '优先错信息1', 'c2': '优先错信息2', 'c3': '优先错信息3', }, validators=[ RegexValidator(regex='root_\d+', message='错误了', code='c1'), RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'), EmailValidator(message='又错误了', code='c3'), ] 更多错误信息的使用方法参考武沛齐 - FORM 创建 Django admin用户: python manage.py createsuperuser Meta元信息 class UserInfo(models.Model): ... class Meta: # 定义数据库中生成的表名称 默认 app名称 + 下划线 + 类名 db_table = "table_name" # 联合索引 index_together = [("pub_date", "deadline"),] # 联合唯一索引,一旦三者都相同,则会被Django拒绝创建。 可以同时设置多组约束。为了方便,对于只有一组约束的情况下,可以简单地使用一维元素 unique_together = (("driver", "restaurant"),) # admin后台中显示的表名称 verbose_name = '用户信息' # verbose_name加s,复数形式,不指定自动加s verbose_name_plural = # 默认排序 ordering=['-order_date'] # 按订单降序排列,-表示降序,不加升序,加?表示随机 ordering=['-pub_date','author'] # 以pub_date为降序,再以author升序排列 更多:https://docs.djangoproject.com/en/1.10/ref/models/options/ Admin拓展知识 触发Model中的验证和错误提示有两种方式: a. Django Admin中的错误信息会优先根据Admin内部的ModelForm错误信息提示,如果都成功,才来检查Model的字段并显示指定错误信息 b. 调用Model对象的 clean_fields 方法,如: # models.py class UserInfo(models.Model): username = models.CharField(max_length=32) email = models.EmailField(error_messages={'invalid': '格式错了.'}) # views.py def index(request): obj = models.UserInfo(username='11234', email='uu') try: print(obj.clean_fields()) except Exception as e: print(e) return HttpResponse('ok') # Model的clean方法是一个钩子,可用于定制操作,如:上述的异常处理。 Admin中修改错误提示 # admin.py from django.contrib import admin from model_club import models from django import forms class UserInfoForm(forms.ModelForm): username = forms.CharField(error_messages={'required': '用户名不能为空.'}) email = forms.EmailField(error_messages={'invalid': '邮箱格式错误.'}) age = forms.IntegerField(initial=1, error_messages={'required': '请输入数值.', 'invalid': '年龄必须为数值.'}) class Meta: model = models.UserInfo # fields = ('username',) fields = "__all__" class UserInfoAdmin(admin.ModelAdmin): form = UserInfoForm admin.site.register(models.UserInfo, UserInfoAdmin) ORM连表的几种类型 ORM一对多 当一张表中创建一行数据时,有一个单选的下拉框(可以被重复选择) 创建表结构时关联外键 user_group = models.ForeignKey("UserGroup",to_field='uid') ->> obj(UserGroup) # 自动创建user_group_id列,存的是数字(关联主键) 添加数据时关联id或对象 方式一:创建数据时添加id关联 models.UserInfo.object.create(name='root', user_group_id=1) 方式二:查询obj对象进行关联 user_group = models.UserGroup.objects.filter(id=1).first() 一对多自关联 由原来的2张表,变成一张表! # 例:回复评论 class Comment(models.Model): news_id = models.IntegerField() -> 新闻ID content = models.CharField(max_length=32) -> 评论内容 user = models.CharField(max_length=32) -> 评论者 reply = models.ForeignKey('Comment',null=True,blank=True,related_name='xxxx') -> 回复ID # 注意:回复的id必须是已经存在的评论的id ORM多对多 在某表中创建一行数据是,有一个可以多选的下拉框 两种创建方式 以下两种创建方式建议都用,自动创建的只能关联两个表,自定义的可以不断关联 方式一:自定义关系表 可以直接操作第三张表,但无法通过字段跨表查询,查询麻烦 class UserInfo(models.Model): ... class UserGroup(models.Model): ... # 创建中间表 class UserInfoToUserGroup(models.Model): user_info_obj = models.ForeignKey(to='UserInfo',to_field='nid') group_obj = models.ForeignKey(to='UserGroup',to_field='id') # 添加关联数据: UserInfoToApp.objects.create(user_info_obj_id=1,group_obj_id=2) 方式二:Django自动创建关系表 可以使用字段跨表查询,但无法直接操作第三张表 class UserInfo(models.Model): ... # ManyToManyField字
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信