Django第二天。
ORM(Object-Relation Mapping)意思是对象-关系映射,是通过使用描述对象和数据库之间映射的元数据,将面向对象程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。这也同时暗示着额外的执行开销;然而,如果ORM作为一种中间件实现,则会有很多机会做优化,而这些在手写的持久层并不存在。更重要的是用于控制转换的元数据需要提供和管理;但是同样,这些花费要比维护手写的方案要少;而且就算遵守ODMG规范的对象数据库依然需要类级别的元数据。
对象关系映射是随着面向对象的软件开发方法发展而产生的。面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系数据是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系。因此,对象-关系映射ORM系统一般以中间件的形式存在,主要实现程序对象到关系数据库数据的映射。面向对象是从软件工程基本原则(如耦合、聚合、封装)的基础上发展起来的,而关系数据库则是从数学理论发展而来的,两套理论存在显著的区别。为了解决这个不匹配的现象,对象关系映射技术应运而生。O/R中字母O起源于“对象”(Object),而R则来自于“关系”(Relational)。几乎所有的程序里面,都存在对象和关系数据库。在业务逻辑层和用户界面层中,我们是面向对象的。当对象信息发生变化的时候,我们需要把对象的信息保存在关系数据库中。目前流行的ORM产品如Java的Hibernate,.Net的EntityFormerWork等。
在MVC框架中的Model模块中都包括ORM,对于开发人员主要带来了如下好处:
创建虚拟环境:
python -m venv env
执行env\Scripts目录下的activate,进入虚拟环境
E:\python-projects\django_second_day\env\Scripts\activate
在虚拟环境下,安装django,安装mysqlclient
python -m pip install django
pip install mysqlclient
创建Django项目
django-admin startproject django_second_day
打开django_second_day目录下的settings.py文件,找到DATABASES项,默认使用的是SQLites3数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
修改数据库为MySQL数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'django_second_day', #数据库名字,
'USER': 'root', #数据库登录用户名
'PASSWORD': '123456', #数据库登录密码
'HOST': 'localhost', #数据库所在主机
'PORT': '3306', #数据库端口
}
}
注意这里数据库需要我们自己手动创建好,我们回到项目根目录,创建一个应用bookapp
python manage.py startapp bookapp
然后我们将bookapp注册到项目中,在settings.py文件中,添加到INSTALLED_APPS中
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'bookapp',
]
应用中的模型类都定义在应用文件夹下的models.py文件中,比如我们定义的模型类就应该在“bookapp/models.py”文件中。需要记住的是模型类必须继承自django.db.models中的Model类。我们定义两个类,一个图书类,一个人物类代表书中的人物。
from django.db import models
# Create your models here.
# 图书类Book
class Book(models.Model):
name = models.CharField(max_length=32)
pub_date = models.DateField()
read_num = models.IntegerField(default=0)
isDelete = models.BooleanField(default=False)
# 人物类
class Human(models.Model):
name = models.CharField(max_length=32)
gender = models.BooleanField(default=True)
# 人物简介
comment = models.CharField(max_length=512)
# 图书,人物和图书的关系定义为图书1:n人物
book = models.ForeignKey('Book', on_delete=models.CASCADE)
isDelete = models.BooleanField(default=False)
然后生成迁移文件,执行迁移
python manage.py makemigrations
python manage.py migrate
这样我们的数据表就已经建立了,表名分别是bookapp_book和bookapp_human。
表中添加几条数据。
INSERT INTO bookapp_book(NAME,pub_date,read_num,isDelete) VALUES
('射雕英雄传','1981-1-1',12,0),
('天龙八部','1976-7-23',36,0),
('倚天屠龙记','1998-11-2',20,0),
('鹿鼎记','1955-12-12',58,0);
INSERT INTO bookapp_human(NAME,gender,book_id,COMMENT,isDelete) VALUES
('郭靖',1,1,'降龙十八掌',0),
('黄蓉',0,1,'打狗棍法',0),
('黄药师',1,1,'弹指神通',0),
('欧阳锋',1,1,'蛤蟆功',0),
('乔峰',1,2,'降龙十八掌',0),
('段誉',1,2,'六脉神剑',0),
('虚竹',1,2,'天山六阳掌',0),
('王语嫣',0,2,'神仙姐姐',0),
('张无忌',1,3,'九阳神功',0),
('赵敏',0,3,'郡主',0),
('谢逊',1,3,'七伤拳',0),
('张三丰',1,3,'太极拳',0),
('康熙',1,4,'钱',0),
('韦小宝',1,4,'抓奶龙抓手',0);
定义视图,打开bookapp/views.py并进行编辑。
from django.shortcuts import render, redirect
from bookapp.models import *
from datetime import date
# Create your views here.
# 首页查询所有图书并返回
def index(request):
book_list = Book.objects.all()
return render(request, 'bookapp/index.html', {'list': book_list})
# 创建新图书
def save(request):
book = Book()
book.name = '龙珠'
book.pub_date = date(1986,12,2)
book.save()
return redirect('/bookapp')
# 逻辑删除图书
def delete(request, id):
book = Book.objects.get(id=int(id))
book.delete()
return redirect('/bookapp')
在根目录创建templates/bookapp文件夹,再在bookapp里面创建index.html。
书
新增
{% for book in list %}
- 书名:{{book.name}} 删除
{% endfor %}
在bookapp下创建urls.py,配置url如下。
from django.urls import path, re_path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('save/', views.save, name='save'),
re_path(r'^delete(\d+)/$',views.delete,name='delete'),
]
修改django_second_day目录下的urls.py。
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('bookapp/', include('bookapp.urls')),
]
控制台输入python manage.py runserver
运行服务器。浏览器中访问localhost:8000/bookapp
,这样一个小demo就完成了。
Django会为我们创建的模型类创建自动增长的默认主键列id
,每个模型类只能有一个主键列,如果使用选项设置某属性作为这张表的主键,那么Django就不会创建默认主键。
属性命名限制:
属性 = models.字段类型(选项)
字段类型 | 含义 |
---|---|
AutoField | 自动增长的IntegerField,也就是自动增长的整数,通常不需要指定,不指定时Django会自动创建属性名为id的自动增长属性 |
booleanField | 布尔类型字段,True或False |
NullBooleanField | 三种值,Null、True、False |
CharField(max_length=字符长度) | 字符串,参数是值最大长度 |
TextField | 大文本字段 |
IntegerField | 整数 |
DecimalField(max_digits=None, decimal_places=None) | 十进制浮点数,第一个参数指总位数,第二个指小数位数 |
FloatField | 浮点数 |
DateField[auto_now=False, auto_now_add=False])) | 日期,auto_now表示每次保存对象时,自动保存为当前时间,用于更新的时间戳,总是指向最后一次修改时间,也就是当前时间,默认为false,auto_now_add表示当对象创建后保存创建的时间,也就是第一次创建时的时间,用于创建的时间戳,总是指向当前时间,默认为false。注意两个参数不可同为true |
TimeField | 时间,参数和DateField相同 |
DateTimeField | 日期时间,参数和DateField相同 |
FileField | 文件,上传文件字段 |
ImageField | 继承于FileField,对上传的内容进行校验,确保是有效的图片 |
选项 | 含义 |
---|---|
null | 为True表示允许为空,默认值为False |
blank | 为True表示字段允许为空,默认值是False |
以上两者区别 | null是数据库范畴的概念,blank是表单验证范畴的概念 |
db_column | 字段的名称,未指定的话使用属性名称 |
db_index | 为True表示在表中为此字段创建索引,默认值为False |
default | 默认值 |
primary_key | 为True表示该字段为主键,默认为False,一般作AutoField的选项用 |
unique | 为True表示这个字段必须唯一,默认值为False |
在bookapp/models.py中对两个类进行修改,代码如下
from django.db import models
# Create your models here.
# 图书类Book
class Book(models.Model):
# 添加db_column选项,修改数据库字段名为title
name = models.CharField(max_length=32, db_column="title")
pub_date = models.DateField()
read_num = models.IntegerField(default=0)
isDelete = models.BooleanField(default=False)
# 人物类
class Human(models.Model):
name = models.CharField(max_length=32)
# 设置数据库字段为sex
gender = models.BooleanField(default=True, db_column="sex")
# 人物简介,null数据库字段允许为空,blank是指输入框不能为空
comment = models.CharField(max_length=512, null=True, blank=False)
# 图书,人物和图书的关系定义为图书1:n人物
book = models.ForeignKey('Book', on_delete=models.CASCADE)
isDelete = models.BooleanField(default=False)
生成迁移文件,然后执行迁移。查看数据库的字段内容改变。
接下来在settings.py文件中添加如下配置,用以在控制台打印sql语句
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level': 'DEBUG',
},
}
}
在Django中可以通过调用一些方法来完成条件查询,比如filter()、exclude() 、get()等方法,参数的语法格式如下
属性名称__比较运算符=值
说明:属性名称和比较运算符间有两个下划线,所以属性名中不能包含连续的下划线。
等于使用exact,比如查询编号为1的图书
book = Book.objects.filter(id__exact=1)
简化为
book = Book.objects.fiter(id=1)
模糊查询使用contains,包含
book = Book.objects.filter(title__contains='雕')
如果包含%,无需转义,直接使用。
以…开头或结尾 startswith、endswith:以指定的值开头或结尾
book = Book.objects.filter(title__startswith='射')
以上运算符都区分大小写,在这些运算符前加上 i 表示不区分大小写,如iexact、icontains、istartswith、iendswith。
空查询 isnull:是否为null。
book = Book.objects.filter(title__isnull=False)
范围查询in:是否包含在范围内。
book = Book.objects.filter(in__in=[1,2,3])
比较查询 gt、gte、lt、lte:大于、大于等于、小于、小于等于。
查询 id 大于等于 2 的图书
book = Book.objects.filter(id__gte=2)
不等于使用 exclude() 过滤器。
查询 id 不等于 1 的图书
book = Book.objects.exclude(id=1)
日期查询 year、month、day、week_day、hour、minute、second:对日期时间类型的属性进行运算。
查询 1981 年发表的图书。
book = Book.objects.filter(pub_date__year=1981)
查询 1985 年 5 月 1 日后发表的作品
book = Book.objects.filter(pub_date__gt=date(1985, 5, 1))
注意:filter() 方法返回的是查询集,结果内容是 [] 列表内的值,而 get() 方法返回的是单个结果。
之前的查询都是对象的属性与常量值比较,使用 django.db.models.F 对象比较两个属性。语法如下:
F(属性名)
比如查询 id 的值小于阅读量的图书
book = Book.objects.filter(id__lt=F('read_num'))
多个过滤器逐个调用表示逻辑与关系,相当于 and。
查询阅读量大于 30,并且名字中包含“龙”的图书
book = Book.objects.filter(read_num__gt=30, title__contains='龙')
或
book = Book.objects.filter(read_num__gt=30).filter(title__contains='龙')
逻辑或 or 的查询需要 django.db.models.Q() 对象结合 | 运算符实现。语法:
Q(属性名__运算符=值)
查询名字中包含“龙”或“英”的图书
book = Book.objects.filter(Q(title__contains="龙") | Q(title__contains="英"))
事实上逻辑与还可以使用 Q() 对象实现,需要配合使用 & 运算符。
查询阅读量大于 30,并且名字中包含“龙”的图书
book = Book.objects.filter(Q(read_num__gt=30) & Q(title__contains='龙'))
非,使用 ~ 操作符。
查询编号不等于 1 的图书
book = Book.objects.filter(~Q(id=1))
使用 aggregate() 过滤器调用聚合函数,Avg,Count,Max,Min,Sum,均定义于 django.db.models 中。
查询所有图书书目
book = Book.objects.aggregate(Count('id'))
这时,aggregate 的返回值是一个字典类型,格式如下:
{'属性名__聚合类小写': 值}
比如上述
{'id__count': 4}
事实上,使用 count 一般不使用 aggregate() 过滤器,而是下面的方式,返回一个数字
book_num = Book.objects.count()
刚刚也有提到,filter() 方法返回值是一个查询集,那么什么是查询集呢?查询集表示从数据库中获取的对象集合,一些过滤器方法会返回查询集,查询集可以含义零个、一个或多个过滤器。过滤器基于所给的参数限制查询的结果,从 Sql 的角度来看,查询集就相当于 select 语句的结果,过滤器为限制条件,类似于 where 和 limit 子句。
返回查询集的过滤器:
返回单个值的过滤器:
判断一个查询集是否有数据:exist() 方法,判断查询集中是否有数据,如果有则返回True,否则返回False。
可以对查询集进行取下标或切片操作,等同于 sql 中的 limit 和 offset 子句。不支持负数索引。对查询集进行切片后返回一个新的查询集,不会立即执行查询。如果获取一个对象,直接使用 [0],等同于 [0:1].get(),但是如果没有数据,[0] 引发 IndexError 异常,[0:1].get() 如果没有数据引发 DoesNotExist 异常。
关系型数据库中的关系,包含有三种,一对多,一对一,多对多,在 Django 中对应的字段类型如下:
Django 实现关联查询有两种方式,一种是通过对象执行关联查询,一种是通过模型类执行关联查询。
在定义模型类时,可以指定三种关联关系,最常用的时一对多关系,本次所写的“图书-人”为一对多关系。由一访问到多的访问语法:
1 的一方模型类.多对应的一方模型类名小写_set
也就是:
book = Book.objects.get(id=1)
book.human_set.all()
反过来由多的一方访问一的一方的语法:
直接使用多对应的模型类对象.该模型类中关系类的属性名称
human = Human.objects.get(id=1)
human.book
访问一对应的模型类关联对象的 id 语法:
多对应的模型类对象.关联类属性_id
human = Human.objects.get(id=1)
human.book_id
由多模型类条件查询一模型类数据,语法如下
关联模型类名小写__属性名__条件运算符=值
注意:可以不填写__条件运算符部分,表示等于,结果和 sql 中的 inner join 相同
查询关联有名字中有“郭”的英雄的图书
books = Book.objects.filter(human__name__contains='郭')
由一模型类条件查询多模型类数据,语法如下
一模型类在多对应的模型类中关联属性名__一模型类中的属性__条件运算符=值
查询对应图书标题中含有“天龙”的人物
human = Human.objects.filter(book__title__contains='天龙')
对于省市区三级目录,这种类型的数据,表结构类似,并且数据有限,我们可以设置自关联的数据表,使一个字段关联本表的主键。可以定义一个 Area 类。
# 定义用于表示地区的类,存储省、市、区三级目录信息
class Area(models.Model):
# 名称
name=models.CharField(max_length=32)
# 父级id
parent=models.ForeignKey('self', null=True, blank=True)
执行迁移
python manage.py makemigrations
ython manage.py migrate
我们再写一个模板,用以在页面上显示地区信息
area信息
当前地区:{{area.name}}
上级地区:{{area.parent.name}}
下级地区:
{%for a in area.area_set.all%}
- {{a.name}}
{%endfor%}
在 views.py 中添加 area 方法
def area(request):
area = Area.objects.get(id=2)
return render(request, 'bookapp/area.html', {'area': area})
在 urls.py 中配置url
path('area/', views.area, name='area')
在数据库中添加几条数据
INSERT INTO `django_second_day`.`bookapp_area`(`id`,`name`,`parent_id`) VALUES ( '1','黑龙江',NULL);
INSERT INTO `django_second_day`.`bookapp_area`(`id`,`name`,`parent_id`) VALUES ( '2','哈尔滨市',NULL);
INSERT INTO `django_second_day`.`bookapp_area`(`id`,`name`,`parent_id`) VALUES ( '3','平房区','2');
INSERT INTO `django_second_day`.`bookapp_area`(`id`,`name`,`parent_id`) VALUES ( '4','南岗区','2');
INSERT INTO `django_second_day`.`bookapp_area`(`id`,`name`,`parent_id`) VALUES ( '5','佳木斯市','1');
浏览器中访问
http://localhost:8000/bookapp/area/
就可以在页面上看到自关联显示的信息了。
objects:管理器,是 models.Manager 类型的对象,用于与数据库进行交互。当没有为模型类定义管理器时,Django 会为每一个模型类生成一个名为 objects 的管理器,自定义管理器后,Django 不再生成默认管理器 objects。
为模型类 Book 定义管理器 book_manager 语法如下:
class Book(models.Model):
...
book_manager = models.Manager()
管理器是 Django 的模型进行数据库操作的接口,Django 应用的每个模型类都拥有至少一个管理器。Django 支持自定义管理器类,继承自 models.Manager。
自定义管理器类主要用于两种情况:
打开 bookapp/models.py 文件,定义 BookManager
# 图书类管理器
class BookManager(models.Manager):
def all(self):
# 查询没有被删除的图书信息,重写 all() 方法
return super().all().filter(isDelete=False)
在模型类 Book 中定义管理器
class Book(models.Model):
...
books = BookManager()
class BookManager(models.Manager):
...
#创建模型类对象,接收要添加的信息
def add_book(self, title, pub_date):
#创建模型类对象self.model可以获得模型类
book = self.model()
book.title = title
book.pub_date = pub_date
book.read=0
book.isDelete = False
# 将数据插入进数据表
book.save()
return book
调用方式如下
book=Book.books.add_book("add", date(2001,1,1))
在模型类中定义类Meta,用于设置元信息,如使用db_table自定义表的名字。
表的默认名称是:
_
例:
bookapp_book
我们修改Book模型类生成的数据表名为book,在Book模型类中添加如下内容,代码如下
class Book(models.Model):
...
# 定义元选项
class Meta:
db_table='book'
这篇内容断断续续写了好几天,还要加紧学习!
如果你发现我的文章哪里有错误或者有什么好的想法可以联系我,我们一起学习共同进步,我的邮箱地址是[email protected]
let’s do more of those!