Python学习(三十四)—— Django之ORM之单表、联表操作
一、单表查询API汇总
<1> all(): 查询所有结果 <2> filter(**kwargs): 它包含了与所给筛选条件相匹配的对象 <3> get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。 <4> exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象 <5> values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列 <6> values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列 <7> order_by(*field): 对查询结果排序 <8> reverse(): 对查询结果反向排序 <9> distinct(): 从返回结果中剔除重复纪录,配合values和values_list使用 <10> count(): 返回数据库中匹配查询(QuerySet)的对象数量。 <11> first(): 返回第一条记录 <12> last(): 返回最后一条记录 <13> exists(): 如果QuerySet包含数据,就返回True,否则返回False
注意:一定区分Object与QuerySet的区别 !!!
QuerySet有update方法而Object默认没有。
单表查询之神奇的双下划线
models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于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)
备注:
在Django的日志设置中,配置上一个名为django.db.backends的logger实例即可查看翻译后的SQL语句。
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } }
Django项目完整版LOGGING配置:
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'standard': { 'format': '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' '[%(levelname)s][%(message)s]' }, 'simple': { 'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' }, 'collect': { 'format': '%(message)s' } }, 'filters': { 'require_debug_true': { '()': 'django.utils.log.RequireDebugTrue', }, }, 'handlers': { 'console': { 'level': 'DEBUG', 'filters': ['require_debug_true'], # 只有在Django debug为True时才在屏幕打印日志 'class': 'logging.StreamHandler', 'formatter': 'simple' }, 'default': { 'level': 'INFO', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自动切 'filename': os.path.join(BASE_LOG_DIR, "xxx_info.log"), # 日志文件 'maxBytes': 1024 * 1024 * 50, # 日志大小 50M 'backupCount': 3, 'formatter': 'standard', 'encoding': 'utf-8', }, 'error': { 'level': 'ERROR', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自动切 'filename': os.path.join(BASE_LOG_DIR, "xxx_err.log"), # 日志文件 'maxBytes': 1024 * 1024 * 50, # 日志大小 50M 'backupCount': 5, 'formatter': 'standard', 'encoding': 'utf-8', }, 'collect': { 'level': 'INFO', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自动切 'filename': os.path.join(BASE_LOG_DIR, "xxx_collect.log"), 'maxBytes': 1024 * 1024 * 50, # 日志大小 50M 'backupCount': 5, 'formatter': 'collect', 'encoding': "utf-8" } }, 'loggers': { # 默认的logger应用如下配置 '': { 'handlers': ['default', 'console', 'error'], # 上线之后可以把'console'移除 'level': 'DEBUG', 'propagate': True, }, # 名为 'collect'的logger还单独处理 'collect': { 'handlers': ['console', 'collect'], 'level': 'INFO', } }, }
二、基于对象的跨表查询
一对多查询(班级表和学生表)
正向查询(由学生表查询班级表)
查询学生的班级信息
>>> student_obj = models.Student.objects.first() >>> student_obj.cid # 通过model类中的属性查找到对应的外键数据对象 <Class: Class object> >>> student_obj.cid.cname '1班' >>> student_obj.cid_id # 获取实际外键的值 1
反向查询(由班级表查询学生表)
查询班级的学生信息
>>> class_obj = models.Class.objects.first() # 获取第一个班级对象 >>> class_obj.student_set.all() # 通过表名_set反向查询出所有的学生 <QuerySet [<Student: Student object>, <Student: Student object>]>
注意:
如果不在外键的字段中设置related_name的话,默认就用表名_set。
如果设置了related_name="students",反向查询时可直接使用students进行反向查询。
>>> class_obj.students.all()
一对一查询
表结构设计
class Student(models.Model): sname = models.CharField(max_length=32, verbose_name="学生姓名") the_class = models.ForeignKey(to=Class, to_field="id", on_delete=models.CASCADE, related_name="students") detail = models.OneToOneField(to="StudentDetail", null=True) class StudentDetail(models.Model): height = models.PositiveIntegerField() weight = models.PositiveIntegerField() email = models.EmailField()
正向查询(由学生信息表查询学生详情表)
>>> student_obj = models.Student.objects.first() >>> student_obj.detail.email '1@1.com'
反向查询(由学生详情表反向查询学生信息表)
>>> detail_obj = models.StudentDetail.objects.get(id=1) >>> detail_obj.student.sname 'a'
三、多对多关系
三种方式创建多对多外键方式及其优缺点
通过外键创建
class Class(models.Model): id = models.AutoField(primary_key=True) # 主键 cname = models.CharField(max_length=32) # 班级名称 first_day = models.DateField() # 开班时间 class Teacher(models.Model): tname = models.CharField(max_length=32) # 自定义第三张表,通过外键关联上面两张表 class Teacher2Class(models.Model): teacher = models.ForeignKey(to="Teacher") the_class = models.ForeignKey(to="Class") class Meta: unique_together = ("teacher", "the_class")
通过ManyToManyField创建
class Class(models.Model): id = models.AutoField(primary_key=True) # 主键 cname = models.CharField(max_length=32) # 班级名称 first_day = models.DateField() # 开班时间 class Teacher(models.Model): tname = models.CharField(max_length=32) # 通过ManyToManyField自动创建第三张表 cid = models.ManyToManyField(to="Class", related_name="teachers")
通过外键和ManyToManyField创建
class Class(models.Model): id = models.AutoField(primary_key=True) # 主键 cname = models.CharField(max_length=32) # 班级名称 first_day = models.DateField() # 开班时间 class Teacher(models.Model): tname = models.CharField(max_length=32) # 通过ManyToManyField和手动创建第三张表 cid = models.ManyToManyField(to="Class", through="Teacher2Class", through_fields=("teacher", "the_class")) class Teacher2Class(models.Model): teacher = models.ForeignKey(to="Teacher") the_class = models.ForeignKey(to="Class") class Meta: unique_together = ("teacher", "the_class")
四、多对多操作
正向查询(由老师表查询班级表)
>>> teacher_obj = models.Teacher.objects.first() >>> teacher_obj.cid.all() # 查询该老师授课的所有班级 <QuerySet [<Class: Class object>, <Class: Class object>]>
反向查询(由班级表反向查询老师表)
>>> class_obj = models.Class.objects.first() >>> class_obj.teachers.all() # 此处用到的是related_name,如果不设置的话就用默认的表名_set <QuerySet [<Teacher: Teacher object>, <Teacher: Teacher object>, <Teacher: Teacher object>]>
五、class RelatedManager
"关联管理器"是在一对多或者多对多的关联上下文中使用的管理器。
它存在于下面两种情况:
- 外键关系的反向查询
- 多对多关联关系
常用方法
create()
创建一个新的对象,保存对象,并将它添加到关联对象集之中,返回新创建的对象。
>>> import datetime >>> teacher_obj.cid.create(cname="9班", first_day=datetime.datetime.now())
创建一个新的班级对象,保存对象,并将它添加到关联对象集之中。返回新创建的对象:
>>> class_obj = models.Class.objects.first() >>> class_obj.student_set.create(sname="小明")
上面的写法等价于下面的写法,但是比下面的这种写法更简单。
>>> class_obj = models.Class.objects.first() >>> models.Student.objects.create(sname="小明", cid=class_obj)
add()
把指定的model
对象添加到关联对象集中。
添加对象
>>> class_objs = models.Class.objects.filter(id__lt=3)
>>> models.Teacher.objects.first().cid.add(*class_objs)
添加id
>>> models.Teacher.objects.first().cid.add(*[1, 2])
set()
更新model对象的关联对象。
>>> teacher_obj = models.Teacher.objects.first()
>>> teacher_obj.cid.set([2, 3])
remove()
从关联对象集中移除执行的model对象
>>> teacher_obj = models.Teacher.objects.first()
>>> teacher_obj.cid.remove(3)
对于ForeignKey对象,这个方法仅在null=True时存在。
clear()
从关联对象集中移除一切对象。
>>> teacher_obj = models.Teacher.objects.first()
>>> teacher_obj.cid.clear()
同理,对于ForeignKey对象,这个方法仅在null=True时存在。
注意:
- 对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法。
了不起的双下划线
在这之前我们所有的跨表查询都是基于对象的查询。
比如:
Django还提供了一种直观而高效的方式在查询中表示数据表之间的关联关系,它能自动确认 SQL JOIN 关系。
需要做跨关系查询时,就可以使用两个下划线来链接模型(model)间关联字段的名称,直到最终链接到你想要的 model 为止。