模型基础:
字段类型:
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 mathfrom 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 pathfrom 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 > 1Author.objects.filter(id gte=1) # 获取id⼤于或等于1的值
# select * from Author where id >= 1Author.objects.filter(id lt=10) # 获取id⼩于10的值
# select * from Author where id < 10Author.objects.filter(id lte=10) # 获取id⼩于或等于10的值
# select * from Author where id <= 10Author.objects.filter(id lt=10, id gt=1) # 获取id⼤于1 且 ⼩于10的值
# select * from Author where id < 10 and id > 1Author.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>300persons = PersonModel.objects.filter(age__gte=300) # age>=300persons = PersonModel.objects.filter(age__lt=300) # age<300persons = PersonModel.objects.filter(age__lte=300) # age<=300persons = 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'))# 包含 正则 inprint('-' * 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 inpersons = PersonModel.objects.filter(age__contains='6') # 包含, 模糊查找,类似likepersons = PersonModel.objects.filter(name__contains='3') # 包含, 模糊查找,类似likepersons = 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_pageper_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 Paginatordef 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 pathfrom 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模型知识。