Python笔记:Django框架的模型层详解

Model模型

  • 模型是你的数据的唯一的、权威的信息源。它包含你所储存数据的必要字段和行为。
  • 通常,每个模型对应数据库中唯一的一张表。
  • 每个模型都是django.db.models.Model的一个Python 子类。
  • 模型的每个属性都表示为数据库中的一个字段。
  • Django 提供一套自动生成的用于数据库访问的API;
  • 这极大的减轻了开发人员的工作量,不需要面对因数据库变更而导致的无效劳动。
  • 备注:本文基于django版本1.11展开, 技术栈会有所升级, 仅供参考!

模型与数据库的关系

  • 模型(Model)负责业务对象和数据库的关系映射(ORM)
  • ORM是“对象-关系-映射”的简称,主要任务是:
    • 根据对象的类型生成表结构
    • 将对象、列表的操作,转换为sql语句
    • 将sql查询到的结果转换为对象、列表
  • 使用ORM,我们就可以不用像以前一样写sql语言了

为什么要用模型

  • Model是MVC框架中重要的一部分,主要负责程序中用于处理数据逻辑的部分。通常模型对象负责在数据库中存取数据
  • 它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库

配置Mysql数据库

1 ) 在当前环境中安装mysql

  • $ sudo apt-get install mysql-server
  • $ sudo apt install mysql-client
  • $ sudo apt install libmysqlclient-dev

2 ) 在当前python环境中安装 pymysql

  • $ pip3 install pymysql

3 ) 在mysql中创建数据库

  • $ create databases d_models_db default charset=utf8

4 ) 在Django项目中配置数据库

  • 修改settings.py文件中的DATABASE配置项
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'd_models_db', # 选择数据库的名, 请确认你的mysql中有这个库
            'USER': 'root',
            'PASSWORD': '',
            'HOST': 'localhost',
            'PORT': '3306',
            }
    }
    

5 ) 告诉Django在接下来的mysql操作中使用pymysql

  • 打开主项目目录下的__init__.py,写入以下代码导入pymysql:
    import pymysql
    pymysql.install_as_MySQLdb()
    

开发流程

1 ) 在models.py中定义模型类,要求继承自models.Model

from django.db import models
from datetime import datetime

# Create your models here.
class Users(models.Model):
 name = models.CharField(max_length=32)
 age = models.IntegerField(default=20)
 phone = models.CharField(max_length=16)
 addtime=models.DateTimeField(default=datetime.now)

 class Meta:
    db_table = "users"  # 指定表名

2 ) 把应用加入settings.py文件的installed_app项

  • 编辑主项目目录下的settings.py文件,并将项目应用文件名添加到该INSTALLED_APPS设置。

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'app',
    ]
    
  • 生成迁移文件

    • $ python3 manage.py makemigrations
      django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module: No module named 'MySQLdb'.
      Did you install mysqlclient or MySQL-python?
      # 安装上面"配置Mysql数据库"的第五步配置,或安装:pip install mysqlclient后就可以了
      
    • 若执行 $python3 manage.py makemigrations命令(可能把migrations文件夹给删了),会提示"No changes detected." 可能有用的解决方式如下:
      • 先 $python3 manage.py makemigrations --empty yourappname 生成一个空的initial.py
      • 再 $python3 manage.py makemigrations 生成原先的model对应的migration file
  • 执行迁移

    • $ python3 manage.py migrate
  • 使用模型类进行crud操作

  • 备注:在实际项目中生成迁移文件和执行迁移这两部基本不会出现,因为我们的数据库基本是已经设计好的

定义模型

  • 在模型中定义属性,会生成表中的字段
  • django根据属性的类型确定以下信息:
    • 当前选择的数据库支持字段的类型
    • 渲染管理表单时使用的默认html控件
    • 在管理站点最低限度的验证
  • django会为表增加自动增长的主键列,每个模型只能有一个主键列,如果使用选项设置某属性为主键列后,则django不会再生成默认的主键列
  • 属性命名限制
    • 不能是python的保留关键字
    • 由于django的查询方式,不允许使用连续的下划线

定义属性

  • 定义属性时,需要字段类型
  • 字段类型被定义在django.db.models.fields目录下,为了方便使用,被导入到django.db.models
  • 使用方式
    • 导入from django.db import models
    • 通过models.Field创建字段类型的对象,赋值给属性
  • 对于重要数据都做逻辑删除,不做物理删除,实现方法是定义isDelete属性,类型为BooleanField,默认值为False

1 ) 字段类型

  • AutoField:一个根据实际ID自动增长的IntegerField,通常不指定
    • 如果不指定,一个主键字段将自动添加到模型中
  • BooleanField:true/false 字段,此字段的默认表单控制是CheckboxInput
  • NullBooleanField:支持null、true、false三种值
  • CharField(max_length=字符长度):字符串,默认的表单样式是 TextInput
  • TextField:大文本字段,一般超过4000使用,默认的表单控件是Textarea
  • IntegerField:整数
  • DecimalField(max_digits=None, decimal_places=None):使用python的Decimal实例表示的十进制浮点数
    • DecimalField.max_digits:位数总数
    • DecimalField.decimal_places:小数点后的数字位数
  • FloatField:用Python的float实例来表示的浮点数
  • DateField[auto_now=False, auto_now_add=False]):使用Python的datetime.date实例表示的日期
    • 参数DateField.auto_now:每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,默认为false
    • 参数DateField.auto_now_add:当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为false
    • 该字段默认对应的表单控件是一个TextInput. 在管理员站点添加了一个JavaScript写的日历控件,和一个“Today"的快捷按钮,包含了一个额外的invalid_date错误消息键
    • auto_now_add, auto_now 这些设置是相互排斥的,他们之间的任何组合将会发生错误的结果
  • TimeField:使用Python的datetime.time实例表示的时间,参数同DateField
  • DateTimeField:使用Python的datetime.datetime实例表示的日期和时间,参数同DateField
  • FileField:一个上传文件的字段
  • ImageField:继承了FileField的所有属性和方法,但对上传的对象进行校验,确保它是个有效的image

2 ) 字段选项

  • 通过字段选项,可以实现对字段的约束
  • 在字段对象时通过关键字参数指定
  • null:如果为True,Django 将空值以NULL 存储到数据库中,默认值是 False
  • blank:如果为True,则该字段允许为空白,默认值是 False
  • 对比:null是数据库范畴的概念,blank是表单验证证范畴的
  • db_column:字段的名称,如果未指定,则使用属性的名称
  • db_index:若值为 True, 则在表中会为此字段创建索引
  • default:默认值
  • primary_key:若为 True, 则该字段会成为模型的主键字段
  • unique:如果为 True, 这个字段在表中必须有唯一值

3 ) 关系

  • 关系的类型包括
    • ForeignKey:一对多,将字段定义在多端中
    • ManyToManyField:多对多,将字段定义在两端中
    • OneToOneField:一对一,将字段定义在任意一端中
  • 可以维护递归的关联关系,使用’self’指定,详见“自关联”
  • 用一访问多:对象.模型类小写_set
    • bookinfo.heroinfo_set
  • 用一访问一:对象.模型类小写
    • heroinfo.bookinfo
  • 访问id:对象.属性_id
    • heroinfo.book_id

元选项

  • 在模型类中定义类Meta,用于设置元信息

  • 元信息db_table:定义数据表名称,推荐使用小写字母

  • 数据表的默认名称:<app_name>_<model_name>

  • 例如: djangoApp_users

  • ordering:对象的默认排序字段,获取对象的列表时使用,接收属性构成的列表

    class BookInfo(models.Model):
        ...
        class Meta():
            ordering = ['id']
    
  • 字符串前加-表示倒序,不加-表示正序

    class BookInfo(models.Model):
        ...
        class Meta():
            ordering = ['-id']
    
  • 排序会增加数据库的开销

示例演示

  • 创建test2项目,并创建booktest应用,使用mysql数据库
  • 定义图书模型
    class BookInfo(models.Model):
        btitle = models.CharField(max_length=20)
        bpub_date = models.DateTimeField()
        bread = models.IntegerField(default=0)
        bcommet = models.IntegerField(default=0)
        isDelete = models.BooleanField(default=False)
    
  • 英雄模型
    class HeroInfo(models.Model):
        hname = models.CharField(max_length=20)
        hgender = models.BooleanField(default=True)
        isDelete = models.BooleanField(default=False)
        hcontent = models.CharField(max_length=100)
        hbook = models.ForeignKey('BookInfo')
    
  • 定义index、detail视图
  • index.html、detail.html模板
  • 配置url,能够完成图书及英雄的展示

测试数据

  • 模型BookInfo的测试数据

    insert into booktest_bookinfo(btitle,bpub_date,bread,bcommet,isDelete) values
    ('射雕英雄传','1980-5-1',12,34,0),
    ('天龙八部','1986-7-24',36,40,0),
    ('笑傲江湖','1995-12-24',20,80,0),
    ('雪山飞狐','1987-11-11',58,24,0)
    
  • 模型HeroInfo的测试数据

    insert into booktest_heroinfo(hname,hgender,hbook_id,hcontent,isDelete) values
    ('郭靖',1,1,'降龙十八掌',0),
    ('黄蓉',0,1,'打狗棍法',0),
    ('黄药师',1,1,'弹指神通',0),
    ('欧阳锋',1,1,'蛤蟆功',0),
    ('梅超风',0,1,'九阴白骨爪',0),
    ('乔峰',1,2,'降龙十八掌',0),
    ('段誉',1,2,'六脉神剑',0),
    ('虚竹',1,2,'天山六阳掌',0),
    ('王语嫣',0,2,'神仙姐姐',0),
    ('令狐冲',1,3,'独孤九剑',0),
    ('任盈盈',0,3,'弹琴',0),
    ('岳不群',1,3,'华山剑法',0),
    ('东方不败',0,3,'葵花宝典',0),
    ('胡斐',1,4,'胡家刀法',0),
    ('苗若兰',0,4,'黄衣',0),
    ('程灵素',0,4,'医术',0),
    ('袁紫衣',0,4,'六合拳',0)
    

模型实例

1 ) 类的属性

  • objects:是Manager类型的对象,用于与数据库进行交互
  • 当定义模型类时没有指定管理器,则Django会为模型类提供一个名为objects的管理器
  • 支持明确指定模型类的管理器
    class BookInfo(models.Model):
    ...
    books = models.Manager()
    
  • 当为模型类指定管理器后,django不再为模型类生成名为objects的默认管理器

2 ) 创建对象

  • 当创建对象时,django不会对数据库进行读写操作
  • 调用save()方法才与数据库交互,将对象保存到数据库中
  • 使用关键字参数构造模型对象很麻烦,推荐使用下面的两种之式
  • 说明:init方法已经在基类models.Model中使用,在自定义模型中无法使用

3 ) 实例的属性

  • DoesNotExist:在进行单个查询时,模型的对象不存在时会引发此异常,结合try/except使用

4 ) 实例的方法

  • __str__(self):重写object方法,此方法在将对象转换成字符串时会被调用
  • save():将模型对象保存到数据表中
  • delete():将模型对象从数据表中删除

模型查询

  • 查询集表示从数据库中获取的对象集合
  • 查询集可以含有零个、一个或多个过滤器
  • 过滤器基于所给的参数限制查询的结果
  • 从Sql的角度,查询集和select语句等价,过滤器像where和limit子句
  • 接下来主要讨论如下知识点
    • 查询集
    • 字段查询:比较运算符,F对象,Q对象

1 ) 查询集

  • 在管理器上调用过滤器方法会返回查询集
  • 查询集经过过滤器筛选后返回新的查询集,因此可以写成链式过滤
  • 惰性执行:创建查询集不会带来任何数据库的访问,直到调用数据时,才会访问数据库
  • 何时对查询集求值:迭代,序列化,与if合用
  • 返回查询集的方法,称为过滤器
    • all()
    • filter()
    • exclude()
    • order_by()
    • values() : 一个对象构成一个字典,然后构成一个列表返回
  • 写法:
    filter(键1=值1,键2=值2)
    等价于
    filter(键1=值1).filter(键2=值2)
    
  • 返回单个值的方法
    • get():返回单个满足条件的对象
      • 如果未找到会引发"模型类.DoesNotExist"异常
      • 如果多条被返回,会引发"模型类.MultipleObjectsReturned"异常
    • count():返回当前查询的总条数
    • first():返回第一个对象
    • last():返回最后一个对象
    • exists():判断查询集中是否有数据,如果有则返回True

限制查询集

  • 查询集返回列表,可以使用下标的方式进行限制,等同于sql中的limit和offset子句
  • 注意:不支持负数索引
  • 使用下标后返回一个新的查询集,不会立即执行查询
  • 如果获取一个对象,直接使用[0],等同于[0:1].get(),但是如果没有数据,[0]引发IndexError异常,[0:1].get()引发DoesNotExist异常
    #这会返回前5个对象 LIMIT 5
    Entry.objects.all()[:5]
    #这将返回第六个到第十个对象 OFFSET 5 LIMIT 5
    Entry.objects.all()[5:10]
    

查询集的缓存

  • 每个查询集都包含一个缓存来最小化对数据库的访问
  • 在新建的查询集中,缓存为空,首次对查询集求值时,会发生数据库查询,django会将查询的结果存在查询集的缓存中,并返回请求的结果,接下来对查询集求值将重用缓存的结果
  • 情况一:这构成了两个查询集,无法重用缓存,每次查询都会与数据库进行一次交互,增加了数据库的负载
    print([e.title for e in Entry.objects.all()])
    print([e.title for e in Entry.objects.all()])
    
  • 情况二:两次循环使用同一个查询集,第二次使用缓存中的数据
    querylist=Entry.objects.all()
    print([e.title for e in querylist])
    print([e.title for e in querylist])
    
  • 何时查询集不会被缓存:当只对查询集的部分进行求值时会检查缓存,但是如果这部分不在缓存中,那么接下来查询返回的记录将不会被缓存,这意味着使用索引来限制查询集将不会填充缓存,如果这部分数据已经被缓存,则直接使用缓存中的数据

2 ) 字段查询

  • 实现where子名,作为方法filter()、exclude()、get()的参数
  • 语法:属性名称__比较运算符=值
  • 表示两个下划线,左侧是属性名称,右侧是比较类型
  • 对于外键,使用“属性名_id”表示外键的原始值
  • 转义:like语句中使用了%与,匹配数据中的%与,在过滤器中直接写,例如:filter(title__contains="%")=>where title like ‘%%%’,表示查找标题中包含%的

比较运算符

  • exact:表示判等,大小写敏感;如果没有写“ 比较运算符”,表示判等

    • filter(isDelete=False)
  • contains:是否包含,大小写敏感

    • exclude(btitle__contains='www')
  • startswithendswith:以value开头或结尾,大小写敏感

    • exclude(btitle__endswith='com')
  • isnullisnotnull:是否为null

    • filter(btitle__isnull=False)
  • 在前面加个i表示不区分大小写,如iexacticontainsistarswithiendswith

  • in:是否包含在范围内

    • filter(pk__in=[1, 2, 3, 4, 5])
  • gtgteltlte:大于、大于等于、小于、小于等于

    • filter(id__gt=3)
  • yearmonthdayweek_dayhourminutesecond:对日期间类型的属性进行运算

    • filter(bpub_date__year=1980)
    • filter(bpub_date__gt=date(1980, 12, 31))

跨关联关系的查询:处理join查询

  • 语法:模型类名 <属性名> <比较>
  • 注:可以没有__<比较>部分,表示等于,结果同inner join
  • 可返向使用,即在关联的两个模型中都可以使用
    • filter(heroinfo_ _hcontent_ _contains='八')

聚合函数

  • 使用aggregate()函数返回聚合函数的值
  • 函数:AvgCountMaxMinSum
    from django.db.models import Max
    maxDate = list.aggregate(Max('bpub_date'))
    
  • count的一般用法
    • count = list.count()

Model模型的实战

1 ) 创建数据库和表(若是执行表迁移,可跳过下面操作)

  • 进入MySQL数据库创建数据库:d_models_db
  • 进入数据库创建数据表:users
    CREATE TABLE `myapp_users` (                          
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,       
    `name` varchar(32) NOT NULL,                         
    `age` tinyint(3) unsigned NOT NULL DEFAULT '20',     
    `phone` varchar(16) DEFAULT NULL,
    `addtime` datetime(6) NOT NULL,                    
    PRIMARY KEY (`id`)                                   
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
    
  • 备注:当然也可以通过迁移文件生成users表(需要使用Meta来改名)
  • 添加几条测试数据

2 ) 创建项目djangoApp和应用app

  • 创建项目框架 djangoApp $ django-admin startproject djangoApp && cd djangoApp
  • 在项目中创建一个myapp应用 $ python3 manage.py startapp app
  • 创建模板目录 $ mkdir tpls && cd mkdir tpls/app
  • 注:最外层的目录Django-1.11.11-model 是改名而来的
  • 项目目录结构:
    Django-1.11.11-model
        ├── djangoApp
        │   ├── __init__.py
        │   ├── settings.py
        │   ├── urls.py
        │   └── wsgi.py
        ├── manage.py
        ├── app
        │   ├── admin.py
        │   ├── apps.py
        │   ├── __init__.py
        │   ├── models.py
        │   ├── tests.py
        │   └── views.py
        └── tpls
            └── app
    

3 ) 执行数据库连接配置,网站配置

  • 编辑djangoApp/init.py文件,添加Pymysql的数据库操作支持

    import pymysql
    pymysql.install_as_MySQLdb()
    
  • 编辑djangoApp/settings.py文件,配置数据库连接

    # ...
    #配置自己的服务器IP地址
    ALLOWED_HOSTS = ['localhost','127.0.0.1','*']
    
    # ...
    #添加自己应用
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'app',
    ]
    
    # ...
    
    # 配置模板路径信息
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR,'tpls')],
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    
    # ...
    # 数据库连接配置
    DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'd_models_db',
        'USER': 'root',
        'PASSWORD': '', # 填写自己的数据库密码
        'HOST': 'localhost',
        'PORT': '3306',
    }
    # ...
    

4 ) 定义Model类

  • 编辑djangoApp/models.py

    from django.db import models
    from datetime import datetime
    
    class Users(models.Model):
        name = models.CharField(max_length=32)
        age = models.IntegerField(default=20)
        phone = models.CharField(max_length=20)
        createTime = models.DateTimeField(default=datetime.now)
        
        def __str__(self):
            return self.name+":"+self.phone
    
        class Meta:
            db_table = "users"  # 指定表名
    
  • 测试Model类的使用, 执行 $ python3 manage.py shell

    [root@localhost djangoApp]#  python3 manage.py shell
    >>> from app.models import Users
    >>> Users.objects.all()
    <QuerySet [<Users: 张三:12345678901>, <Users: aa:13456789023>]>
    >>> s = Users.objects.get(id=1)
    >>> s.id
    1
    >>> s.name
    '张三'
    >>> s.age
    20
    >>> s.phone
    '12345678901'
    

5 ) 实现Web端访问

  • 编辑djangoApp/settings.py文件.做 ALLOWED_HOSTS 主机访问配置(若之前已做可跳过此步骤)

    • ALLOWED_HOSTS = ['localhost','127.0.0.1','这里填写主机的ip地址', '*']
  • 编写项目主路由urls配置,配置对djangoApp应用路由的访问连接配置

    from django.conf.urls import include,url
    from django.contrib import admin
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^', include('app.urls')),
    ]
    
  • 配置当前应用djangoApp的路由配置

    • 在djangoApp应用目录下创建一个路由文件urls.py文件,注意此文件编码为utf-8(建议复制一个)。
    • 编辑应用中的路由配置文件:djangoApp/app/urls.py, 内容如下:
      from django.conf.urls import url
      from . import views
      
      urlpatterns = [
          url(r'^$', views.index, name="index"),
      ]
      
  • 编辑视图文件:djangoApp/views.py,内容如下

    #from django.shortcuts import render
    from django.http import HttpResponse
    from myapp.models import Users
    
    def index(request):
        try:
            s = Users.objects.get(id=1)
            return HttpResponse(s)
        except:
            return HttpResponse("没有找到对应的信息!")
    
  • 测试环节:在项目根目录下运行 $ python3 manage.py runserver命令,开启服务

  • 打开浏览器,在浏览其中输入网址测试:127.0.0.1:8000

  • 继续上面操作完成Web版的Users信息的crud操作,详见下面示例仓库

6 ) 示例操作代码仓库详见

发布了417 篇原创文章 · 获赞 228 · 访问量 70万+

猜你喜欢

转载自blog.csdn.net/Tyro_java/article/details/104296423