Django之模型ORM

ORM介绍

ORM概念

对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。

简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。

ORM在业务逻辑层和数据库层之间充当了桥梁的作用。

ORM总结

ORM只是一种工具,工具确实能解决一些重复,简单的劳动。这是不可否认的。

但我们不能指望某个工具能一劳永逸地解决所有问题,一些特殊问题还是需要特殊处理的。

但是在整个软件开发过程中需要特殊处理的情况应该都是很少的,否则所谓的工具也就失去了它存在的意义。

Django中的ORM

Model

在Django中model是你数据的单一、明确的信息来源。它包含了你存储的数据的重要字段和行为。通常,一个模型(model)映射到一个数据库表,

基本情况:

  • 每个模型都是一个Python类,它是django.db.models.Model的子类。
  • 模型的每个属性都代表一个数据库字段。
  • 综上所述,Django为您提供了一个自动生成的数据库访问API,详询官方文档链接

创建一个ORM过程

总结详细步骤:
1. 手动创建数据库
2. 在app/models.py里面写上一个类,必须继承models.Model这个类 (注意启动Django项目)
3. 在Django 项目的settings.py 里面 配置上数据库的相关信息
4. 在Django项目里的__init__.py里面写上 两句话 import pymysql pymysql.install_as_MySQLdb()
5. 给Django发布命令
1. python manage.py makemigrations # 相当于去你的models.py 里面看一下有没有改动
2. python manage.py migrate # 把改动翻译成SQL语句,然后去数据库执行 models.py --> Django翻译成SQL --> pymysql --> MySQL(4p)

总结一下:

models.py                      MySQL
类         对应         数据表
类的属性        对应        数据库里面的字段(列)
对象           对应        一行数据(一条数据)

ORM到底是什么? --> 对象关系映射

ORM操作

# 增
1. models.Tb1(表名).objects.create(c1='xx', c2='oo') # 增加一条数据,可以接受字典类型数据 **kwargs
  models.Tb1(表名).objects.create(**{})
2.obj = models.Tb1(c1='xx', c2='oo')
 obj.save() # 查
models.Tb1.objects.get(id=123) # 获取单条数据,不存在则报错(不建议)
models.Tb1.objects.all() # 获取全部
models.Tb1.objects.filter(name='seven') # 获取指定条件的数据 ,不满足条件不报错,但是返回的结果是一个对象的列表

 models.User.objects.filter().first()  # 获取指定条件的数据 ,的第一个

models.User.objects.filter().last()# 获取指定条件的数据,的最后一个

models.Tb1.objects.exclude(name='seven')  # 去除指定条件的数据

# 删
# models.Tb1.objects.filter(name='seven').delete() # 删除指定条件的数据 (不区分model对象和queryset对象) # 改
1.models.Tb1.objects.filter(name='seven').update(gender='0') # 将指定条件的数据更新,均支持 **kwargs 2. obj = models.Tb1.objects.get(id=1) #不建议使用,它是对id=1的整条记录都进行“更改”(除c1是对进行更改外,其余都是进行赋值)
  obj.c1 = '111'
  obj.save()

单表查询API汇总

返回的是QuerySet对象

  <1> all():             查询所有结果【】
  <2> filter(**kwargs):      它包含了与所给筛选条件相匹配的对象(queryset),返回的是一个列表,取一个值时需要用索引或切片,
                  什么时候使用filter()什么时候使用get()
                    当filter().后面继续使用的是Queryset对象时,
                    当get(). 后面跟的可以是字段或表名   <4> exclude(**kwargs):     它包含了与所给筛选条件不匹配的对象
  <5> values(*要显示的字段):         返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列 【】

      l=[]
      for obj in Book.objects.all():
        temp={}
        temp["title"]=obj.title
        temp["id"]=obj.id
        temp["publish__name"]=obj.publish.name # QuerySet() []
      l.append(temp)

  
  <6> values_list(*field):    它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列  
 
  <7> order_by(*field):      对查询结果排序  正序 order_by(-*field): 对查询结果排序  反序 
  <8> reverse():          对查询结果反向排序  
 
  <9> distinct():          从返回结果中剔除重复纪录(必须是满足所有条件都重复) 返回bool值   <3> exists():          如果QuerySet包含数据,就返回True,否则返回False  判断表里有没有数据用exists(),减少数据查询 返回数字的:
 
  <10> count():          返回数据库中匹配查询(QuerySet)的对象数量。
返回具体的数据(model)对象:   
  <11> first():          返回第一条记录  
  <12> last():            返回最后一条记录
  <13> get(**kwargs):        返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误

注意:一定区分Object与QuerySet的区别 !!!

QuerySet有update方法而Object默认没有。

单表查询之神奇的双下划线

models.Tb1.objects.filter(id__gt=1, id__lt=10)   # 获取 id 大于1的值 且 id 小于10的值

models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in models.Tb1.objects.filter(name__contains="ven") # 获取name字段包含"ven"的
models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感 models.Tb1.objects.filter(id__range=[1, 3]) # id范围是1到3的,等价于SQL的bettwen and 类似的还有:startswith,istartswith, endswith, iendswith  date字段还可以:
models.Class.objects.filter(first_day__year=2017)
date字段还可以:
  models.Class.objects.filter(first_day__year=2017 查询某字段(sname)为空的:
  models.Class.objects.cname__isnull=True

ORM跨表查询

 基于对象查询(sql:子查询)
一对多的关系 多对多的关系 一对一的关系
正向查询,按字段(外键):
     反向查询,按表名小写_set:

 基于Queryset和 __(sql:join语句):

  一对多的关系  多对多的关系 一对一的关系

     正向查询,按字段(外键)
     反向查询,按表名小写

verbose_name
verbose_name:
可以通过在 ForeignKey() 和ManyToManyField的定义中设置 related_name 的值来覆写 FOO_set 的名称。例如,如果 Article model 中做一下更改: publish = ForeignKey(Blog, related_name='bookList'),那么接下来就会如我们看到这般: # 查询 人民出版社出版过的所有书籍(反向:表名_set) publish=Publish.objects.get(name="人民出版社") book_list=publish.bookList.all() # 与人民出版社关联的所有书籍对象集合 ######################################################################
# print(type(obj._meta))#
print(obj._meta.related_objects) #获取关联当前表(obj)的所有字段对象集合 from django.db.models.fields.reverse_related import ManyToOneRel for obj_related_field in obj._meta.related_objects:
print(obj_related_field.field_name) # 关联字段对象的to_field的值
print(obj_related_field.related_name) # 反向查询别名,每张表中的名字唯一
print(obj_related_field.limit_choices_to) # 筛选条件(limit_choices_to=[条件],对被关联的表条件限制)
print(obj_related_field.to) # 关联字段关联的表
print(obj_related_field.field.model._meta.model_name) # 关联字段对应的model
ORM 跨表查询
class Book(models.Model):
title = models.CharField( max_length=32) publish=models.ForeignKey(to="Publish",to_field="id")
authors=models.ManyToManyField(to='Author',related_name='bookList')  class Publish(models.Model):
name=models.CharField( max_length=32) class Author(models.Model):
name=models.CharField( max_length=32)
ad=models.OneToOneField("AuthorDetail") class AuthorDetail(models.Model): telephone=models.BigIntegerField() 基于对象查询(sql:子查询) 一对多的关系 (Publish--Book)
正向查询,按字段(外键): 查询python这本书的出版社所在的名称
book_obj=Book.objects.filter(title="python").first()
#print(book_obj.publish.name) 反向查询,按表名小写_set: 人民出版社出版过的所有书籍名称 publish_obj=Publish.objects.filter(name="人民出版社出版").first()
print(publish_obj.book_set.all())
for obj in publish_obj.book_set.all():
print(obj.title) 多对多的关系
正向查询,按字段:
python这本书所有作者的名字
book_obj=Book.objects.filter(title="python").first()
book_obj.authors.all() 反向查询,按表名小写_set:
alex出版过的所有书籍名称
alex=Author.objects.filter(name="alex").first()
alex.bookList.all() 一对一的关系
正向查询,按字段:
查询alex的手机号
alex=Author.objects.filter(name="alex").first()
alex.ad.telephone 反向查询:按表名小写
以151开头的手机号的作者的名字
ad=AuthorDetail.objects.get(telephone__startswith="")
ad.author.name 基于Queryset和 __(sql:join语句): 正向查询,按字段(外键)
反向查询,按表名小写 一对多的关系 (Publish--Book) 查询python这本书的所在出版社的名称
Book.objects.filter(title="python").values("publish__name") #原理
for obj in Book.objects.filter(title="python"):
temp={}
temp["publish__name"]=obj.publish.name 人民出版社出版过的所有书籍名称
Publish.objects.filter(name="人民出版社出版").values("book__title") 多对多的关系 python这本书所有作者的名字
Book.objects.filter(title="python").values("authors__name") alex出版过的所有书籍名称
Author.objects.filter(name="alex").values("book__title") 一对一的关系 查询alex的手机号
Author.objects.filter(name="alex").values("ad__telephone") 以151开头的手机号的作者的名字
AuthorDetail.objects.filter(telephone__startswith="").values("author__name") 扩展:
练习1:
查询python这本书的所在出版社的名称
Book.objects.filter(title="python").values("publish__name")
Publish.objects.filter(book__title="python").values("name") 练习2:
手机号以151开头的作者出版过的所有书籍名称以及出版社名称 Book.objects.filter(authors__ad__telephone__startswith="").values("title","publish__name") 聚合:
计算所有图书的平均价格:
book.objects.all().aggregate(Avg("price")) 分组查询: 查询每一个出版社出版过的书籍个数 Publish.objects.annotate(Count("book__id"))
select count(*) from publish group by id SELECT "app01_publish"."name", COUNT("app01_book"."id") AS "c" FROM "app01_publish" LEFT OUTER JOIN "app01_book" ON ("app01_publish"."id" = "app01_book"."publish_id") GROUP BY "app01_publish"."id", "app01_publish"."name" publish: book: id name id title publish_id
1 A 1 python 1
2 B 2 go 1
3 C 3 linux 2
4 php 3
5 java 3 publish-book: nid publish_id publish_name book_id book_title publish_id
1 1 A 1 python 1
2 1 A 2 go 1
3 2 B 3 linux 2
4 3 C 4 php 3
5 3 C 5 java 3

ORM跨表查询

ORM特殊查询

#表名小写
book.model._meta.model_name #book所在app名
book.model._meta.app_label #获取book表下name字段对象,可以对象.属性(field_obj.verbose_name)
field_obj=book.model._meta.get_field(name)

多对多对应关系建表方法

# 第一种   自定义第三张表,通过外键与其他两张表关联
class TeacherToClass(models.Model):
tid=models.ForeignKey("Teacher")
cid=models.ForeignKey("Class") #联合唯一,不能同时相等
class Meta:
unique_together=[("tid","cid"),] # 第二种 老师表 通过ManyToManyField直接创建
# 自动创建第三张关系表,没有相关类与其对应,不能使用ORM单独操作
#但是可以使用Django封装的方式法 class Teacher(models.Model):
tname=models.CharField(max_length=32)
cid=models.ManyToManyField(to="Class",related_name="teacher")
# Teaccher.cid 是Djando自带的功能,是一个中间桥梁(关联表),因为Teacher表和Class表没有直接关系
#Django把关系包装成一个具体的对象(Teaccher.cid.字段(class表中)) def __str__(self):
return self.tname #第三种 通过外键和ManyToManyField 手动创建关联表(中间模型)
#可以增加第三张表的字段,可以单独用第三张表进行相关操作
#不能用Django ORM 多对多操作的语法(add,set,remove,clear)
class Teacher(models.Model):
tname = models.CharField(max_length=32) cid = models.ManyToManyField(to="Class", through="Teacher2Class", through_fields=("tid", "cid")) class Teacher2Class(models.Model):
tid = models.ForeignKey(to="Teacher")
cid = models.ForeignKey(to="Class") class Meta:
unique_together = ("tid", "cid")

三种方法

注意事项

1.触发Model中的验证和错误提示有两种方式:
a. Django Admin中的错误信息会优先根据Admiin内部的ModelForm错误信息提示,如果都成功,才来检查Model的字段并显示指定错误信息
b. 使用ModelForm
c. 调用Model对象的 clean_fields 方法,如:
# models.py
class UserInfo(models.Model):
nid = models.AutoField(primary_key=True)
username = models.CharField(max_length=32) email = models.EmailField(error_messages={'invalid': '格式错了.'}) # views.py
def index(request):
obj = models.UserInfo(username='', email='uu')
try:
print(obj.clean_fields())
except Exception as e:
print(e)
return HttpResponse('ok') # Model的clean方法是一个钩子,可用于定制操作,如:上述的异常处理。 2.Admin中修改错误提示
# admin.py
from django.contrib import admin
from model_club import models
from django import forms class UserInfoForm(forms.ModelForm):
age = forms.IntegerField(initial=1, error_messages={'required': '请输入数值.', 'invalid': '年龄必须为数值.'}) class Meta:
model = models.UserInfo
# fields = ('username',)
fields = "__all__"
exclude = ['title']
labels = { 'name':'Writer', }
help_texts = {'name':'some useful help text.',}
error_messages={ 'name':{'max_length':"this writer name is too long"} }
widgets={'name':Textarea(attrs={'cols':80,'rows':20})} class UserInfoAdmin(admin.ModelAdmin):
form = UserInfoForm admin.site.register(models.UserInfo, UserInfoAdmin)

注意事项

字段参数

null                数据库中字段是否可以为空
db_column 数据库中字段的列名
default 数据库中字段的默认值
primary_key 数据库中字段是否为主键
db_index 数据库中字段是否可以建立索引
unique 数据库中字段是否可以建立唯一索引
unique_for_date 数据库中字段【日期】部分是否可以建立唯一索引
unique_for_month 数据库中字段【月】部分是否可以建立唯一索引
unique_for_year 数据库中字段【年】部分是否可以建立唯一索引 verbose_name Admin中显示的字段名称
blank Admin中是否允许用户输入为空
editable Admin中是否可以编辑
help_text Admin中该字段的提示信息
choices Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1) error_messages 自定义错误信息(字典类型),从而定制想要显示的错误信息;
字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
如:{'null': "不能为空.", 'invalid': '格式错误'} validators 自定义错误验证(列表类型),从而定制想要的验证规则
from django.core.validators import RegexValidator
from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\
MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
如:
test = models.CharField(
max_length=32,
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'), ]
)

字段参数

元信息

class UserInfo(models.Model):
nid = models.AutoField(primary_key=True)
username = models.CharField(max_length=32)
class Meta:
# 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
db_table = "table_name" # 联合索引
index_together = [
("pub_date", "deadline"),
] # 联合唯一索引
unique_together = (("driver", "restaurant"),) # admin中显示的表名称
verbose_name # verbose_name加s
verbose_name_plural

元信息

多表关系和参数

ForeignKey(ForeignObject) # ForeignObject(RelatedField)
to, # 要进行关联的表名
to_field=None, # 要关联的表中的字段名称
on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为
- models.CASCADE,删除关联数据,与之关联也删除
- models.DO_NOTHING,删除关联数据,引发错误IntegrityError
- models.PROTECT,删除关联数据,引发错误ProtectedError
- models.SET_NULL,删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
- models.SET_DEFAULT,删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
- models.SET,删除关联数据,
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象) def func():
return 10 class MyModel(models.Model):
user = models.ForeignKey(
to="User",
to_field="id"
on_delete=models.SET(func),)
related_name=None, # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件:
# 如:
- limit_choices_to={'nid__gt': 5}
- limit_choices_to=lambda : {'nid__gt': 5} from django.db.models import Q
- limit_choices_to=Q(nid__gt=10)
- limit_choices_to=Q(nid=8) | Q(nid__gt=10)
- limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
db_constraint=True # 是否在数据库中创建外键约束
parent_link=False # 在Admin中是否显示关联数据 OneToOneField(ForeignKey)
to, # 要进行关联的表名
to_field=None # 要关联的表中的字段名称
on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为 ###### 对于一对一 ######
# 1. 一对一其实就是 一对多 + 唯一索引
# 2.当两个类之间有继承关系时,默认会创建一个一对一字段
# 如下会在A表中额外增加一个c_ptr_id列且唯一:
class C(models.Model):
nid = models.AutoField(primary_key=True)
part = models.CharField(max_length=12) class A(C):
id = models.AutoField(primary_key=True)
code = models.CharField(max_length=1) ManyToManyField(RelatedField)
to, # 要进行关联的表名
related_name=None, # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件:
# 如:
- limit_choices_to={'nid__gt': 5}
- limit_choices_to=lambda : {'nid__gt': 5} from django.db.models import Q
- limit_choices_to=Q(nid__gt=10)
- limit_choices_to=Q(nid=8) | Q(nid__gt=10)
- limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
symmetrical=None, # 仅用于多对多自关联时,symmetrical用于指定内部是否创建反向操作的字段
# 做如下操作时,不同的symmetrical会有不同的可选字段
models.BB.objects.filter(...) # 可选字段有:code, id, m1
class BB(models.Model): code = models.CharField(max_length=12)
m1 = models.ManyToManyField('self',symmetrical=True) # 可选字段有: bb, code, id, m1
class BB(models.Model): code = models.CharField(max_length=12)
m1 = models.ManyToManyField('self',symmetrical=False) through=None, # 自定义第三张表时,使用字段用于指定关系表
through_fields=None, # 自定义第三张表时,使用字段用于指定关系表中那些字段做多对多关系表
from django.db import models class Person(models.Model):
name = models.CharField(max_length=50) class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through='Membership',
through_fields=('group', 'person'),
) class Membership(models.Model):
group = models.ForeignKey(Group, on_delete=models.CASCADE)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
inviter = models.ForeignKey(
Person,
on_delete=models.CASCADE,
related_name="membership_invites",
)
invite_reason = models.CharField(max_length=64)
db_constraint=True, # 是否在数据库中创建外键约束
db_table=None, # 默认创建第三张表时,数据库中表的名称 多表关系和参数

多表关系和参数

进阶操作

# 获取个数
#
# models.Tb1.objects.filter(name='seven').count() # 大于,小于
#
# models.Tb1.objects.filter(id__gt=1) # 获取id大于1的值
# models.Tb1.objects.filter(id__gte=1) # 获取id大于等于1的值
# models.Tb1.objects.filter(id__lt=10) # 获取id小于10的值
# models.Tb1.objects.filter(id__lte=10) # 获取id小于10的值
# models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值 # in
#
# models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据
# models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in # isnull
# Entry.objects.filter(pub_date__isnull=True) # contains
#
# models.Tb1.objects.filter(name__contains="ven")
# models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
# models.Tb1.objects.exclude(name__icontains="ven") # range
#
# models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and # 其他类似
#
# startswith,istartswith, endswith, iendswith, # order by
#
# models.Tb1.objects.filter(name='seven').order_by('id') # asc
# models.Tb1.objects.filter(name='seven').order_by('-id') # desc # group by
#
# from django.db.models import Count, Min, Max, Sum
# models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))
# SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id" # limit 、offset
#
# models.Tb1.objects.all()[10:20] # regex正则匹配,iregex 不区分大小写
#
# Entry.objects.get(title__regex=r'^(An?|The) +')
# Entry.objects.get(title__iregex=r'^(an?|the) +') # date
#
# Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
# Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1)) # year
#
# Entry.objects.filter(pub_date__year=2005)
# Entry.objects.filter(pub_date__year__gte=2005) # month
#
# Entry.objects.filter(pub_date__month=12)
# Entry.objects.filter(pub_date__month__gte=6) # day
#
# Entry.objects.filter(pub_date__day=3)
# Entry.objects.filter(pub_date__day__gte=3) # week_day
#
# Entry.objects.filter(pub_date__week_day=2)
# Entry.objects.filter(pub_date__week_day__gte=2) # hour
#
# Event.objects.filter(timestamp__hour=23)
# Event.objects.filter(time__hour=5)
# Event.objects.filter(timestamp__hour__gte=12) # minute
#
# Event.objects.filter(timestamp__minute=29)
# Event.objects.filter(time__minute=46)
# Event.objects.filter(timestamp__minute__gte=29) # second
#
# Event.objects.filter(timestamp__second=31)
# Event.objects.filter(time__second=2)
# Event.objects.filter(timestamp__second__gte=31)

进阶操作

高级操作

# extra
# 在QuerySet的基础上继续执行子语句
# extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None) # select和select_params是一组,where和params是一组,tables用来设置from哪个表
# Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
# Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
# Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
# Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid']) 举个例子:
models.UserInfo.objects.extra(
select={'newid':'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 newid
from app01_userinfo,app01_usertype
where
app01_userinfo.age > 18
order by
app01_userinfo.age desc
""" # 执行原生SQL
# 更高灵活度的方式执行原生SQL语句
# from django.db import connection, connections
# cursor = connection.cursor() # cursor = connections['default'].cursor()
# cursor.execute("""SELECT * from auth_user where id = %s""", [1])
# row = cursor.fetchone()

高级操作

QuerySet相关方法

##################################################################
# PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET #
################################################################## def all(self)
# 获取所有的数据对象 def filter(self, *args, **kwargs)
# 条件查询
# 条件可以是:参数,字典,Q def exclude(self, *args, **kwargs)
# 条件查询
# 条件可以是:参数,字典,Q def select_related(self, *fields)
性能相关:表之间进行join连表操作,一次性获取关联的数据。 总结:
1. select_related主要针一对一和多对一关系进行优化。
2. select_related使用SQL的JOIN语句进行优化,通过减少SQL查询的次数来进行优化、提高性能。 def prefetch_related(self, *lookups)
性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。 总结:
1. 对于多对多字段(ManyToManyField)和一对多字段,可以使用prefetch_related()来进行优化。
2. prefetch_related()的优化方式是分别查询每个表,然后用Python处理他们之间的关系。 def annotate(self, *args, **kwargs)
# 用于实现聚合group by查询 from django.db.models import Count, Avg, Max, Min, Sum v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id'))
# SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1)
# SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1 v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1)
# SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1 def distinct(self, *field_names)
# 用于distinct去重
models.UserInfo.objects.values('nid').distinct()
# select distinct nid from userinfo 注:只有在PostgreSQL中才能使用distinct进行去重 def order_by(self, *field_names)
# 用于排序
models.UserInfo.objects.all().order_by('-id','age') def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
# 构造额外的查询条件或者映射,如:子查询 Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid']) def reverse(self):
# 倒序
models.UserInfo.objects.all().order_by('-nid').reverse()
# 注:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序 def defer(self, *fields):
models.UserInfo.objects.defer('username','id')

models.UserInfo.objects.filter(...).defer('username','id')
#映射中排除某列数据 def only(self, *fields):
#仅取某个表中的数据
models.UserInfo.objects.only('username','id')

models.UserInfo.objects.filter(...).only('username','id') def using(self, alias):
指定使用的数据库,参数为别名(setting中的设置) ##################################################
# PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
################################################## def raw(self, raw_query, params=None, translations=None, using=None):
# 执行原生SQL
models.UserInfo.objects.raw('select * from userinfo') # 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名
models.UserInfo.objects.raw('select id as nid from 其他表') # 为原生SQL设置参数
models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,]) # 将获取的到列名转换为指定列名
name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
Person.objects.raw('SELECT * FROM some_other_table', translations=name_map) # 指定数据库
models.UserInfo.objects.raw('select * from userinfo', using="default") ################### 原生SQL ###################
from django.db import connection, connections
cursor = connection.cursor() # cursor = connections['default'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1])
row = cursor.fetchone() # fetchall()/fetchmany(..) def values(self, *fields):
# 获取每行数据为字典格式 def values_list(self, *fields, **kwargs):
# 获取每行数据为元祖 def dates(self, field_name, kind, order='ASC'):
# 根据时间进行某一部分进行去重查找并截取指定内容
# kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)
# order只能是:"ASC" "DESC"
# 并获取转换后的时间
- year : 年-01-01
- month: 年-月-01
- day : 年-月-日 models.DatePlus.objects.dates('ctime','day','DESC') def datetimes(self, field_name, kind, order='ASC', tzinfo=None):
# 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间
# kind只能是 "year", "month", "day", "hour", "minute", "second"
# order只能是:"ASC" "DESC"
# tzinfo时区对象
models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)
models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai')) """
pip3 install pytz
import pytz
pytz.all_timezones
pytz.timezone(‘Asia/Shanghai’)
""" def none(self):
# 空QuerySet对象 ####################################
# METHODS THAT DO DATABASE QUERIES #
#################################### def aggregate(self, *args, **kwargs):
# 聚合函数,获取字典类型聚合结果
from django.db.models import Count, Avg, Max, Min, Sum
result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid'))
===> {'k': 3, 'n': 4} def count(self):
# 获取个数 def get(self, *args, **kwargs):
# 获取单个对象 def create(self, **kwargs):
# 创建对象 def bulk_create(self, objs, batch_size=None):
# 批量插入
# batch_size表示一次插入的个数
objs = [
models.DDD(name='r11'),
models.DDD(name='r22')
]
models.DDD.objects.bulk_create(objs, 10) def get_or_create(self, defaults=None, **kwargs):
# 如果存在,则获取,否则,创建
# defaults 指定创建时,其他字段的值
obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '','u_id': 2, 't_id': 2}) def update_or_create(self, defaults=None, **kwargs):
# 如果存在,则更新,否则,创建
# defaults 指定创建时或更新时的其他字段
obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '','u_id': 2, 't_id': 1}) def first(self):
# 获取第一个 def last(self):
# 获取最后一个 def in_bulk(self, id_list=None):
# 根据主键ID进行查找
id_list = [11,21,31]
models.DDD.objects.in_bulk(id_list) def delete(self):
# 删除 def update(self, **kwargs):
# 更新 def exists(self):
# 是否有结果 其他操作

QuerySet相关方法

Dajngo数据库操作

Django项目使用MySQL数据库

1. 在Django项目的settings.py文件中,配置数据库连接信息:

DATABASES = {
"default": {
"ENGINE": "django.db.backends.mysql",
"NAME": "你的数据库名称", # 需要自己手动创建数据库
"USER": "数据库用户名",
"PASSWORD": "数据库密码",
"HOST": "数据库IP",
"POST": 3306
}
}

2. 在Django项目的__init__.py文件中写如下代码,告诉Django使用pymysql模块连接MySQL数据库:

import pymysql

pymysql.install_as_MySQLdb()

多数据库操作

model.py

from django.db import models

class UserType(models.Model):
title = models.CharField(max_length=) def __str__(self):
return self.title
class UserInfo(models.Model):
name = models.CharField(max_length=)
email = models.CharField(max_length=)
ut = models.ForeignKey(to='UserType',db_constraint=False)

seetings.py

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
},
'db1': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db1.sqlite3'),
}
}

数据化迁移:

1、python manage.py makemigraions

#将哪个app下的表迁移到哪个数据库
2、python manage.py migrate app01 --database=db1

views.py

1、手动操作

using()选择数据库
  #选择哪个数据库插入数据
models.UserType.objects.using('db1').create(title='普通用户')
  #选择查询哪个数据库数据
result = models.UserType.objects.all().using('default')

2、自动操作

  、创建db_router.py文件
  、在settings.py中配置
    DATABASE_ROUTERS = ['db_router.Router1',]
 
  db_router.py 
class Router1:
     #读数据时选择db1数据库
def db_for_read(self, model, **hints):
"""
Attempts to read auth models go to auth_db.
"""
return 'db1'
    #写数据时选择default数据库
def db_for_write(self, model, **hints):
"""
Attempts to write auth models go to auth_db.
"""
return 'default'
    
  views.py
    #写操作
    models.UserType.objects.create(title='VVIP')
    #读操作
    result = models.UserType.objects.all()   补充:更细粒度的操作
    class Router1:

def db_for_read(self, model, **hints):
  #如果读usertype表时,使用db1数据库;读其他表时,使用default数据库
  if model._meta.model_name == 'usertype':
    return 'db1'
  else:
    return 'default'

   def db_for_write(self, model, **hints):

     return 'default'

分库

app01中的表在default数据库创建
app02中的表在db1数据库创建

# 第一步:
python manage.py makemigraions # 第二步:
app01中的表在default数据库创建
python manage.py migrate app01 --database=default # 第三步:
app02中的表在db1数据库创建
python manage.py migrate app02 --database=db1

手动操作:

    m1.UserType.objects.using('default').create(title='VVIP')
m2.Users.objects.using('db1').create(name='VVIP',email='xxx')

自动操作:和上面步骤一样

db_router.py

class Router1:
def db_for_read(self, model, **hints):
#读app01下的表时,使用default数据库
if model._meta.app_label == 'app01':
return 'default'
else:
return 'db1' def db_for_write(self, model, **hints):
     #对app01下的表写入数据时,使用defalut数据库 
if model._meta.app_label == 'app01':
return 'default'
else:
return 'db1' views.py

  m1.UserType.objects.using('default').create(title='VVIP')
  m2.Users.objects.using('db1').create(name='VVIP',email='xxx')

约束数据化迁移

class Router1:
def allow_migrate(self, db, app_label, model_name=None, **hints):
     
     #数据化迁移时,必须满足将app02下的表放入db1数据库中,否则迁移不成功 if db=='db1' and app_label == 'app02':
return True
elif db == 'default' and app_label == 'app01':
return True
else:
return False
  # 如果返回None,那么表示交给后续的router,如果后续没有router,则相当于返回True
  
   def db_for_write(self, model, **hints):
      pass

性能优化查询

参考

待续......

上一篇:Django的日常-模型层(1)


下一篇:Django模型系统——ORM