模型基础:
字段类型:
django根据属性的类型确定以下信息
- 当前选择的数据库⽀持字段的类型
- 渲染管理表单时使⽤的默认html控件
- 在管理站点最低限度的验证
- django会为表增加⾃动增⻓的主键列,每个模型只能有⼀个主键列,如果使⽤选项设置某属性为主键列后,则django不会再⽣成默认的主键列。
属性命名限制
·遵循标识符规则
·由于django的查询⽅式,不允许使⽤连续的下划线
定义属性时,需要字段类型,字段类型被定义在django.db.models.fields⽬录下,为了⽅便使⽤,被导⼊到django.db.models中
使⽤⽅式
- 导⼊from django.db import models
- 通过models.Field创建字段类型的对象,赋值给属性
逻辑删除和物理删除
对于重要数据都做逻辑删除,不做物理删除,实现⽅法是定义is_delete属性,类型为BooleanField,默认值为False。(物理删除就是真正对数据库进行操作,逻辑删除就是打标记,筛选一下就可以)。
is_delete = models.BooleanField(default=False)
常⽤字段类型:
-
AutoField
·⼀个根据实际ID⾃动增⻓的IntegerField,通常不指定, 如果不指定,主键字段id将⾃动添加到模型中。 -
CharField(max_length=字符⻓度)
·字符串,默认的表单样式是 Input,相当于数据库中的varchar. -
TextField
·⼤⽂本字段,⼀般超过4000使⽤,默认的表单控件是Textarea,相当于数据库中的text. -
IntegerField
·整数 -
DecimalField(max_digits=None, decimal_places=None)
·使⽤python的Decimal实例表示的⼗进制浮点数
·参数说明
·DecimalField.max_digits
·位数总数
·DecimalField.decimal_places
·⼩数点后的数字位数 -
FloatField
⽤Python的float实例来表示的浮点数
- BooleanField
True/False 字段,此字段的默认表单控制是CheckboxInput
- DateField([auto_now=False, auto_now_add=False])
·使⽤Python的datetime.date实例表示的⽇期
·参数说明
·DateField.auto_now
每次保存对象时,⾃动设置该字段为当前时间,⽤于"最后⼀次修改"的时间戳,它总是使⽤当前⽇期,默认为false。
DateField.auto_now_add
当对象第⼀次被创建时⾃动设置当前时间,⽤于创建的时间戳,它总是使⽤创建时的⽇期,默认为false。
注意:auto_now_add, auto_now, and default 这些设置是相互排斥的,他们之间的任何组合将会发⽣错误的结果
- TimeField
使⽤Python的datetime.time实例表示的时间,参数同DateField。
- DateTimeField
·使⽤Python的datetime.datetime实例表示的⽇期和时间,参数同DateField。
-
FileField
⼀个上传⽂件的字段
-
ImageField
继承了FileField的所有属性和⽅法,但对上传的对象进⾏校验,确保它是个有效的image
需要安装Pillow: “pip install Pillow”
字段参数:
字段参数写在models.BooleanField()
内部。
# 常⽤字段选项(通过字段选项,可以实现对字段的约束):
1、 null=True
数据库中字段是否可以为空
2、 blank=True
django的 Admin 中添加数据时是否可允许空值
⼀般null=True & blank=True 搭配着⽤,出现null=True就⽤上blank=True
3、 primary_key = True
主键,对AutoField设置主键后,就会代替原来的⾃增 id 列
4、 auto_now 和 auto_now_add
auto_now ⾃动创建---⽆论添加或修改,都是当前操作的时间
auto_now_add ⾃动创建---永远是创建时的时间
5、 choices (后台admin下拉菜单)
//限制选择
USER_TYPE_LIST = (
(1, '超级⽤户'),
(2, '普通⽤户'),)
user_type = models.IntegerField(choices=USER_TYPE_LIST,
default=1, verbose_name='⽤户类型')
6、 max_length 最⼤⻓度
7、 default 默认值
8、 verbose_name Admin(后台显示的名称)中字段的显示名称(类似注释)
9、 name|db_column 数据库中的字段名称
10、unique=True 不允许重复
11、db_index = True 数据库索引,例如:如果你想通过name查询的更快的话,给他设置为索引即可
12、editable=True 在Admin⾥是否可编辑,不可编辑则不显示
13、设置表名
class Meta:
db_table = 'person' (表名)
案例实战:
创建项目工程:
数据迁移:
python manage.py makemigrations
映射数据库:
python manage.py migrate
注意:每对模型进行一次修改,都需要重新进行映射。
对于新添加的列,最后给一个默认值,因为可能前几列的值已经固定了,但是我们后面的列没有进行赋值,就会存在问题.
后台管理配置:
设置管理员:
python manage.py createsuperuser
后台查看:
显示问题:
如果想让其显示除需要定义model的魔术方法例如:
def __str__(self): # 控制
return f'{self.id}-{self.name}-{self.age}'
def __repr__(self):
return f'{self.id}-{self.name}-{self.age}'
如何实现回滚操作呢?
1.首先删除migrantions中的内容,如下:
- 在数据库中删除
App下的models.py
from django.db import models
# 模型 字段类型和约束
class UserModel(models.Model):
# uid 会成为主键,原来的id不会创建
uid = models.AutoField(auto_created=True, primary_key=True)
# CharField: 字符串类型,unique唯一,db_index索引
# 名字唯一
name = models.CharField(max_length=30, unique=True, db_index=True)
# IntegerField: 整数类型,default默认值
age = models.IntegerField(default=18)
# BooleanField: bool类型
sex = models.BooleanField(default=True)
# # TextField:长字符串,大文本,
# null=True 表示可以为空,blank=True在Admin管理页面可以为空,一般都是同时写
info = models.TextField(null=True, blank=True)
# # FloatField: 小数
salary = models.FloatField(default=100000.345)
# DecimalField: 十进制小数,
# max_digits=4:最大长度
# decimal_places=2:小数点后是2位
money = models.DecimalField(max_digits=4, decimal_places=2, default=10.34)
# # 日期
birthday = models.DateField(default='2000-03-04')
birthday2 = models.DateTimeField(auto_now=True) # 每一次修改后都会自动修改该时间为最新的修改时间
birthday3 = models.DateTimeField(auto_now_add=True) # 第一次添加数据的时候的时间,以后不会再修改
birthday4 = models.DateTimeField(auto_now_add=True) # 第一次添加数据的时候的时间,以后不会再修改
# # 文件和图片
# 保存图片的二进制 可以为空
icon = models.FileField(null=True, blank=True, upload_to='static/uploads')
icon2 = models.ImageField(null=True, blank=True, upload_to='static/uploads')
#
# # 其他约束
# 元组中第一个参数是我们输入的元素
# name 属性表示在数据库中生成的字段名字
# db_column 数据库中的字段名称
# verbose_name 是在后台管理系统中显示出来的名字
# editable=True 在Admin⾥是否可编辑,不可编辑则不显示
choices = ((1, '青铜'), (2, '大师'), (3, '王者'))
user_type = models.IntegerField(choices=choices, default=1,
name='utype', verbose_name='用户类型')
user_type2 = models.IntegerField(default=1, editable=False,
db_column='utype2', verbose_name='用户类型2')
def __str__(self):
return f'{self.name} - {self.age}'
模型操作:
⼀般的数据库操作流程:
- 创建数据库,设计表结构和字段
- 连接Mysql数据库,并编写数据访问层代码
- 业务逻辑层去调⽤数据访问层执⾏数据库操作
Django通过Model操作数据库,不管你数据库的类型是MySql或者Sqlite,Django⾃动帮你⽣成相应数据库类型的SQL语句,所以不需要关注SQL语句和类型,对数据的操作Django帮我们⾃动完成。只要会写Model就可以了。
django使⽤对象关系映射(Object Relational Mapping,简称ORM)框架去操控数据库。
ORM(Object Relational Mapping)对象关系映射,是⼀种程序技术,⽤于实现⾯向对象编程语⾔⾥不同类型系统的数据之间的转换。
ORM:
模型 | <=> | 表 |
---|---|---|
类结构 | -> | 表结构 |
对象 | -> | 表的⼀条数据 |
类属性 | -> | 表的字段 |
增删改查:
添加:
# models基本操作
# 增:
1)创建对象实例,然后调⽤save⽅法:
obj = Author()
obj.first_name = 'zhang'
obj.last_name = 'san'
obj.save()
2)创建对象并初始化,再调⽤save⽅法:
obj = Author(first_name='zhang', last_name='san') obj.save()
3)使⽤create⽅法
Author.objects.create(first_name='li', last_name='si')
4)使⽤get_or_create⽅法,可以防⽌重复
Author.objects.get_or_create(first_name='zhang', last_name='san')
class PersonModel(models.Model):
name = models.CharField(max_length=30, unique=True)
age = models.IntegerField(default=18)
class Meta:
# 自定义表名(默认是app的名字+模型名的小写)
db_table = 'tb_person'
def __str__(self):
return f'{self.id}-{self.name}-{self.age}'
def __repr__(self):
return f'{self.id}-{self.name}-{self.age}'
迁移:
查看表结构:
App下的views.py 提供接口
import math
from django.db.models import Max, Min, Sum, Avg, Count
from django.shortcuts import render, HttpResponse
# 导入models
from App.models import *
# 增加数据
def add_person(request):
# 方式1
# try:
# p = PersonModel()
# p.name = '李四'
# p.age = 44
# p.save() # 同步到数据库表中
# except Exception as e:
# return HttpResponse('添加失败')
#
# return HttpResponse('添加成功!')
# 方式2
# try:
# p = PersonModel(name='王五', age=55)
# p.save() # 同步到数据库表中
# except Exception as e:
# return HttpResponse('添加失败')
#
# return HttpResponse('添加成功!')
# 方式3
# try:
# PersonModel.objects.create(name='赵六', age=66)
# except Exception as e:
# return HttpResponse('添加失败')
#
# return HttpResponse('添加成功!')
# 方式4
# try:
# ret = PersonModel.objects.get_or_create(name='钱七', age=77)
# print('ret:', ret)
# # ret: (<PersonModel: PersonModel object (5)>, True)
# # 如果是第一次创建:则是True,如果已经存在则是False
#
# except Exception as e:
# return HttpResponse('添加失败')
# 添加多条数据
for i in range(21, 51):
PersonModel.objects.create(name=f'武{i}范', age=i)
return HttpResponse('添加成功!')
项目工程下的urls.py 写路由
from django.contrib import admin
from django.urls import path
from App.views import *
urlpatterns = [
path('add/', add_person), # 添加数据
# path('del/', del_person), # 删除数据
# path('update/', update_person), # 修改数据
# path('get/', get_person), # 查询数据
#
# # 分页
# path('paginate/<int:page>/', paginate, name='paginate'),
# path('paginate2/<int:page>/', paginate2, name='paginate2'),
path('admin/', admin.site.urls),
]
删除:
# 删:
使⽤Queryset的delete⽅法:
# 删除指定条件的数据
Author.objects.filter(first_name='zhang').delete() # 删除所有数据
Author.objects.all().delete()
注意: objects不能直接调⽤delete⽅法。
使⽤模型对象的delete⽅法:
obj = Author.objects.get(id=5) obj.delete()
views.py
# 删除数据
def del_person(request):
# 删除数据:
# 1. 先找到要删除的数据
# 2. 然后删除
try:
# 删除一条数据
# p = PersonModel.objects.first() # 第一条数据
# p.delete()
# 删除多条数据
PersonModel.objects.filter(age__gt=15).delete() # age>15的多条数据
except Exception as e:
return HttpResponse('删除失败!')
return HttpResponse('删除成功!')
修改:
# 改:
Author.objects.filter(last_name='dfdf').update(last_name='san')
模型没有定义update⽅法,直接给字段赋值,并调⽤save,能实现update的功能,⽐如:
obj = Author.objects.get(id=3)
obj.first_name = 'zhang'
obj.save()
save更新时会更新所有字段。如果只想更新某个字段,减少数据库操作,可以这么做:
obj.first_name = 'li'
obj.save(update_fields=['first_name'])
views.py
# 修改数据
def update_person(request):
# 修改数据
# 1. 先找到要修改的数据
# 2. 然后修改
try:
# 修改一条数据
p = PersonModel.objects.first()
p.age = 666
# p.save() # 同步到数据库表中
p.save(update_fields=['age']) # 指定更新的字段,提高更新效率
# 修改多条数据
# PersonModel.objects.all().update(age=100)
except Exception as e:
return HttpResponse('修改失败!')
return HttpResponse('修改成功!')
查询:
# 查:
get():获取单条数据:
Author.objects.get(id=123) # 一般对主键或者唯一的
如果没有找到符合条件的对象,会引发模型类.DoesNotExist异常如果找到多个,会引发模型类.MultipleObjectsReturned 异常
first():返回查询集(QuerySet)中的第⼀个对象
last():返回查询集中的最后⼀个对象
count():返回当前查询集中的对象个数
exists():判断查询集中是否有数据,如果有数据返回True没有反之
all():获取全部数据集:
Author.objects.all()
values(): 获取指定列的值,可以传多个参数!返回包含字典的列表(保存了字段名和对应的值)
Author.objects.all().values('password')
values_list(): 获取指定列的值,可以传多个参数!返回包含元组列表(只保存值)
Author.objects.all().values_list('password')
进阶操作:
# 获取个数
Author.objects.filter(name='seven').count()
Author.objects.filter(id gt=1) # 获取id⼤于1的值
# select * from Author where id > 1
Author.objects.filter(id gte=1) # 获取id⼤于或等于1的值
# select * from Author where id >= 1
Author.objects.filter(id lt=10) # 获取id⼩于10的值
# select * from Author where id < 10
Author.objects.filter(id lte=10) # 获取id⼩于或等于10的值
# select * from Author where id <= 10
Author.objects.filter(id lt=10, id gt=1) # 获取id⼤于1 且 ⼩于10的值
# select * from Author where id < 10 and id > 1
Author.objects.filter(id in=[11, 22, 33]) # 获取id在11、22、33中的数据
# select * from Author where id in (11,22,33)
# exclude 除了以外,取反
Author.objects.exclude(id in=[11, 22, 33]) # not in # select * from Author where id not in (11,22,33)
Author.objects.filter(name contains="ven") # contains(和数据库中like语法相同) # select * from Author where name like '%ven%'
Author.objects.filter(name icontains="ven") # icontains⼤⼩写不敏感
Author.objects.filter(name regex="^ven") # 正则匹配
Author.objects.filter(name iregex="^ven") # 正则匹配,忽略⼤⼩写
Author.objects.filter(age range=[10, 20]) # 范围bettwen and
# startswith,istartswith, endswith, iendswith:
# 以什么开始,以什么结束,和上⾯⼀样带i的是⼤⼩写不敏感的, 其实不带i的也忽略⼤⼩写
Author.objects.filter(name='seven').order_by('id') # asc升序 Author.objects.filter(name='seven').order_by('-id') # desc降序
Author.objects.all()[10:20] # 切⽚,取所有数据的10条到20条,分⻚的时候⽤的到,
# 下标从0开始,不能为负数, 可以实现分⻚
views.py
# 查询数据
def get_person(request):
# get(): 得到一个对象(一条数据) 必须获得一条数据
# 如果没有找到符合条件的对象,会引发模型类.DoesNotExist异常
# 如果找到多个,会引发模型类.MultipleObjectsReturned 异常
p = PersonModel.objects.get(id=5)
# p = PersonModel.objects.get(18) # 不可以这样写,会报错
# p = PersonModel.objects.get(pk=6) # pk:primary key
# p = PersonModel.objects.get(age=100) # 可以
# p = PersonModel.objects.get(age=100) # 不可以,报错,MultipleObjectsReturned
# # p = PersonModel.objects.get(age=1000) # 不可以,报错,DoesNotExist
# print('*' * 30)
# print(p, type(p)) # PersonModel对象
# print(p.name, p.age)
# print('*' * 30)
# # all(): 获取所有数据
# <QuerySet [5-武21范-100, 6-武22范-100, 7-武23范-100, 8-武24范-100, 9-武25范-100, 35-de-100]>
persons = PersonModel.objects.all()
print(persons, type(persons))
# # QuerySet 查询集
# # 可以遍历查询集
for p in persons:
print(p.name, p.age)
#
# # first(): 第一条数据
p = PersonModel.objects.first()
print(p.name, p.age)
#
# # last(): 最后一条数据
p = PersonModel.objects.last()
print(p.name, p.age)
return HttpResponse('查询成功!')
过滤filter:
# # filter(): 过滤,使用最多
# 查询有可能一条 也有可能多条,所以得到的是查询集
# persons = PersonModel.objects.filter() # 默认没有条件,得到所有数据
persons = PersonModel.objects.filter(age__gt=300) # age>300
persons = PersonModel.objects.filter(age__gte=300) # age>=300
persons = PersonModel.objects.filter(age__lt=300) # age<300
persons = PersonModel.objects.filter(age__lte=300) # age<=300
persons = PersonModel.objects.filter(age=300) # age=300
# # 查询集可以做链式调用 相当于子查询
# print(persons.filter().filter().all().first())
print(type(persons)) # django.db.models.query.QuerySet
# for p in persons:
# print('--- ', p.name, p.age)
# #
# print(persons.first())
# print(persons.last())
# print(persons.exists()) # 查询集是否存在数据,如果存在则为True,否则为False
# print(persons.count()) # 查询集中的数据个数
# values() 和 values_list()
persons = PersonModel.objects.filter().all()
print("persons:", persons)
print("list(persons):", list(persons)) # 将查询集强制转换成列表
# values() : 列表套字典,包括字段和值
# <QuerySet [{'name': '武21范', 'age': 100}, {'name': '武22范', 'age': 50}, {'name': '武23范', 'age': 300
# }, {'name': '武24范', 'age': 100}>
print("persons.values():", persons.values()) # 列表套字典
# 指定某个字段
print("persons.values('name', 'age'):", persons.values('name', 'age'))
# values_list(): 列表套元组, 只有值
# <QuerySet [(5, '武21范', 100), (6, '武22范', 50), (7, '武23范', 300), (8, '武24范', 100), (9, '武25范', 200), (
# 35, 'de', 600)]>
print("persons.values_list():", persons.values_list())
print("persons.values_list('name', 'age'):", persons.values_list('name', 'age'))
# 包含 正则 in
print('-' * 60)
# filter(): 详细, 类似数据库中的where语句
persons = PersonModel.objects.filter(age__in=[100, 200, 666, 777, 888]) # in
# exclude(): 排除,取反的意思
persons = PersonModel.objects.exclude(age__in=[100, 200, 666, 777, 888]) # not in
persons = PersonModel.objects.filter(age__contains='6') # 包含, 模糊查找,类似like
persons = PersonModel.objects.filter(name__contains='3') # 包含, 模糊查找,类似like
persons = PersonModel.objects.filter(name__icontains='3') # 包含, 模糊查找,类似like ignore大小写(忽略大小写)
persons = PersonModel.objects.filter(name__regex='^wu') # 正则匹配,姓武的
persons = PersonModel.objects.filter(name__iregex='^wu') # 正则匹配,忽略大小写
persons = PersonModel.objects.filter(age__range=[200, 400]) # 200-400之间,两边都包含
# 开头和结尾
persons = PersonModel.objects.filter(name__startswith='wu') # 以wu开头,忽略大小写
persons = PersonModel.objects.filter(name__istartswith='wu') # 以wu开头,忽略大小写
persons = PersonModel.objects.filter(name__endswith='wu') # 以wu结尾,忽略大小写
persons = PersonModel.objects.filter(name__iendswith='wu') # 以wu结尾,忽略大小写
print(persons)
聚合函数:
# # 聚合函数:max,min,sum
# 返回的是字典 {'age__max': 600}
result = PersonModel.objects.aggregate(Max('age')) # 最大值 {'age__max': 666}
result = PersonModel.objects.aggregate(Min('age')) # 最小值 {'age__min': 100}
result = PersonModel.objects.aggregate(Sum('age')) # 求和 {'age__sum': 1666}
result = PersonModel.objects.aggregate(Avg('age')) # 平均值 {'age__avg': 333.2}
result = PersonModel.objects.aggregate(Count('age')) # 计数 {'age__count': 5}
print(result)
排序:
# 排序
# 默认是按照升序排列
# -字段 表示降序
persons = PersonModel.objects.all().order_by('age') # 升序
persons = PersonModel.objects.all().order_by('age', '-id') # 先按照age升序,如果age相同则按id降序排列
persons = PersonModel.objects.all().order_by('-age') # 降序
print(persons)
分页:
手动分页:
views.py
# 分页功能
# 手动分页
def paginate(request, page=1):
# 页码:page
# 每页数量:per_page
per_page = 10
# 分页功能:
# 数据 =【1,2,3,4,5,...,100】
# 第几页 数据范围 数据下标范围 切片
# page=1 1 ~ 10 0 ~ 9 [0 : 10]
# page=2 11 ~ 20 10 ~ 19 [10 : 20]
# page=3 21 ~ 30 20 ~ 29 [20 : 30]
# page=4 31 ~ 40 30 ~ 39 [30 : 40]
# ...
# page=n [(n-1) * 10 : n * 10]
# page=page [(page-1) * per_page : page * per_page]
# 实现分页功能
persons = PersonModel.objects.all()
persons = persons[(page-1) * per_page : page * per_page] # 切片
# 总页数
total = PersonModel.objects.count() # 总数据量
total_page = math.ceil(total / per_page) # 总页数 向上取整,因为可能有一部分不到10个
pages = range(1, total_page+1) # 1,2,3,4,5,6,7...
data = {'persons': persons,'pages': pages}
return render(request, 'paginate.html', data)
templates下的paginate.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
ul {list-style: none; padding: 0}
.btns li {
float: left;
margin: 5px;
}
hr {
clear: both;
}
</style>
</head>
<body>
<h2>分页功能</h2>
<hr>
<ul class="btns">
{% for page in pages %}
<li>
<a href="{% url 'paginate' page %}"> <button>{{ page }}</button> </a>
</li>
{% endfor %}
</ul>
<hr>
<ul>
{% for person in persons %}
<li>{{ person.name }} - {{ person.age }}</li>
{% endfor %}
</ul>
</body>
</html>
当然了,还有进行路由绑定:
自动分页:
# 分页器:自动分页
from django.core.paginator import Paginator
def paginate2(request, page=1):
per_page = 10 # 每一页数量
all_data = PersonModel.objects.all()
# 分页器
paginator = Paginator(all_data, per_page)
persons = paginator.page(page) # 获取第page页的数据
pages = paginator.page_range # 页码范围,可以遍历
data = {'persons': persons, 'pages': pages}
return render(request, 'paginate2.html', data)
from django.contrib import admin
from django.urls import path
from App.views import *
urlpatterns = [
path('add/', add_person), # 添加数据
path('del/', del_person), # 删除数据
path('update/', update_person), # 修改数据
path('get/', get_person), # 查询数据
#
# 分页
path('paginate/<int:page>/', paginate, name='paginate'), # 给这个路由函数起一个名字
path('paginate2/<int:page>/', paginate2, name='paginate2'),
path('admin/', admin.site.urls),
]
templates下的paginate2.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
ul {list-style: none; padding: 0}
.btns li {
float: left;
margin: 5px;
}
hr {
clear: both;
}
</style>
</head>
<body>
<h2>分页功能</h2>
<hr>
<ul class="btns">
{% for page in pages %}
<li>
<a href="{% url 'paginate2' page %}"> <button>{{ page }}</button> </a>
</li>
{% endfor %}
</ul>
<hr>
<ul>
{% for person in persons %}
<li>{{ person.name }} - {{ person.age }}</li>
{% endfor %}
</ul>
</body>
</html>
总结
本篇博客深入探讨了Django中的模型基础,涵盖了ORM(对象关系映射)的概念及其在Django中的应用,详细介绍了各种字段类型以及常用字段参数的使用方法。通过实战案例的讲解,读者将学习如何在Django中使用模型进行数据操作,并解答了一些常见问题。此外,我们还重点介绍了模型的基本操作,包括CURD(创建、读取、更新和删除)以及分页的实现方法。无论您是初学者还是有一定经验的开发者,本文都将为您提供全面而实用的Django模型知识。