Django模型层之单表操作
ORM简介
查询数据层次图解:如果操作mysql,ORM是在pymysql之上又进行了一层封装
- MVC或者MTV框架中一个重要组成部分,就是ORM,它实现了数据模型与数据库的解耦,即数据类型的设计不需要依赖于特定的数据库通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员的工作量,不需要面对因数据库变更而导致的无效劳动
- ORM是“对象-关系-映射”的简称
例子
#sql中的表
#创建表:
CREATE TABLE employee(
id INT PRIMARY KEY auto_increment ,
name VARCHAR (20),
gender BIT default 1,
birthday DATA ,
department VARCHAR (20),
salary DECIMAL (8,2) unsigned,
);
#sql中的表纪录
#添加一条表纪录:
INSERT employee (name,gender,birthday,salary,department)
VALUES ("alex",1,"1985-12-12",8000,"保洁部");
#查询一条表纪录:
SELECT * FROM employee WHERE age=24;
#更新一条表纪录:
UPDATE employee SET birthday="1989-10-24" WHERE id=1;
#删除一条表纪录:
DELETE FROM employee WHERE name="alex"
#python的类
class Employee(models.Model):
id=models.AutoField(primary_key=True)
name=models.CharField(max_length=32)
gender=models.BooleanField()
birthday=models.DateField()
department=models.CharField(max_length=32)
salary=models.DecimalField(max_digits=8,decimal_places=2)
#python的类对象
#添加一条表纪录:
emp=Employee(name="alex",gender=True,birthday="1985-12-12",epartment="保洁部")
emp.save()
#查询一条表纪录:
Employee.objects.filter(age=24)
#更新一条表纪录:
Employee.objects.filter(id=1).update(birthday="1989-10-24")
#删除一条表纪录:
Employee.objects.filter(name="alex").delete()
1 更多的字段和参数
每个字段有一些特有的参数,例如,CharField需要max_length参数来指定VARCHAR
数据库字段的大小。还有一些适用于所有字段的通用参数。 这些参数在文档中有详细定义,这里我们只简单介绍一些最常用的:
字段
AutoField(Field)
- int自增列,必须填入参数 primary_key=True
BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True
注:当model中如果没有自增列,则自动会创建一个列名为id的列
from django.db import models
class UserInfo(models.Model):
# 自动创建一个列名为id的且为自增的整数列
username = models.CharField(max_length=32)
class Group(models.Model):
# 自定义自增列
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767
IntegerField(Field)
- 整数列(有符号的) -2147483648 ~ 2147483647
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整数 0 ~ 2147483647
BigIntegerField(IntegerField):
- 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
自定义无符号整数字段
class UnsignedIntegerField(models.IntegerField):
def db_type(self, connection):
return 'integer UNSIGNED'
PS: 返回值为字段在数据库中的属性,Django字段默认的值为:
'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob',
'BooleanField': 'bool',
'CharField': 'varchar(%(max_length)s)',
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
'UUIDField': 'char(32)',
BooleanField(Field)
- 布尔值类型
NullBooleanField(Field):
- 可以为空的布尔值
CharField(Field)
- 字符类型
- 必须提供max_length参数, max_length表示字符长度
TextField(Field)
- 文本类型
EmailField(CharField):
- 字符串类型,Django Admin以及ModelForm中提供验证机制
IPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制
GenericIPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
- 参数:
protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启刺功能,需要protocol="both"
URLField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证 URL
SlugField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
CommaSeparatedIntegerField(CharField)
- 字符串类型,格式必须为逗号分割的数字
UUIDField(Field)
- 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
FilePathField(Field)
- 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
- 参数:
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹
FileField(Field)
- 字符串,路径保存在数据库,文件上传到指定目录,本质是varchar
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
ImageField(FileField)
- 字符串,路径保存在数据库,文件上传到指定目录,本质是varchar继承了FileField
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
width_field=None, 上传图片的高度保存的数据库字段名(字符串)
height_field=None 上传图片的宽度保存的数据库字段名(字符串)
DateTimeField(DateField)
- 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
DateField(DateTimeCheckMixin, Field)
- 日期格式 YYYY-MM-DD
TimeField(DateTimeCheckMixin, Field)
- 时间格式 HH:MM[:ss[.uuuuuu]]
DurationField(Field)
- 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
FloatField(Field)
- 浮点型
DecimalField(Field)
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度
BinaryField(Field)
- 二进制类型
参数
(1)null
如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False.
(1)blank
如果为True,该字段允许不填。默认为False。
要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。
如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。
(2)default
字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用。
(3)primary_key
如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,
Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,
否则没必要设置任何一个字段的primary_key=True。
(4)unique
如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的
(5)choices
由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,<br>而且这个选择框的选项就是choices 中的选项。
(6)auto_now 和 auto_now_add
# 相同:
两者都是默认传入当前的时间,即进行原生添加记录的数据库操作,即当数据对象调用save()方法时即使是没有输入时间,也会自动记录下当前的时间传入。
# 不同:
auto_now创建对象记录下当前时间后,如果该对象调用update()方法进行记录的更新,那么即使没有特意去更换当前的时间,该字段也会自动记录下当前的时间。
auto_now_add则是创建对象记录当前时间后,不会随着调用update()方法实时更新自己字段下的记录,除非是强行更改。
# 注意:
res = models.Work.objects.filter(pk=72).first()
res.ip = '1111.111.111'
res.save()
用这种方式将对象单独取出来,进行更改,而不是取出querset对象。这样才能触发实时更新时间的效果
# models.Work.objects.filter(pk=72).update(ip='111.111.111')
可以修改但是不能更新时间
(7)db_index
设置子索引
元数据
class Meta:
# 表名
db_table = 'book'
# 联合索引
index_together = [
('name','publish'),
]
# 联合唯一索引:联合字段下的记录不能出现同时重复的对象(一条数据)
unique_together = (("name", "publish"),)
# 了解
# admin中显示的表名称,会自动加上s表示为复数
verbose_name='图书表'
# verbose_name取消s
verbose_name_plural='图书表'
2 settings配置
若想将模型转为Mysql数据库中的表,需要在settings中配置:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'lqz',
'USER': 'root',
'PASSWORD': '123456',
'HOST': '127.0.0.1',
'PORT': 3306,
'ATOMIC_REQUEST': True,
'OPTIONS': {
"init_command": "SET storage_engine=MyISAM",
}
}
}
'''
NAME':要连接的数据库,连接前需要创建好
'USER':连接数据库的用户名
'PASSWORD':连接数据库的密码
'HOST':连接主机,默认本机
'PORT':端口 默认3306
'ATOMIC_REQUEST': True,
设置为True统一个http请求对应的所有sql都放在一个事务中执行(要么所有都成功,要么所有都失败)。
是全局性的配置, 如果要对某个http请求放水(然后自定义事务),可以用non_atomic_requests修饰器
'OPTIONS': {
"init_command": "SET storage_engine=MyISAM",
}
设置创建表的存储引擎为MyISAM,INNODB
'''
注意1:NAME即数据库的名字,在mysql连接前该数据库必须已经创建,而上面的sqlite数据库下的db.sqlite3则是项目自动创建 USER和PASSWORD分别是数据库的用户名和密码。设置完后,再启动我们的Django项目前,我们需要激活我们的mysql。然后,启动项目,会报错:no module named MySQLdb 。这是因为django默认你导入的驱动是MySQLdb,可是MySQLdb 对于py3有很大问题,所以我们需要的驱动是PyMySQL 所以,我们只需要找到项目名(app)文件下的__init__,在里面写入:
import pymysql
pymysql.install_as_MySQLdb()
最后通过两条数据库迁移命令即可在指定的数据库中创建表 :
python manage.py makemigrations # 产生数据相关操作的日志
python manage.py migrate # 真正将数据迁移到数据库中
注意2:确保配置文件中的INSTALLED_APPS中写入我们创建的app名称
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
"book"
]
增加删除字段
删除,直接注释掉字段,执行数据库迁移命令即可
新增字段,在类里直接新增字段,直接执行数据库迁移命令会提示输入默认值,此时需要设置
publish = models.CharField(max_length=12,default='人民出版社',null=True)
# 需要匹配之前已经输入过的字段,保证每条数据即每个对象的字段下记录的完整性,所以设置默认值为空
3 添加表记录
方式1
# create方法的返回值book_obj就是插入book表中的python葵花宝典这本书籍纪录对象
book_obj=Book.objects.create(title="python葵花宝典",state=True,price=100,publish="苹果出版社",pub_date="2012-12-12")
方式2
book_obj=Book(title="python葵花宝典",state=True,price=100,publish="苹果出版社",pub_date="2012-12-12")
book_obj.save()
注意
'''
当建立一对多,或多对多,一对一,存在外键时
在 Django 删除对象时,会模仿 SQL 约束 ON DELETE CASCADE 的行为,换句话说,删除一个对象时也会删除与它相关联的外键对象。例如:
'''
b = Blog.objects.get(pk=1)
# This will delete the Blog and all of its Entry objects.
b.delete()
# 如果不想联级删除,可以设置为:
pubHouse = models.ForeignKey(to='Publisher', on_delete=models.SET_NULL, blank=True, null=True)
'''
要注意的是: delete() 方法是 QuerySet 上的方法,但并不适用于 Manager 本身。这是一种保护机制,是为了避免意外地调用 Entry.objects.delete() 方法导致 所有的 记录被误删除。如果你确认要删除所有的对象,那么你必须显式地调用:
'''
Entry.objects.all().delete()
4 删除表记录
# 删除的两种方式
# 第一种:queryset的delete方法
book = models.Book.filter(pk=2).delete()
print(type(book)) # queryset 对象
# 第二种:对象自己的delete方法
book = models.Book.objects.all().filter(name='嘻哈猴').first()
print(type(book)) # python中的dict类型
book.delete()
5 修改表记录
# 修改记录
# 第一种:queryset的update方法
res=models.Book.objects.filter(pk=30).update(ip='111.111')
print(res)
# 对应的原生sql
UPDATE `app10_work` SET `ip` = '111.111' WHERE `app10_work`.`id` = 30; args=('111.111', 30)
# 只改变了ip的值,没有触发 auto_now 的属性,时间没有随之更改
# 第二种:对象自己的
# 注意:如果要触发auto_now的修改对象则自动更新当前时间的属性用此种方式修改记录
book = models.Book.objects.filter(pk=12).first()
book.name='asdfasd'
book.save()
# 原生sql
UPDATE `app10_work` SET `ip` = '1111.111.111', `date` = '2020-10-10', `addr` = '/app10/index', `date_update` = '2020-10-13 15:47:26.951295' WHERE `app10_work`.`id` = 12; args=('1111.111.111', '2020-10-10', '/app10/index', '2020-10-13 15:47:26.951295', 12)
# 整体进行重新赋值
# date不变 auto_now_add 参数
# update随之改变为当前时间 auto_now 参数
6 在Python脚本中调用Django环境
# tests.py 在项目下
import os
if __name__ == '__main__':
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "untitled15.settings")
import django
django.setup()
from app01 import models
books = models.Book.objects.all()
print(books)
7 Django终端打印SQL语句
如果想打印orm转换过程中的sql,需要在settings中进行如下配置:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
8 查询表记录API
1.all(): 查询所有的结果,每一条数据(ORM中的每一个数据对象)
2.filter(**kwargs) 它包含了与所给筛选条件相匹配的对象
3.get(**kwargs) 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的 对象超过一个或者没有都会抛出错误
4.exclude(**kwargs) 它包含了与所给筛选条件不匹配的对象如exclude(pk=2)返回除了此主键的数据
5.order_by() 对查询结果排序默认从小到大,('-id')反序
6.reverse() 对查询结果反向排序
7.count() 返回数据库中匹配查询(QuerySet)的对象数量
8.first() 返回第一条记录,或第一个对象
9.last() 返回最后一条记录,或最后一个对象
10.exists() 如果QuerySet包含数据,就返回True,否则返回False
11.values(*field) 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列 model的实例化对象,而是一个可迭代的字典序列(在mysql中只显示括号内中字 段的记录)
12.values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个 字典序列
13.distinct(): 从返回结果中剔除重复纪录
# 注意
'''
类似于
res = models.Book.objects.get(pk=1)
res =models.Book.objects.first()
res =models.Book.objects.filter(pk=1).first()
print(type(res))
这样单独拿出来的数据对象
数据类型都为:
<class 'app10.models.Book'> # 就是定义此字段的类
其他:
<class 'django.db.models.query.QuerySet'> # 都是QuerySet对象
其本质是生成器,也就是具备在某个节点能够暂停循环并保存状态,以开始下一次的循环。
比如设置了 limited 21 代表循环21次从硬盘读取数据到内存21条就通过生成式yield挂起状态,
如果此21条有用则被引用保存在内存中,没有则进行垃圾回收。然后再读入下个21条数据,再重复操作。
这样时刻保持了内存的资源不会被过多消耗。
不然一下从硬盘中循环读出百万条数据到内存,那么内存的负荷会被撑爆,无法进行后续操作
只有QuerySet对象可以导入到html文件中进行模板的渲染
'''
9 基于双下划线的模糊查询
#1 价格在[100,200,300]这个范围内
Book.objects.filter(price__in=[100,200,300])
# 2 大于,小于,大于等于,小于等于
Book.objects.filter(price__gt=100)
Book.objects.filter(price__lt=100)
Book.objects.filter(price__gte=100)
Book.objects.filter(price__lte=100)
# 3 范围 BETWEEN 100 AND 200
Book.objects.filter(price__range=[100,200])
# 包含
Book.objects.filter(title__contains="python")
# 4 忽略大小写包含
Book.objects.filter(title__icontains="python")
# 5 以xx开头
Book.objects.filter(title__startswith="py")
# 6 时间类型
Book.objects.filter(pub_date__year=2011) # 匹配年份2011
Book.objects.filter(pub_date__month=11) # 匹配月份11月
Book.objects.filter(pub_date__day=11) # 匹配日历11日
补充
1 时区和国际化问题
setting.py中
1 后台管理汉语问题
LANGUAGE_CODE = 'zh-hans' # 管理后台看到的就是中文
2 时区问题(使用东八区)
TIME_ZONE = 'Asia/Shanghai'
USE_TZ = False
2 django admin(管理后台的简单使用)
0 管理后台是django提供的可以快速对表进行增删查改操作
1 创建一个后台管理账号
python3 manage.py createsuperuser
输入用户名
输入邮箱(可以不填,敲回车)
输入密码
确认密码
# 超级用户创建出来了,可以登录管理后台了
2 admin中表中一行一行的数据显示我们定制的样子
重写模型类的__str__方法
3 blank参数作用
1 需要把book表注册到admin中
在app下的admin.py中写
from app01 import models
# 把book表注册一些,管理后台就能看到了
admin.site.register(models.Book)
2 可以快速的对book表进行增删查改操作
练习
'''
1 查询老男孩出版社出版过的价格大于200的书籍
2 查询2017年8月出版的所有以py开头的书籍名称
3 查询价格为50,100或者150的所有书籍名称及其出版社名称
4 查询价格在100到200之间的所有书籍名称及其价格
5 查询所有人民出版社出版的书籍的价格(从高到低排序,去重)
'''
def book(request):
# 1 查询老男孩出版社出版过的价格大于200的书籍
all_obj = models.Book.objects.filter(publish_name='老男孩').filter(price__gt=200).values('name')
# 2 查询2017年8月出版的所有以py开头的书籍名称(区分大小写)
all_obj = models.Book.objects.filter(create_time__year='2017').filter(create_time__month=8).filter(name__startswith='Py').values('name')
# 3 查询价格为50,100或者150的所有书籍名称及其出版社名称
all_obj = models.Book.objects.filter(price__in=[50, 100, 150]).values('name','publish_name')
# 4 查询价格在100到200之间的所有书籍名称及其价格
all_obj = models.Book.objects.filter(price__range=[100, 200]).values('name','price')
# 5 查询所有人民出版社出版的书籍的价格(从高到低排序,去重)
all_obj = models.Book.objects.filter(publish_name='人民').order_by('-price').distinct()
return render(request, 'book.html', locals())
# 链式调用(queryset就是链式调用的使用),实现一个可以支持链式调用的类
'''
链式调用就是在对象调用一个方法的基础上还可以通过.的方式进行方法的调用
那么只要在每个类中的函数定义下返回对象本身,那么相当于还是原对象通过.调用方法
'''
class Base:
def name(self, name):
print(name)
return self
def age(self, age):
print(age)
return self
def func(self):
print(self.name, self.age)
obj = Base()
obj.name('arther').age('age').func()
# 调用返回了self相当于 obj.age又返回self 相当于obj.func
# 验证:queryset对象,for循环的时候,其本质每打印完21条,又会执行一句查询sql
# 在 test.py 测试
import os
if __name__ == '__main__':
# 1 引入django配置文件
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Django_learn.settings')
# 2 让djagno启动
import django
django.setup()
# 3 使用表模型
from app10 import models
res = models.Book.objects.all()
print(res)
for i in res:
print(i)
# 终端显示的原生sql语句
# 取出每个对象 但是LIMIT 21 限制了只能取头21条数据 采用了生成式的原理 防止读取到内存的数据量过大
(0.001) SELECT `app10_book`.`id`, `app10_book`.`name`, `app10_book`.`price`, `app10_book`.`create_time`, `app10_book`.`update_time`, `app10_book`.`publish_name` FROM `app10_book` LIMIT 21; args=()
# 然后再执行完整的取出所有对象的sql语句
(0.007) SELECT `app10_book`.`id`, `app10_book`.`name`, `app10_book`.`price`, `app10_book`.`create_time`, `app10_book`.`update_time`, `app10_book`.`publish_name` FROM `app10_book`; args=()