2.Django模型

1、ORM模型

对象关系映射(英语:(Object Relational Mapping,简称ORM,或ORM,或OR mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。

面向对象是从软件工程基本原则(如耦合、聚合、封装)的基础上发展起来的,而关系数据库则是从数学理论发展而来的,两套理论存在显著的区别。为了解决这个不匹配的现象,对象关系映射技术应运而生。

对象关系映射(Object-Relational Mapping)提供了概念性的、易于理解的模型化数据的方法。

ORM的方法论基于三个核心原则:

· 简单:以最基本的形式建模数据。

· 传达性:数据库结构被任何人都能理解的语言文档化。

· 精确性:基于数据模型创建正确标准化了的结构。

简单的说:ORM相当于中继数据

对于开发人员主要带来了如下好处:

  • 实现了数据模型与数据库的解耦,通过简单的配置就可以轻松更换数据库,而不需要修改代码
  • 只需要面向对象编程,不需要面向数据库编写代码

Model中定义的类,通过ORM与关系型数据库中的表对应,对象的属性体现对象间的关系,这种关系也被映射到数据表中

定义模型

1、创建项目

使用MySQL数据库,这是Web项目首选的数据库

进入虚拟环境py_django

workon py_3

安装pymsyql模块:

pip install pymysql

学习使用pycharm创建项目: 打开pycharm,点击File--New project

然后按照下图填写好后 点击create创建项目。

选择加入当前窗口,打开项目,点击ok。

设置项目使用msyql数据库: 修改setting.py中的 DATABASES

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'myweb',   # 数据库名字
        'USER': 'root',  # 数据库账号
        'PASSWORD': 'mysql',  # 数据库密码
        'HOST': '127.0.0.1',     # 数据库主机
        'PORT': '3306',     # 端口
    }
}
  • 注意:python3 中使用mysql,需要在项目目录下的init.py中加入下面这两行代码:
  • 由于python3 版本中mysql模块改了模块名为pymysql,所以要声明pymysql模块,就是MySQLdb
import pymysql
pymysql.install_as_MySQLdb()
  • 否则会报MySQLdb模块

django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module: No module named 'MySQLdb'

进入mysql数据库创建库:

mysql -uroot -pmysql  # shell终端连接mysql
mysql>  show databases;   # 查看所有数据库
mysql>  create database myweb charset=utf8;  # 创建名为 myweb 的数据库

创建名为myapp应用:

(py3) python@python:~/桌面/mytest$ python manage.py startapp myapp

setting.py中 INSTALLED_APPS 安装我们的应用myapp:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'myapp',

]

启动服务没有报错,说明连接mysql成功。

2、模型定义

  • 模型是你的数据的唯一的、权威的信息源。它包含你所储存数据的必要字段和行为。通常,每个模型对应数据库中唯一的一张表。
  • 每个模型都是django.db.models.Model 的一个Python 子类。
  • 模型的每个属性都表示为数据库中的一个字段。

举个例子定义一个Person模型,它有first_name 和last_name 两个属性:

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

迁移生成数据表

python manage.py makemigrations
python manage.py migrate

进到myweb数据库中查看是否生成表:

first_name和last_name是模型的两个字段。每个字段都被指定成一个类属性,每个属性映射到一个数据库的列。

上面的Person 模型会在数据库中创建这样一张表:

mysql> show create table myapp_person;
+--------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table        | Create Table                                                                                                                                                                                             |
+--------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| myapp_person | CREATE TABLE `myapp_person` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `first_name` varchar(30) NOT NULL,
  `last_name` varchar(30) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |

这个表的名称myapp_person,是根据 模型中的元数据自动生成的,也可以重写为别的名称。 使用内置类 Mate 的db_name属性:

class Mate:
    db_table = "you_app_name"

id 字段是自动添加的,但这个行为可以被重写。默认情况下,Django 会给每个模型添加下面这个字段:

id = models.AutoField(primary_key=True)

这个例子中的CREATE TABLE SQL 语句使用mysql 语法格式,要注意的是Django 会根据设置文件setting.py 中指定的数据库类型来使用相应的SQL 语句。

3、字段类型

使用时需要引入django.db.models包,字段类型如下

  1. AutoField:自动增长的IntegerField,通常不用指定,不指定时Django会自动创建属性名为id的自动增长属性
  2. BooleanField:布尔字段,值为True或False
  3. NullBooleanField:支持Null、True、False三种值
  4. CharField(max_length=字符长度):字符串,必选 参数max_length表示最大字符个数
  5. TextField:大文本字段,一般超过4000个字符时使用
  6. IntegerField:整数
  7. DecimalField(max_digits=None, decimal_places=None):十进制浮点数参数max_digits表示总位数,参数decimal_places表示小数位数
  8. FloatField:浮点数
  9. DateField[auto_now=False, auto_now_add=False]):日期 参数auto_now表示每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,默认为false,参数auto_now_add表示当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为false,参数auto_now_add和auto_now是相互排斥的,组合将会发生错误
  10. TimeField:时间,参数同DateField
  11. DateTimeField:日期时间,参数同DateField
  12. FileField:上传文件字段
  13. ImageField:继承于FileField,对上传的内容进行校验,确保是有效的图片

关系型数据库的关系包括三种类型:

  • ForeignKey:一对多,将字段定义在多的一端中
  • ManyToManyField:多对多,将字段定义在两端中
  • OneToOneField:一对一,将字段定义在任意一端中

字段选项: null:如果为True,表示允许为空,默认值是False blank:如果为True,则该字段允许为空白,默认值是False default:默认值 unique:如果为True, 这个字段在表中必须有唯一值,默认值是False primary_key:若为True,则该字段会成为模型的主键字段,默认值是False,一般作为AutoField的选项使用

模型类成员

1、模型类实例方法

  • save():将模型对象保存到数据表中
  • delete():将模型对象从数据表中删除

利用django入门章节写的博客模型类演示:

def save_tag(request,tag):

    t = Tag()  # 创建模型实例对象
    t.tag_name = tag   # 将数据赋值给实例对象属性
    t.save()  # 将模型对象保存在数据库中
    return HttpResponse('ok')

配置url:

url(r'save_tag/(\w+)/',views.save_tag)

delete方法:

def delete_tag(request,tag):

    tag = Tag.objects.get(tag_name=tag)  # 查询tag_name为参数tag数据
    tag.delete()  # 删除

    return HttpResponse('%s 标签被删除'%tag)

配置url:

url(r'delete_tag/(\w+)/',views.delete_tag)

将刚才加入的标签删除:

http://127.0.0.1:8000/blog/delete_tag/python开发/

2、模型类的属性

属性objects:

管理器,是Manager类型的对象,用于与数据库进行交互 当没有为模型类定义管理器时,Django会为模型类生成一个名为objects的管理器,自定义管理器后,Django不再生成默认管理器objects。

管理器Manager:

管理器是Django的模型进行数据库操作的接口,Django应用的每个模型都拥有至少一个管理器 Django支持自定义管理器类,继承自models.Manager

自定义管理器类主要用于两种情况 1.修改原始查询集,重写get_queryset()方法 2.向管理器类中添加额外的方法,如创建对象

修改原始查询集,重写get_queryset()方法

文章管理器:

class PostManage(models.Manager):
    def get_queryset(self):
        # super(PostManage,self).get_queryset() #调用父类的方法
        # 只返回没有被逻辑删除的文章
        return super(PostManage,self).get_queryset().filter(delete=False)

在模型中实例化自定义的管理器

class Post(models.Model):
    .... 省略其他代码
    post_object = PostManage()

在管理器类中定义创建对象的方法:

  • 当创建模型类对象时,django不会对数据库进行读写操作,调用save()方法才与数据库交互,进行insert或update操作,将数据保存到数据库中
  • 如果模型类的属性比较多,逐个属性赋值很麻烦,推荐使用管理器方式
class PostManage(models.Manager):
    def get_queryset(self):
        # super(PostManage,self).get_queryset() #调用父类的方法
        # 只返回没有被逻辑删除的文章
        return super(PostManage,self).get_queryset().filter(delete=False)

    def create(self,title,content,category,author):
        p = self.model()  # 创建模型类对象self.model可以获得模型类
        p.title = title
        p.content = content
        p.created_time = timezone.now()
        p.modified_time = timezone.now()
        p.category = category
        p.author = author
        p.delete = False
        return p      # 返回模型对象

在视图中调用管理器方法:

p = Post.post_object.create(title,content,cg,author) p.save()

添加文章的视图函数:

def add_post(request):

    '''在视图中定义文章字段值'''

    title = 'python 高级快速入门'
    content = 'python 大法好'

    cg = Category()  # 创建分类
    cg.category_name = 'python高级'
    cg.save()

    author = User.objects.get(id=1)  # 作者

    tag = Tag()  # 创建标签
    tag.tag_name = 'python大法'
    tag.save()

    # 调用管理器方法
    p = Post.post_object.create(title,content,cg,author)
    p.save()

    p.tags.add(tag) # 添加标签

    return HttpResponse('添加成功')

创建url

url(r'add_post/',views.add_post)

#请求视图: http://127.0.0.1:8000/blog/add_post/  执行视图。

在后台管理页面中查看文章表,新增一篇刚才视图执行保存进入的文章:

3、查询集

查询集表示从数据库中获取的对象集合,在管理器上调用过滤器方法会返回查询集,查询集可以含有零个、一个或多个过滤器 过滤器基于所给的参数限制查询的结果,查询集和select语句等价,过滤器像where和limit子句

两大特性:

  • 惰性执行:创建查询集不会访问数据库,直到调用数据时,才会访问数据库,调用数据的情况包括迭代、序列化、与if合用
  • 缓存:查询集的结果被存下来之后,再次查询时会使用之前缓存的数据

返回列表的过滤器如下:

  • all():返回所有数据
  • filter():返回满足条件的数据
  • exclude():返回满足条件之外的数据,相当于sql语句中where部分的not关键字
  • order_by():排序,参数为字段名,-号表示降序

返回单个值的过滤器如下:

  • get():返回单个满足条件的对象 如果未找到会引发"模型类.DoesNotExist"异常 如果多条被返回,会引发"模型类.MultipleObjectsReturned"异常
  • count():返回当前查询的总条数
  • exists():判断查询集中是否有数据,如果有则返回True,没有则返回False

限制查询集:

查询集返回列表,可以使用下标的方式进行限制,等同于sql中的limit和offset子句,不支持负数索引。

使用下标后返回一个新的查询集,不会立即执行查询 如果获取一个对象,直接使用[0],等同于[0:1].get(),但是如果没有数据,[0]引发IndexError异常。

进入项目的shell 中执行:

注意:shell 中执行跟视图中执行效果一样,为了更好的看到效果,使用shell演示

python manage.py shell

获取 第1、2条数据:

>>> from personal_blog.models import *  # 导入模型类
>>> post_list = Post.post_object.all()[0:2]   # post_object 管理器
>>> 
>>> post_list
<QuerySet [<Post: python 高级快速入门>, <Post: e10分钟入门django>]>
>>> post_list[0]  # 利用下标获取查询集第一个数据对象
>>>

返回列表的过滤器演示:

#filter():返回满足条件的数据
# 查询分类为'django'的文章
# post_object   管理器
>>> post_list = Post.post_object.filter(category__category_name='django')
>>> post_list
<QuerySet [<Post: e10分钟入门django>, <Post: django 快速开发>]>

exclude():返回满足条件之外的数据  
# 查询分类不是'django'的数据
>>> Post.post_object.exclude(category__category_name='django')
<QuerySet [<Post: python 高级快速入门>]>

order_by():排序,参数为字段名,-号表示降序
>>> Post.post_object.order_by('created_time')
<QuerySet [<Post: django 快速开发>, <Post: e10分钟入门django>, <Post: python 高级快速入门>]>
>>> Post.post_object.order_by('-created_time')
<QuerySet [<Post: python 高级快速入门>, <Post: e10分钟入门django>, <Post: django 快速开发>]>

#查询出结果集后在排序
>>> Post.post_object.filter(category__category_name='django').order_by('created_time')
<QuerySet [<Post: django 快速开发>, <Post: e10分钟入门django>]>
>>> Post.post_object.filter(category__category_name='django').order_by('-created_time')
<QuerySet [<Post: e10分钟入门django>, <Post: django 快速开发>]>

#查询数据中没有满足条件的,返回空列表
>>> Post.post_object.filter(category__category_name='djang大赛的o').order_by('-created_time')
<QuerySet []>

返回单个值的过滤器get:

# 查询标题为 'django 快速开发' 的数据
>>> Post.post_object.get(title='django 快速开发')
<Post: django 快速开发>

 # 查询一个标题不存在的文章,数据库中没有满足查询条件的,会报错,所以在不确定字段条件是否满足的情况下,不建议使用get  
>>> Post.post_object.get(title='快速开发') 
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/home/python/.virtualenvs/py3/lib/python3.5/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/python/.virtualenvs/py3/lib/python3.5/site-packages/django/db/models/query.py", line 385, in get
    self.model._meta.object_name
personal_blog.models.DoesNotExist: Post matching query does not exist.

count():返回当前查询的总条数

>>> Post.post_object.all().count()
3

exists():判断查询集中是否有数据,如果有则返回True,没有则返回False

>>> Post.post_object.all().exists()  # 查询结果集有数据,exists返回True
True 
>>> Post.post_object.filter(id=1000).exists()  # 数据中不存在id为1000的数据,查询结果集为空,返回False
False

数据查询

1、基本查询

运算符如下:

exact:表示相等

contains:是否包含

startswith、endswith:以指定值开头或结尾

iexact,icontains,istartswith,iendswith功能同上面四个一样,前面加字母i表示不区分大小写。

isnull:是否为null

in:是否包含在范围内

gt:大于

gte:大于等于

lt:小于

lte:小于等于

exclude()方法:返回满足条件之外的数据,实现不等于效果

year、month、day、week_day、hour、minute、second:对日期时间类型的属性进行运算

F对象 专门取对象中某列值的操作,F() 返回的实例用作查询内部对模型字段的引用。这些引用可以用于查询的filter 中来比较相同模型实例上不同字段之间值的比较。

Q对象 逻辑运算

注意:在django中查询条件的运算符采用两个下划线连接。 比如:

Post.post_object.filter(id__exact=1)  #查询条件:id等于1的数据

id大于1的数据:

Post.post_object.filter(id__gt=1)

翻译成sql语句为:

select * from Post where id>1;

演示:

  • exact 等于 查询id等于1的数据
Post.post_object.filter(id__exact=1)  #查询条件:id等于1的数据,注意是两个下划线

djago查询中条件查询默认使用exact运算符。 查询相等可简写:

Post.post_object.filter(id=1)
  • contains:是否包含 查询标题(title字段) 中包含'django'的文章
>>> Post.post_object.filter(title__contains='django')
<QuerySet [<Post: e10分钟入门django>, <Post: django 快速开发>]>
  • startswith、endswith:以指定值开头或结尾

查询标题中以python开头的文章

>>> Post.post_object.filter(title__startswith='python')
<QuerySet [<Post: python 高级快速入门>]>

查询标题中以django结尾的文章

>>> Post.post_object.filter(title__endswith='django')
<QuerySet [<Post: e10分钟入门django>]>
  • isnull:是否为null 如果数据库中有字段值为null的不能直接使用 '字段=null' 的方式查询,需要使用isnull判断值是否为null
>>> Post.post_object.filter(title__isnull=False)  #查询文章标题不为null的数据
<QuerySet [<Post: python 高级快速入门>, <Post: e10分钟入门django>, <Post: django 快速开发>]>
>>> Post.post_object.filter(title__isnull=True) <p>#查询文章标题为null的数据</p> 
<QuerySet []>
  • in:是否包含在范围内 查询id为2、3,4的文章,参数是一个列表,id在列表中,就是符合条件的。
>>> Post.post_object.filter(id__in=[2,3,4])
<QuerySet [<Post: e10分钟入门django>, <Post: django 快速开发>]>
  • gt:大于 查询id大于3 的文章
>>> Post.post_object.filter(id__gt=3)
<QuerySet [<Post: python 高级快速入门>, <Post: django 快速开发>]>
  • gte:大于等于 查询id大于等于3 的文章
>>> Post.post_object.filter(id__gte=3)
<QuerySet [<Post: python 高级快速入门>, <Post: e10分钟入门django>, <Post: django 快速开发>]>
  • lt:小于 查询id小于4的文章
>>> Post.post_object.filter(id__lt=4)
<QuerySet [<Post: e10分钟入门django>]>
  • lte:小于等于 查询id小于等于4的文章
>>> Post.post_object.filter(id__lte=4)
<QuerySet [<Post: e10分钟入门django>, <Post: django 快速开发>]>
  • year、month、day、week_day、hour、minute、second:对日期时间类型的属性进行运算 查询2018年写的文章:
>>> Post.post_object.filter(created_time__year=2018)
<QuerySet [<Post: python 高级快速入门>, <Post: e10分钟入门django>]>

查询2018年之前写的文章

>>> Post.post_object.filter(created_time__year__lt=2018)
<QuerySet [<Post: django 快速开发>]>

查询2018年1月1日之后的文章

>>> Post.post_object.filter(created_time__gte=date(2018,1,1))

F对象

如果查询条件为两个属性值比较那么需要用到F对象。使用F对象,被定义在django.db.models中所有要使用F对象要先导入

导入F对象。

from django.db.models import F

F() 返回的实例用作查询内部对模型字段的引用。这些引用可以用于查询的filter 中来比较相同模型实例上不同字段之间值的比较。

>>> Post.post_object.filter(created_time=F('modified_time'))
<QuerySet [<Post: python 高级快速入门>]>

Django 支持对F() 对象使用加法、减法、乘法、除法、取模以及幂计算等算术操作,两个操作数可以都是常数和其它F() 对象。

比如:查询阅读量大于两倍评论量的数据

Post.post_object.filter(read__gt=F('comment')*2)

Q对象:Q对象,被定义在django.db.models中所有要使用Q对象要先导入

from django.db.models import Q

两个条件逻辑与运算在多个条件用逗号分隔就是逻辑与:

>>> Post.post_object.filter(title='django 快速开发',id__gt=2)
<QuerySet [<Post: django 快速开发>]>

如果需要实现逻辑或or的查询,需要使用Q()对象结合|运算符 Q对象可以使用&、|连接,&表示逻辑与,|表示逻辑或 Q对象使用~操作符,表示逻辑非,not

例如:查询标题以django结尾或者python开头的文章。这种逻辑或的关系,直接查询是没办法查询的,需要使用Q对象结合 | 查询

>>> Post.post_object.filter(Q(title__startswith='python') | Q(title__endswith='django'))
<QuerySet [<Post: python 高级快速入门>, <Post: e10分钟入门django>]>

相当于mysql中的语句:

WHERE title LIKE 'python%' OR title LIKE '%django'

查询 id不等于3的文章:

>>> Post.post_object.filter(~Q(id=3))
<QuerySet [<Post: python 高级快速入门>, <Post: django 快速开发>]>

2、关联查询

Django中也能实现类似于mysql中的连接查询。

关联模型类名小写属性名运算符=值

例如:查询分类为'django'的文章

>>> Category.objects.filter(post__title='django 快速开发')
<QuerySet [<Category: django>]>

查询分类为django的所有文章

>>> Post.post_object.filter(category__category_name='django')
<QuerySet [<Post: e10分钟入门django>, <Post: django 快速开发>]>

3、反向查询

如果模型I有一个ForeignKey,那么该ForeignKey 所指的模型II实例可以通过一个管理器返回前面有ForeignKey的模型I的所有实例。这个管理器的名字为foo_set,其中foo是源模型的小写名称。

>>> cg = Category.objects.filter(category_name='django')[0]
>>> cg
<Category: django>
>>> cg.post_set.all()
<QuerySet [<Post: e10分钟入门django>, <Post: django 快速开发>]>

4、聚合函数

使用aggregate()过滤器调用聚合函数, 常用聚合函数:Avg,Count,Max,Min,Sum,被定义在django.db.models中 需要使用要先导入聚合函数

聚合函数返回结果是一个字典:

>>> from django.db.models import Max
>>> Post.post_object.aggregate(Max('id')) # 查询文章表中id的最大值
{'id__max': 7}

>>> from django.db.models import Min
>>> Post.post_object.aggregate(Min('id'))  # 查询文章表中id的最小值
{'id__min': 3}

>>> from django.db.models import Avg
>>> Post.post_object.aggregate(Avg('id'))  # 查询所有文章id的平均数
{'id__avg': 4.666666666666667}
>>> from django.db.models import Count
>>> Post.post_object.aggregate(Count('id'))   #统计一共多少文章。
{'id__count': 3}
>>> from django.db.models import Sum
>>> Post.post_object.aggregate(Sum('id'))
{'id__sum': 14}
Iyoyo电子书 一本集作者多年开发经验的python电子书 all right reserved,powered by Gitbook文件修订时间: 2022年 12:15:52