掌握django orm
序 配置mysql数据库
-
使用pycharm管理数据库
-
在设置中修改数据库信息
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'imooc', 'USER': 'root', 'PASSWORD': '75267156', 'HOST': '127.0.0.1', 'PORT': '3306', } }
-
安装相关插件并应用
使用
pip install pymysql
命令安装在项目的__ init__.py中添加
import pymysql pymysql.install_as_MySQLdb()
-
orm介绍
- orm是什么
-
将对象自动存放到关系型数据库中,充当业务逻辑层和数据层中的桥梁
-
在models.py中编写模型类
-
模型类的优势与劣势
- 让开发人员专注业务逻辑的开发处理,提高效率
- 在一定程度上牺牲程序的执行效率
-
字段类型和参数
常用的字段
class Test(models.Model):
# 自增长字段
# 每增加一条时加一
# 都是int型
Auto = models.AutoField()
# 下面的允许数字更大
BigAuto = models.BigAutoField()
# 二进制数据
BInary = models.BinaryField()
# 布尔型
Boolean = models.BooleanField()
# NullBoolean是允许为空的布尔型
NullBoolean = models.NullBooleanField()
# 整型
# 正整数,并且存储字节为五个
PositiveSmallInteger = models.PositiveSmallIntegerField()
# 6个字节大小的整数
SmallInteger = models.SmallIntegerField()
# 10个字节大小的正整数
PositiveInteger = models.PositiveIntegerField()
# 11个字节大小
Integer = models.IntegerField()
# 20个大小的整型
BigInteger = models.BigIntegerField()
# 字符串型
# 在数据库中对应 varchar, 需要指定程度
Char = models.CharField()
# 在数据库中对应 longtext, 不需要指定长度
Text = models.TextField()
# 时间日期型
# 表示年月日
Date = models.DateField()
# 表示年月日时分秒
DateTime = models.DateTimeField()
# 表示一段时间, 是int型, 通过timedelta实现
Duration = models.DurationField()
# 浮点型
Float = models.FloatField()
# 他需要指定整数有多少位,小数有多少位
Decimal = models.DecimalField()
# 其他字段
# 邮箱
# 限制用户在使用表单或后台时只能输入邮箱地址格式的数据
Email = models.EmailField()
Image = models.ImageField()
File = models.FileField()
FilePath = models.FilePathField()
URL = models.URLField()
UUID = models.UUIDField()
# 可以是ipv4 或 ipv6的地址
GenericIPAddress = models.GenericIPAddressField()
关系型字段
# 关系型字段需要设置相应约束条件
class A(models.Model):
# 一对一型
OneToOne = models.OneToOneField(Test)
class B(models.Model):
# 多对多型
foreign = models.ForeignKey(A)
class C(models.Model):
# 多对多型,默认或自定义中间件
ManyToMany = models.ManyToManyRel(B)
字段参数
# 1.所有字段都有的参数
# 在默认的情况下,数据库的表明是 数据库名称_应用名称小写
class Test(models.Model): # courses_test
# 字段名也是,跟属性名一致
# 如需更改可以使用 db_column 字段实现
PositiveSmallInteger = models.PositiveSmallIntegerField(db_column="age")
# 使用 primary_key 参数将字段定义为主键,他默认情况下是False
SmallInteger = models.SmallIntegerField(primary_key=True)
# 还可以使用 verbose_name 给字段设置一个别名,相当于这个字段的别名或是备注
Integer = models.IntegerField(verbose_name="11个字节大小")
# 字段的唯一键 unique,这个表中字段的值必须唯一
BigInteger = models.BigIntegerField(unique=True)
# 使用 null,blank 允许字段为空
# null 值数据库中为空, blank 指的是前端表单提交是是否为空
# 不能将null设为false,而blank为true,会报错
# 使用 db_index 为字段建立索引
Char = models.CharField(null=True, blank=True, db_index=True)
# 使用 help_text 在表单中为字段设置帮助信息
Text = models.TextField(help_text="这个是longtext")
# 使用 editable=False 让用户无法编辑,默认为true
Date = models.DateField(editable=False)
# 2.个别字段才有的参数
class Test(models.Model):
# 字符串型
# 必须指定最大长度
# 这里的100是指utf-8编码的字符串
Char = models.CharField(max_length=100)
# 时间日期型才有的参数
# unique_for_date = true 该时间日期必须唯一
# auto_now 是更新记录的时间
Date = models.DateField(unique_for_date=True, auto_now=True)
# 设置月份唯一
# auto_now_add 表示增加记录的时间
DateTime = models.DateTimeField(unique_for_month=True, auto_now_add=True)
# 浮点型
# 必须给他设定两个参数
# max_digits 是一共多少位
# decimal_places 是小数点后有多少位
# 整数两位,小数点后两位
Decimal = models.DecimalField(max_digits=4, decimal_places=2)
# 3.关系型字段的参数
class A(models.Model):
# 一对一型
# related_name 用于外键关联间的反向查询,就是通过父表查询到子表信息
OneToOne = models.OneToOneField(Test)
class B(models.Model):
# 多对多型
# on_delete 当一个被外键关联的对象被删除时,django将模仿on_delete参数定义的SQL约束执行相应操作
# CASCADE 模拟SQL语言中的ON DELETE DASCADE约束,将定义有外键的模型对象同时删除,此为django默认操作
foreign = models.ForeignKey(A,on_delete=models.CASCADE)
# PROTECT 阻止上面的删除操作, 但是会弹出ProtectedError异常
foreign = models.ForeignKey(A, on_delete=models.PROTECT)
# SET_NULL 将外键字段设为null, 只有当子段设置了null=True,方可使用
foreign = models.ForeignKey(A, on_delete=models.SET_NULL, null=True, blank=True)
# SET_DEFAULT: 将外键子段设为默认值, 只有当字段设置了default参数才行
foreign = models.ForeignKey(A, on_delete=models.SET_DEFAULT, default=0)
# DO_NOTHING 什么也不做
foreign = models.ForeignKey(A, on_delete=models.DO_NOTHING)
# SET() 设置为一个传递给SET()的值或者一个回调函数的返回值,注意大小写
foreign = models.ForeignKey(A, on_delete=models.SET )
自关联
class AddressInfo(models.Model):
address = models.CharField(max_length=100, null=True, blank=True, verbose_name="地址")
# 定义自关联项
# 可以使用 'self' 的方式自关联,也可以使用 '字段名'
pid = models.ForeignKey('self', null=True, blank=True, verbose_name="自关联")
# pid = models.ForeignKey('address', null=True, blank=True, verbose_name="自关联")
# 给不同的模型类返回一个可读的字符串信息
# def __unicode__(self)
def __str__(self):
# 通常可以使用上面已有的字符串,或者自己拼接
return self.address
元数据meta
元数据介绍
class AddressInfo(models.Model):
address = models.CharField(max_length=100, null=True, blank=True, verbose_name="地址")
# 定义自关联项
# 可以使用 'self' 的方式自关联,也可以使用 '字段名'
pid = models.ForeignKey('self', null=True, blank=True, verbose_name="自关联")
# pid = models.ForeignKey('address', null=True, blank=True, verbose_name="自关联")
note = models.CharField(max_length=200, null=True, blank=True, verbose_name="说明信息")
# 给不同的模型类返回一个可读的字符串信息
# def __unicode__(self)
def __str__(self):
# 通常可以使用上面已有的字符串,或者自己拼接
return self.address
class Meta:
# 元数据
# 封装了一些数据库的信息
# 定义表明
db_table = 'address'
# 按照指定字段进行升序排列
ordering = ['pid']
# 为模型类设定一个直观可读的信息
verbose_name = '省市县地址信息'
# 为verbose_name设定复数形式
verbose_name_plural = verbose_name
# 将模型类设定为基类
# 不生成数据表,仅供其他子类继承
abstract = True
# 为数据表设置额外权限
permissions = (('定义好的权限', '权限声明'),)
# 设定是否按照django既定规则来管理模型类,如增加修改
managed = False
# 联合唯一键, 对应数据表中的联合唯一约束
unique_together = ('address', 'note') # ((),())
# 定义模型类属于哪个应用
app_label = 'courses'
# 定义数据库表空间的名字
db_tablespace = ''
模型类开发示例
class Teacher(models.Model):
"""讲师信息表"""
nickname = models.CharField(max_length=30, primary_key=True, db_index=True, verbose_name="昵称")
introduction = models.TextField(default="这位同学很懒,什么也没说", verbose_name="简介")
fans = models.PositiveIntegerField(default=0, verbose_name="粉丝数")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
update_at = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta:
verbose_name = "讲师信息表"
verbose_name_plural = verbose_name
def __str__(self):
return self.nickname
class Course(models.Model):
"""课程信息表"""
title = models.CharField(max_length=100, primary_key=True, db_index=True, verbose_name="课程名")
# 一对多
# 删除级连
teacher = models.ForeignKey(Teacher, null=True, blank=True, on_delete=models.CASCADE, verbose_name="课程讲师")
# choice 对应 枚举 类型
type = models.CharField(choices=((1, "实战课"), (2, "免费课"), (0, "其他")), max_length=1, default=0, verbose_name="课程类型")
price = models.PositiveSmallIntegerField(verbose_name="课程价格")
volume = models.BigIntegerField(verbose_name="销量")
online = models.DateTimeField(verbose_name="上线时间")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
update_at = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta:
verbose_name = "课程信息表"
verbose_name_plural = verbose_name
def __str__(self):
return f"{self.get_type_display()}--{self.title}" # 示例: 实战课--django入门
class Student(models.Model):
"""学生信息表"""
nickname = models.CharField(max_length=30, primary_key=True, db_index=True, verbose_name="昵称")
course = models.ManyToManyField(Course, verbose_name="所学课程")
age = models.PositiveSmallIntegerField(verbose_name="年龄")
gender = models.CharField(choices=((1, "男"), (2, "女"), (0, "保密")), max_length=1, default=0, verbose_name="性别")
study_time = models.PositiveIntegerField(default=0, verbose_name="学习时长(h)")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
update_at = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta:
verbose_name = "学生信息表"
verbose_name_plural = verbose_name
def __str__(self):
return self.nickname
class TeacherAssistant(models.Model):
"""助教信息表"""
nickname = models.CharField(max_length=30, primary_key=True, db_index=True, verbose_name="昵称")
teacher = models.OneToOneField(Teacher, null=True, blank=True, on_delete=models.SET_NULL, verbose_name="讲师")
hobby = models.CharField(max_length=100, null=True, blank=True, verbose_name="爱好")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
update_at = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta:
verbose_name = "助教信息"
db_table = "courses_assistant"
verbose_name_plural = verbose_name
def __str__(self):
return self.nickname
django数据表操作
django导入数据表
import os
import sys
import random
import django
from datetime import date
# 这四行代码是在django项目中运行脚本
project_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(project_path) # 将项目路径添加到系统搜寻路径中
os.environ['DJANGO_SETTINGS_MODULE'] = 'imooc.settings' # 设置项目的配置文件
django.setup()
from courses.models import Teacher, Course, Student, TeacherAssistant
def import_data():
# 使用django orm导入数据
# 讲师数据 create()
Teacher.objects.create(nickname='jack', introduction="python工程师", fans=666)
Teacher.objects.create(nickname='Allen', introduction="java工程师", fans=123)
Teacher.objects.create(nickname='henry', introduction="golang工程师", fans=818)
# 课程数据 bulk_create()
# 批量导入数据
# 信息以列表形式传入
# teacher是一个外键字段, 到通过orm导入数据时通过对象来进行关联
# .get 返回一个teacher对象
Course.objects.bulk_create([Course(title=f"python系列课程{i}", teacher=Teacher.objects.get(nickname="jack"),
type=random.choice((0, 1, 2)),
price=random.randint(200, 300), volume=random.randint(100, 10000),
online=date(2018, 10, 1))
for i in range(1, 5)])
Course.objects.bulk_create([Course(title=f"java系列课程{i}", teacher=Teacher.objects.get(nickname="Allen"),
type=random.choice((0, 1, 2)),
price=random.randint(200, 300), volume=random.randint(100, 10000),
online=date(2018, 6, 4))
for i in range(1, 4)])
Course.objects.bulk_create([Course(title=f"golang系列课程{i}", teacher=Teacher.objects.get(nickname="henry"),
type=random.choice((0, 1, 2)),
price=random.randint(200, 300), volume=random.randint(100, 10000),
online=date(2018, 1, 1))
for i in range(1, 3)])
# 学生数据 update_or_create()
# 通过第一个参数去表中查询数据,如果存在更新后面defaults中的数据,如果不存在则创建一个新纪录
# 应该把主键或者是具有唯一键属性的字段放在外面
Student.objects.update_or_create(nickname="A同学", defaults={"age": random.randint(18, 58),
"gender": random.choice((1, 2, 0)),
"study_time": random.randint(9, 999)})
Student.objects.update_or_create(nickname="B同学", defaults={"age": random.randint(18, 58),
"gender": random.choice((1, 2, 0)),
"study_time": random.randint(9, 999)})
Student.objects.update_or_create(nickname="C同学", defaults={"age": random.randint(18, 58),
"gender": random.choice((1, 2, 0)),
"study_time": random.randint(9, 999)})
Student.objects.update_or_create(nickname="D同学", defaults={"age": random.randint(18, 58),
"gender": random.choice((1, 2, 0)),
"study_time": random.randint(9, 999)})
# 正向添加
# 销量大于等于1000的课程
# 通过学生(子表)关联到课程(父表)
# 注意要是用 * 来解析
Student.objects.get(nickname="A同学").course.add(*Course.objects.filter(volume__gte=1000))
# 销量大于500的课程
Student.objects.get(nickname="B同学").course.add(*Course.objects.filter(volume__gt=500))
# 反向添加
# 通过课程(父表)关联到学生(子表)
# 学习时间大于等于500小时的同学
# 表名_set 是固定写法
Course.objects.get(title="Python系列课程1").student_set.add(*Student.objects.filter(study_time__gte=500))
# 学习时间小于等于500小时的同学
Course.objects.get(title="Python系列课程2").student_set.add(*Student.objects.filter(study_time__lte=500))
# 助教数据 get_or_create()
# 如果存在则get查询,如果不存在则创建一个新纪录
# 与update_or_create类似
TeacherAssistant.objects.get_or_create(nickname="助教1",
defaults={"hobby": "学习", "teacher": Teacher.objects.get(nickname="jack")})
TeacherAssistant.objects.get_or_create(nickname="助教2",
defaults={"hobby": "舔狗", "teacher": Teacher.objects.get(nickname="Allen")})
TeacherAssistant.objects.get_or_create(nickname="助教3",
defaults={"hobby": "减肥", "teacher": Teacher.objects.get(nickname="henry")})
return True
if __name__ == "__main__":
if import_data():
print("导入成功")
"""
fixtures 也是一个提供数据初识化的方法
提供一个能被django serialization识别的序列化文件
可自动转化为model,保存到数据库
>>> python manage.py dumpdata > imooc.json # 他导出的是数据库中所有的数据
>>> python manage.py loaddata imooc.json # 进行导入
"""
models api
查询集介绍
# 一个视图类
class IndexView(View):
def get(self, request):
# 1.查询、检索、过滤
# objects 是模型类的对象管理器
# all() 取出所有结果
teachers = Teacher.bojects.all()
print(teachers)
# get() 智能返回一条结果, 多条则会报错
# 所以传入的的大多是主键或是唯一键
teachers2 = Teacher.objects.get(nickname="jack")
# 返回的是模型类
print(teachers2, type(teachers2))
# 返回的是 QuerySet 可以是多条结果
teachers3 = Teacher.objects.filter(fans__gte=500)
for t in teachers3:
print(f"讲师姓名{t.nickname}--粉丝数:{t.fans}")
# 2.字段数据匹配,大小写敏感
# 在匹配字段时必须使用双下滑线
# get:大于等于, exact:等于, gt:大于, in:在某某之内, isnull:是否为空, lt:小于, lte:小于等于, range:在某某范围内
teacher4 = Teacher.objects.filter(fans__in=[666, 123])
print(teacher4)
# contains:含有, icontains:(大小写不敏感)含有, endwith:以某某结尾, iendwith:(大小写不敏感)以某某结尾, regex:正则表达
teacher5 = Teacher.objects.filter(nickname__icontains='A')
print(teacher5)
# 3.结果切片、排序、链式查询
print(Teacher.objects.all()[:1])
# 默认升序排列,有 - 是降序
teacher6 = Teacher.objects.all().order_by("-fans")
for t in teacher6:
print(t.fans)
# 4.查看原生的SQL语句
print(str(Teacher.objects.filter(fans__get==500).order_by('nickname')))
return render(request, 'address.html')
返回QuerySet的API
"""返回新QuerySet API"""
# 1.all(),filter(),order_by(),exclude(),reverse(),distinct()
# exclude排除一些记录
print(Student.objects.all().exclude(nickname="A同学"))
# reverse()表示对结果反省排序, 要使用该api必须先对模型的元数据上设置ordering
# distinct() 去重
# 2.extra(),defer(),only() 实现字段别名,排除一些字段,选择一些字段
# 使用extra()为字段取别名
# key:别名, value:字段名
s3 = Student.objects.all().extra(select={"name": "nickname"})
# defer() 排除某些字段
# only() 只包括某些字段
print(str(Student.objects.all().only('nickname', 'age').query))
# 3.values(),values_list() 获取字典或元组形式的QuerySet
# values() 以字典列表形式返回
print(TeacherAssistant.objects.values('nickname', 'hobby'))
# <QuerySet [{'nickname':1, 'hobby':1}]>
# values_list() 以元组列表形式返回
print(TeacherAssistant.objects.values_list('nickname', 'hobby'))
#<QuerySet [('1','1')]>
# 当使用values_list() 只取一个字段时,可以使用flat=True
print(TeacherAssistant.objects.values_list('nicknaame',flat=True))
# <QuerySet ['1','1']>
# 4.dates(),datetimes() 根据时间获取查询集
# 参数分别为 查询字段, 查询种类, 排序顺序
# dates() 可以查 年月日, order默认为 ASC 正序,DESC是倒叙
print(Course.objects.dates('created_at', 'year', order='DESC'))
# 可以查 年月日时分秒
print(Course.objects.datestimes('created_at', 'year', order='DESC'))
# 5.union(),intersection(),difference() 交集,并集,差集
p_240 = Course.objects.filter(price__gte=240)
p_260 = Course.objects.filter(price__gte=260)
# 取交集
print(p_240.union(p_260))
# MYSQL只允许取交集
# 并集
print(p_240.intersection(p_260))
# 差集
print(p_240.difference(p_260))
# 6.select_related() 一对一,多对一查询优化, prefetch_related() 一对多,多对多查询优化; 反向查询
# select_related() 减少查询时所用SQL语句次数
course = Course.objects.all().select_related('teacher')
for c in course:
print(f"{c.title}--{c.teacher}--{c.teacher.fans}")
students = Student.objects.filter(age__lt=30).prefetch_rlated()
for s in students:
print(s.course.all())
# 7.annotate() 使用聚合计数,求和,平均数 raw() 执行原生的SQL
from django.db.models import COunt, Avg, Max, Min, Sum
print(Course.objects.values('teacher').annotate(vol=Sum('volume')))
print(Course.objects.values('teacher').annotate(pri=Avg('price')))
不返回QuerySet的API
"""不返回QuerySet API"""
# 1.获取对象 get(),get_or_create(),first(),last(),latest(),earliest(),in_bulk()
# first()获取第一个记录,last()获取最后一个
print(Course.objcets.first())
# 使用 latest()和earliest 必须在模型的元数据中设置 get_latest_by
# latest 表示最新的记录, earliest表示最早的记录,需要设定一个字段作为判断依据
print(Course.objcets.lastest())
# in_bulk 表示批量返回对象
print(Course.objcets.in_bulk(['python系列教程1', 'java系列教程']))
# 2.创建对象 create(), bulk_create(),update_or_create() 创建,批量创建,创建或更新
# 3.更新对象 update() ,update_or_create() 更新,更新或创建
Course.objcets.filter(title='Java系列教程').update(price=300)
# 3.删除对象 delete() 使用filter过滤
# 不要使用get 没有返回对象或返回多个对象会报错
Course.objcets.filter(title='test').delete()
# 4.其他操作 exists() count() aggregate() 判断是否存在,统计个数,聚合
print(Course.objcets.filter(title='test').exists())
print(Course.objcets.count())
# arrregate 对整个数据表中的数据进行统计
from django.db.models import COunt, Avg, Max, Min, Sum
print(Course.objcets.aggregate(Max('price'), Min('price'), Avg('price'), Sum('volume')))
自定义聚合查询
在模型类中定义
class GroupConcat(models.Aggregate):
"""自定义实现聚合功能,实现GROUP_CONCAT功能"""
function = 'GROUP_CONCAT'
# SQL语句
template = '%(function)s(%(distinct)s%(expressions)s%(ordering)s%(separator)s)'
def __init__(self, expression, distinct=False, ordering=None, separator=',', **extra):
super(GroupConcat, self).__init__(expression,
distinct='DISTINCT' if distinct else '',
ordering=' ORDER BY %s' % ordering if ordering is not None else '',
separator=' SEPRATOR "%s' % separator,
output_field=models.CharField(), **extra)
使用自定义聚合查询
# 先引入
from .moudels import GroupConcat
# 使用
courses = Course.object.values('teacher').annotate(title=GroupConcat('title',distinct=True,ordering='title ASC',
separator='-'))
F对象与Q对象
使用F对象和Q对象必须先导入
from django.db.models.import F, Q
-
F对象的使用: 操作字段数据 ,可以同时操作表中一个字段的所有记录
Course.objects.update(price=F('price')-11) # 两个字段进行比较,不过必须是相同类型 print(Course.objects.filter(volume__lte=F('price')*10)
-
Q对象的使用: 结合AND, OR, NOT, &, |, ~实现复杂的查询
print(Course.objects.filter(Q(title__icontains='java')&Q(volume__gte=5000))