5.模型-单表
一、ORM介绍
ORM解决的开发人员不需要编写sql代码,即数据模型的设计不需要依赖特定的数据库,减轻开发人员的工作量,ORM是“对象—关系—映射”Object Relational Mapping)的简称。django的orm功能很强大,但是没有分离出来让别人使用。
类对象--->sql--->pymysql--->mysql服务端--->磁盘,orm其实就是将类对象的语法翻译成sql语句的一个引擎。
二、数据库
1、配置开发数据库
Django自带的sqlite3不适合做项目的数据库,在setting.py文件中修改DATABASES配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'django_ts',
'USER': 'root',
'PASSWORD': '123',
'HOST': '127.0.0.1',
'PORT': '3306',
}
}
ENGINE: 配置使用的数据库引擎;
NAME: 数据库名;
USER: 用户名;
PASSWORD: 密码;
HOST: 服务器地址;
PORT: 端口;
修改配置之后启动服务器会报错
django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module.
Did you install mysqlclient?
这是由于MySQLdb不支持python3,所以django要安装mysqlclient。
pip install mysqlclient
或者在项目的__init__.py文件中配置
import pymysql
pymysql.install_as_MySQLdb()
2、INSTALLED_APPS中应用的数据库迁移
manage.py的migrate命令用于将应用的模型定义或修改同步到数据库,migrate命令会检查INSTALLED_APPS里的配置的应用列表,依次迭代为每个应用的模型定义或修改同步到数据库。Django对于数据库的迁移工作通过两个命令来实现的。
python manage.py makemigrations
python manage.py migrate
makemigrations命令会检测目录下是否存在migrations目录,没有则创建,首先,根据应用的表结构定义会生成一个0001_initial.py文件,里面定义了数据表的Schema,在执行migrate命令就可以创建数据表了。
对于将来应用的每一次表结构定义修改,都需要再次执行makemigrations命令,Django会重新生成一个新的数据库迁移文件,记录表结构之间的差异,命名规则会对上一个迁移文件加1,如0002_xxx,0003_xxx,之后再执行migrate命令让新的迁移文件生效,完成表结构加定义的修改
为了保证迁移不会重复执行,Django会把每一次的数据库迁移记录记录到django_migrations表中,每一次执行migrate前都比较迁移文件是否记录在表中,没出现的才会执行。
3、查看原生的sql语句
方式一:
# setting.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level': 'DEBUG',
},
}
}
方式二:
from django.db import connection
print(connection.queries)
三、单表操作
1、创建表
在app的目录下的models.py文件中创建模型,
from django.db import models
class UserInfo(models.Model):
id = models.AutoField(primary_key=True)
# create table userinfo(id int primary_key auto_increment,name varchar(16),age int,current_date date)
name = models.CharField(max_length=16)
age = models.IntegerField()
current_date = models.DateField()
2、字段
1)CharFiled 字符串字段,用于较短的字符串,CharFiled必须有一个参数maxlength,用于从数据层和Django校验层限制该字段所允许的最大字符数。
2)IntegerField 用于保存一个整数
3)DecimalField 一个浮点数,必要的两个参数
参数 | 描述 |
---|---|
max_digits | 总位数(不包括小数点和符号) |
decimal_palce | 小数位数 |
4)AutoField 一个IntegerFiled,添加记录时自动增长
5)EmailField 一个带有检查Email合法性的CharField,不接受maxlength参数。
6)DateField 一个日期字段,共有下列额外的可选参数,
Argument:描述;
auto_now:当对象被保存时(更新或者添加都行),自动将该字段的值设置为当前时间,通常用于表示 "last-modified" 时间戳。
auto_now_add 当对象首次被创建时,自动将该字段的值设置为当前时间.通常用于表示对象创建时间.(仅在admin中有意义)
7)DateTimeField 一个日期时间字段,类似DateField支持同样的附加选项。
8)ImageField 类似FileField,校验上传对象是否是一个合法图片,有两个可选参数:height_field和width_field如果提供这两个参数,则图片将按提供的高度和宽度规格保存。
9)FileField 一个文件上传字段,
3、参数
1)null
如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False.
2)blank
如果为True,该字段允许不填。默认为False。
要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。
如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。
3)default
字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用,如果你的字段没有设置可以为空,那么将来如果我们后添加一个字段,这个字段就要给一个default值
4)primary_key
如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,否则没必要设置任何一个字段的primary_key=True。
5)unique
如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的
6)choices
一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,前端显示的表单将是一个选择框而不是标准的文本框,而且这个选择框的选项就是choices 中的选项。
enroll_status_choices = (('signed', "已报名"),
('unregistered', "未报名"),
('studying', '学习中'),
('paid_in_full', "学费已交齐"))
<!-- 前端页面 -->
{{ consult.get_status_display }}
7)db_index
如果db_index=True 则代表着为此字段设置数据库索引。DatetimeField、DateField、TimeField这个三个时间字段,都可以设置如下属性。
8)auto_now_add
配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
9)auto_now
配置上auto_now=True,每次更新数据记录的时候会更新该字段,标识这条记录最后一次的修改时间。
10)vserbose_name 起个复杂点的名字,一般作为中文解释用。可以直接在引号中写中文名,在多表关系中如果to
关键字没有写,直接写了表名,那么该关键字不可省略
consultant = models.ForeignKey('UserInfo', verbose_name="销售", related_name='customers', blank=True, null=True, on_delete=models.CASCADE )
11)
4、添加表记录
在python中orm的对应关系有三种:
类 ---------->表
类对象 ---------->行(记录)
类属性 ---------->表的字段(重点)
添加表记录的方式有四种:
都需要提前导入models.py
from app01 import models
方式一:
stu_obj = models.Student(
name='an',
age=11,
)
stu_obj.save()
方式二:
stu_obj2 = models.Student.objects.create(name='bn', age=12)
stu_obj2.save()
方式三(批量创建):
objs_list = []
for i in range(20):
obj = models.Student(
name='工具人%s' % i,
age=15+i,
)
objs_list.append(obj)
models.Student.objects.bulk_create(objs_list)
方式四:
models.Student.objects.update_or_create(
name='jn',
defaults={
'age': 44,
}
)
有就更新,没有就创建,
5、更新
更新 update()方法 model方法对象不能调用更新方法 报错信息'Student' object has no attribute 'update',只能queryset调用
models.Student.objects.get(name='gn').update(age=39) # 报错
models.Student.objects.filter(name='gn').update(age=18)
6、删除
删除,delete() ,queryset集合和model对象都可以调用
models.Student.objects.get(id=3).delete()
models.Student.objects.filter(name='dn').delete()
四、查询接口(方法)
queryset中支持链式操作
book = models.Book.objects().all().order_by('-cid').first()
1、普通的查询方法
下面是一些与单表查询的一些方法
1)all()
查询所有的数据,返回的是queryset集合,
<QuerySet [<Student: Student object (1)>,
<Student: Student object (2)>, <Student: Student object (3)>, <Student: Student object (4)>,
<Student: Student object (5)>]>
all_objs = models.Student.objects.all()
for i in all_objs:
print(i.name)
print(all_objs)
all[0] 返回queryset匹配到中第一个对象,没有报IndexError错误。
2)first()
返回queryset类型数据中的第一个记录,返回时model对象。如果没有匹配到对象则为None,如果queryset没有定义排序,则按主键自动排序,和all[0]的区别就是没有匹配到不会报错。
all_objs = models.Student.objects.all().first()
last() 返回最后一个,同first()
3)filter(**kwargs)
返回包含了与所给筛选条件相匹配的对象,返回的也是queryset集合,查询不到内容不会报错,返回一个<QuerySet []>空的queryset,支持切片操作。
传入的参数用逗号隔开,参数之间是and关系,可以打散传参。
objs = models.Student.objects.filter(id=2) # 查找id=2的记录
print(objs)
models.Student.object.filter(**{'id'=1, 'name'='an'}).update(age=21)
4)get(**kwargs)
返回与所给筛选条件相匹配的对象,返回models对象,而且get方法有且只有一个结果,如果符合筛选条件的对象超过一个或者没有都会抛出错误。捕获异常try。 Book.objects.get(id=1)。
obj = models.Student.objects.get(name='an')
print(obj)
关于get()方法的两个错误:
报错1:查询结果超过1个,
app01.models.Student.MultipleObjectsReturned: get() returned more than one Student -- it returned 2!
报错2:没有查到任何内容报错,
app01.models.Student.DoesNotExist: Student matching query does not exist.
5)exclude(**kwargs)
排除的意思,它包含了与所给筛选条件不匹配的对象,没有不等于的操作,用这个exclude,返回值是queryset类型
query = models.Student.object.exclude(id=1) # 返回id不等于6的所有的对象,
queery = models.Student.object.filter(age=38).exclude(id=6) # 在queryset基础上调用,
6)order_by(*field)
queryset类型的数据来调用,对查询结果排序,默认是按照id来升序排列的,返回值还是queryset集合。
query = models.Student.objects.all().order_by('id','-age')
默认按照id升序排列,降序排列在前面加一个负号,order_by('-age'),上面写的是先按照id升序排列,id一样在按照,age降序排列
7)reverse()
queryset类型的数据来调用,对查询结果反向排序,返回值还是queryset类型。
query = models.Student.objects.all().order_by('age').reverse()
8)count()
queryset类型的数据来调用,返回数据库中匹配查询(Queryset)的对象数量。
9)exists()
queryset类型来调用,如果QuerySet包含数据,就返回True,否则返回False。
空的queryset类型数据也有布尔值True和False,但是一般不用它来判断数据库里面是不是有数据,如果有大量的数据,你用它来判断,那么就需要查询出所有的数据,效率太差了,用count或者exits。
10)values(*field)
用的比较多,queryset类型的数据来调用,返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列,只要是返回的queryset类型,就可以继续链式调用queryset类型的其他的查找方法,其他方法也是一样的。
11)values_list(*field)
它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列。
12)distinct()
values和values_list得到的queryset类型的数据来调用,从返回结果中剔除重复纪录。
2、模糊查询
__in 查询在某一范围的
# price值等于这三个里面的任意一个的对象
models.Book.objects.filter(price__in=[100,200,300])
__gt 大于
__gte 大于等于
# 大于,大于等于是price__gte=100,别写price>100,这种参数不支持
models.Book.objects.filter(price__gt=100)
__lt 小于
__lte 小于等于
models.Book.objects.filter(price__lt=100) # 小于
__exact 等于like 'xxx'
__iexact 等于忽略大小写like 'xxx'
__contains 包含like '%xxx%'
__icontains 包含,忽略大小写like '%xxx%',但是对于sqlite来说,contains的作用效果等同于icontains。
__isnull 判断是否为空
# 查询用户名不为空的学生
models.Student.objects.filter(name__isnull=True)
# 查询用户名不为空的学生
models.Student.objects.filter(name__isnull=False)
不等于/不包含于:
# 查询id不为3的学生
models.Student.objects.filter().excute(id=3)
# 查询年龄不在[10, 20]的学生
models.Student.objects.filter().excute(age__in=[10. 20])
__range sql的between and,大于等于100,小于等于200
models.Book.objects.filter(price__range=[100,200])
__contains 包含某个字符串
models.Book.objects.filter(title__contains="python") #title值中包含python的
models.Book.objects.filter(title__icontains="python") #不区分大小写
models.Book.objects.filter(title__startswith="py") #以什么开头,istartswith 不区分大小写,endswith同理
models.Book.objects.filter(pub_date__year=2012)
3、日期相关
all_books = models.Book.objects.filter(pub_date__year=2016) #找2016年的所有书籍
all_books = models.Book.objects.filter(pub_date__year__gt=2016)#找大于2016年的所有书籍
all_books = models.Book.objects.filter(pub_date__year=2019,pub_date__month=2)
找2019年月份的所有书籍,如果明明有结果,却查不出结果,是因为mysql数据库的时区和django的时区不同导致的,将django中的settings配置文件里面的USE_TZ = True
改为False,就可以查到结果了,以后这个值就改为False,因为用的mysql数据库才会有这个问题,其他数据库没有这个问题。
# models.py
class Brithday(models.Model):
name = models.CharField(max_length=16)
date = models.DateField()
def __str__(self):
return self.name
import datetime
current_date = datetime.datetime.now()
# print(current_date)
models.Brithday.objects.create(name='an', date=current_date)
models.Brithday.objects.create(name='bn', date='2020-08-01')
五、ORM字段与数据库实际字段的对应关系
'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)',
六、外部文件使用django的models
# 外部文件使用django的models,需要配置django环境
import os
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'SingleTablehw.settings')
import django
django.setup()
from app01 import models
# 以上是配置
obj_list = []
for i in range(10):
obj = models.Book(
title='葵花宝典第%s版' % i,
price=100+i*2,
pub_date='200%s-01-01' % i,
publish='笑傲江湖出版社',
)
obj_list.append(obj)
models.Book.objects.bulk_create(obj_list)
七、pycharm使用mysql数据库报错
pycharm连接数据库报错:
Connection to sthw@localhost failed. [08001] Could not create connection to database server. Attempted reconnect 3 times. Giving up.
在URL后面添加?serverTimezone=GMT
修改时区。‘
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗