Django框架3——模型

Django数据库层解决的问题

在本例的视图中,使用了pymysql 类库来连接 MySQL 数据库,取回一些记录,将它们提供给模板以显示一个网页:

from django.shortcuts import render
import pymysql
def book_list(request):
    db = pymysql.connect(user='me', db='mydb', passwd='secret',                                                   host='localhost',charset='utf8')
    cursor = db.cursor()
    cursor.execute('SELECT name FROM books ORDER BY name')
    names = [row[0] for row in cursor.fetchall()]
    db.close()
    return render('book_list.html', {'names': names})

这个方法可用,但很快一些问题将出现在你面前:

  • 我们将数据库连接参数硬行编码于代码之中。 理想情况下,这些参数应当保存在 Django 配置中。
  • 我们不得不重复同样的代码: 创建数据库连接、创建数据库游标、执行某个语句、然后关闭数据库。 理想
    情况下,我们所需要应该只是指定所需的结果。
  • 它把我们栓死在 MySQL 之上。 如果过段时间,我们要从 MySQL 换到 PostgreSQL,就不得不使用不同
    的数据库适配器(例如 psycopg 而不是 pymysql ),改变连接参数,根据 SQL 语句的类型可能还要修改
    SQL 。 理想情况下,应对所使用的数据库服务器进行抽象,这样一来只在一处修改即可变换数据库服务
    器。

MTV 开发模式

​ Django 数据驱动 Web 应用的总体设计:
​ Django 的设计鼓励松耦合及对应用程序中不同部分的严格分割。 遵循这个理念的话,要想修改应用的某部分而不影响其它部分就比较容易了。 在视图函数中,我们已经讨论了通过模板系统把业务逻辑和表现逻辑分隔开的重要性。
把数据存取逻辑、业务逻辑和表现逻辑组合在一起的概念有时被称为软件架构的 Model-View-Controller
(MVC)模式。 在这个模式中, Model 代表数据存取层,View 代表的是系统中选择显示什么和怎么显示的部
分,Controller 指的是系统中根据用户输入并视需要访问模型,以决定使用哪个视图的哪个部分。

以下是 Django 中 M、V 和 C 各自的含义:
M ,数据存取部分,由django数据库层处理。
V ,选择显示哪些数据要显示以及怎样显示的部分,由视图和模板处理。
C ,根据用户输入委派视图的部分,由 Django 框架根据 URLconf 设置,对给定 URL 调用适当的Python 函数。
由于 C 由框架自行处理,而 Django 里更关注的是模型(Model)、模板(Template)和视图(Views),
Django 也被称为 MTV 框架 。在 MTV 开发模式中:
M 代表模型(Model),即数据存取层。 该层处理与数据相关的所有事务: 如何存取、如何验证有效
性、包含哪些行为以及数据之间的关系等。

​ T 代表模板(Template),即表现层。 该层处理与表现相关的决定: 如何在页面或其他类型文档中进行显
示。
​ V 代表视图(View),即业务逻辑层。 该层包含存取模型及调取恰当模板的相关逻辑。 你可以把它看作
模型与模板之间的桥梁。
如果你熟悉其它的 MVC Web开发框架,比方说 Ruby on Rails,你可能会认为 Django 视图是控制器,而
Django 模板是视图。 很不幸,这是对 MVC 不同诠释所引起的错误认识。 在 Django 对 MVC 的诠释中,视
图用来描述要展现给用户的数据;不是数据 如何展现 ,而且展现 哪些 数据。 相比之下,Ruby on Rails 及一些
同类框架提倡控制器负责决定向用户展现哪些数据,而视图则仅决定 如何 展现数据,而不是展现 哪些 数据。
两种诠释中没有哪个更加正确一些。 重要的是要理解底层概念。

数据库配置

https://docs.djangoproject.com/en/2.2/ref/settings/

初始配置;告诉Django使用什么数据库以及如何连接数据库。先创建了数据库(例如,用 CREATE DATABASE语句) settings.py :

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mydb',
        'USER': 'Sroxi',
        'PASSWORD': '123',
        'HOST': '127.0.0.1',
        'PORT': '6379',
    }
}

第一个应用程序

​ 让我们来创建一个 Django app:一个包含模型,视图和Django代码,并且形式为独立Python包的完整Django应用。我们已经创建了 project , 那么 project 和 app之间到底有什么不同呢?它们的区别就是一个是配置另一个是 代码
​ 一个project包含很多个Django app以及对它们的配置。技术上,project的作用是提供配置文件,比方说哪里定义数据库连接信息, 安装的app列表,TEMPLATE_DIRS ,等等。一个app是一套Django功能的集合,通常包括模型和视图,按Python的包结构的方式存在。例如,Django本身内建有一些app,例如注释系统和自动管理界面。 app的一个关键点是它们是很容易移植到其他project和被多个project复用
对于如何架构Django代码并没有快速成套的规则。 如果你只是建造一个简单的Web站点,那么可能你只需要
一个app就可以了; 但如果是一个包含许多不相关的模块的复杂的网站,例如电子商务和社区之类的站点,那
么你可能需要把这些模块划分成不同的app,以便以后复用。
不错,你可以不用创建app,这一点应经被我们之前编写的视图函数的例子证明了 。 在那些例子中,我们只是
简单的创建了一个称为views.py的文件,编写了一些函数并在URLconf中设置了各个函数的映射。 这些情况都
不需要使用apps。但是,系统对app有一个约定: 如果你使用了Django的数据库层(模型),你 必须创建一个Django app。模型必须存放在apps中。 因此,为了开始建造 我们的模型,我们必须创建一个新的app。

mysite 项目文件下输入下面的命令来创建books app:

python manage.py startapp books

在Python代码里定义模型

​ MTV里的M代表模型。 Django模型是用Python代码形式表述的数据在数据库中的定义。对数据层来说它等同于 CREATE TABLE 语句,只不过执行的是Python代码而不是 SQL,而且还包含了比数据库字段定义更多的含义。Django用模型在后台执行SQL代码并把结果用Python的数据结构来描述。 Django也使用模型来呈现SQL无法处理的高级概念。
​ 用Python 和 SQL来定义数据模型是不是有点多余? Django这样做是有下面几个原因的:

  • 自省(运行时自动识别数据库)会导致过载和有数据完整性问题。为了提供方便的数据访问API, Django
    需要以某种方式 知道数据库层内部信息,有两种实现方式。第一种方式是用Python明确地定义数据模
    型,第二种方式是通过自省来自动侦测识别数据模型。
  • 第二种方式看起来更清晰,因为数据表信息只存放在一个地方-数据库里,但是会带来一些问题。首先,运
    行时扫描数据库会带来严重的系统过载。如果每个请求都要扫描数据库的表结构,或者即便是服务启动时
    做一次都是会带来不能接受的系统过载。(有人认为这个程度的系统过载是可以接受的,而Django开发者
    的目标是尽可能地降低框架的系统过载)。第二,某些数据库,尤其是老版本的MySQL,并未完整存储那些
    精确的自省元数据。
  • 编写Python代码是非常有趣的,保持用Python的方式思考会避免你的大脑在不同领域来回切换。尽可能的
    保持在单一的编程环境/思想状态下可以帮助你提高生产率。不得不去重复写SQL,再写Python代码,再写
    SQL,…。
  • 把数据模型用代码的方式表述来让你可以容易对它们进行版本控制。这样,你可以很容易了解数据层的变
    动情况。
  • SQL只能描述特定类型的数据字段。例如,大多数数据库都没有专用的字段类型来描述Email地址、URL。
    而用Django的模型可以做到这一点。好处就是高级的数据类型带来更高的效率和更好的代码复用。
  • SQL还有在不同数据库平台的兼容性问题。发布Web应用的时候,使用Python模块描述数据库结构信息可
    以避免为MySQL, PostgreSQL, and SQLite编写不同的CREATE TABLE。

​ 当然,这个方法也有一个缺点,就是Python代码和数据库表的同步问题。 如果你修改了一个Django模型, 你要自己来修改数据库来保证和模型同步。

第一个模型

一个基本的 书籍/作者/出版商数据库结构。假定下面的这些概念、字段和关系:

  • 一个作者有姓,有名及email地址。
  • 出版商有名称,地址,所在城市、省,国家,网站。
  • 书籍有书名和出版日期。 它有一个或多个作者(和作者是多对多的关联关系[many-to-many]), 只有一
    个出版商(和出版商是一对多的关联关系[one-to-many],也被称作外键[foreign key])

第一步是用Python代码来描述它们。 打开由startapp 命令创建的models.py 并输入下面的内容:

from django.db import models
class Publisher(models.Model):
    name = models.CharField(max_length=30)
    address = models.CharField(max_length=50)
    city = models.CharField(max_length=60)
    state_province = models.CharField(max_length=30)
    country = models.CharField(max_length=50)
    website = models.URLField()
class Author(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=40)
    email = models.EmailField()
class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher,on_delete=models.CASCADE)
    publication_date = models.DateField()

首先要注意的事是每个数据模型都是 django.db.models.Model 的子类。它的父类 Model 包含了所有必要的和数据库交互的方法,并提供了一个简洁漂亮的定义数据库字段的语法。 每个模型相当于单个数据库表,每个属性也是这个表中的一个字段。 属性名就是字段名,它的类型(例如CharField )相当于数据库的字段类型 (例如 varchar )。例如, Publisher 模块等同于下面这张表(用MYSQL的 CREATE TABLE 语法描述)

CREATE TABLE books_publisher(
    id serial NOT NULL PRIMARY KEY,
    name varchar(30) NOT NULL,
    address varchar(50) NOT NULL,
    city varchar(60) NOT NULL,
    state_province varchar(30) NOT NULL,
    country varchar(50) NOT NULL,
    website varchar(200) NOT NULL
);

最后需要注意的是,我们并没有显式地为这些模型定义任何主键。 除非你单独指明,否则Django会自动为每
个模型生成一个自增长的整数主键字段每个Django模型都要求有单独的主键id

模型安装

'books'指示我们正在编写的books app。

setting.py

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

版本变化:移除:python manage.py syncdb --->改为: python manage.py makemigrations

执行:

 python manage.py makemigrations    # 负责将模型更改打包到单个迁移文件中(类似于提交) 

出现下面错误:

raise ImproperlyConfigured('mysqlclient 1.3.13 or newer is required; you have %s.' % Database.__version__)
django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.13 or newer is required; you have 0.9.3.

MySQLclient目前只支持到python3.4,因此如果使用的更高版本的python,通过查找路径\Python\Python37-32\Lib\site-packages\django\db\backends\mysql这个路径里的文件

base.py:注释下面内容

if version < (1, 3, 3):
     raise ImproperlyConfigured("mysqlclient 1.3.3 or newer is required; you have %s" % Database.__version__)

再次执行:python manage.py makemigration,又出现下方错误:

query = query.decode(errors='replace')
AttributeError: 'str' object has no attribute 'decode'

修改安装python目录下:\Python\Python37-32\Lib\site-packages\django\db\backends\mysql 的operations.py。

把其中的 
query = query.decode(errors='replace')
修改为
query = query.encode(errors='replace') 

再次执行:python manage.py makemigration,执行成功;books文件夹下的migrations文件夹中会出现0001_initial.py此时模型并没有同步到数据库。

需要执行:

python manage.py migrate          #负责将更改应用到数据库中

执行结束后,模型同步到数据库

在数据库中查看到表名是这种形式:books_publisher

ps:如果想打印模型转换过程中的sql,需要在settings中进行如下配置:

`LOGGING ``=` `{``  ``'version'``: ``1``,``  ``'disable_existing_loggers'``: ``False``,``  ``'handlers'``: {``    ``'console'``:{``      ``'level'``:``'DEBUG'``,``      ``'class'``:``'logging.StreamHandler'``,``    ``},``  ``},``  ``'loggers'``: {``    ``'django.db.backends'``: {``      ``'handlers'``: [``'console'``],``      ``'propagate'``: ``True``,``      ``'level'``:``'DEBUG'``,``    ``},``  ``}``}  `

基本数据访问

运行 python manage.py shell 并输入:

>>> from books.models import Publisher
>>> p1 = Publisher(name='Apress', address='2855 Telegraph Avenue',
... city='Berkeley', state_province='CA', country='U.S.A.',
... website='http://www.apress.com/')
>>> p1.save()
>>> p2 = Publisher(name="O'Reilly", address='10 Fawcett St.',
... city='Cambridge', state_province='MA', country='U.S.A.',
... website='http://www.oreilly.com/')
>>> p2.save()
>>> publisher_list = Publisher.objects.all()
>>> publisher_list
<QuerySet [<Publisher: Publisher object (1)>, <Publisher: Publisher object (2)>]>
  • 首先,导入Publisher模型类, 通过这个类我们可以与包含 出版社 的数据表进行交互。
  • 接着,创建一个Publisher 类的实例并设置了字段name, address 等的值。
    调用该对象的 save() 方法,将对象保存到数据库中。 Django 会在后台执行一条 INSERT 语句。
  • 最后,使用Publisher.objects 属性从数据库取出出版商的信息,这个属性可以认为是包含出版商的记
    录集。 这个属性有许多方法, 这里先介绍调用Publisher.objects.all() 方法获取数据库中Publisher
    类的所有对象。这个操作的幕后,Django执行了一条SQL SELECT 语句。

当你使用Django modle API创建对象时,Django并未将对象保存至数据库内,除非你调用save() 方法。

如果需要一步完成对象的创建与存储至数据库,就使用objects.create() 方法。 下面的例子与之前的例子等
价:

>>> p1 = Publisher.objects.create(name='Apress',
... address='2855 Telegraph Avenue',
... city='Berkeley', state_province='CA', country='U.S.A.',
... website='http://www.apress.com/')

添加模块的字符串表现

当我们打印整个publisher列表时(<QuerySet [<Publisher: Publisher object (1)>, <Publisher: Publisher object (2)>]>),我们没有得到想要的有用信息,无法把对象区分开来:

django官网: https://docs.djangoproject.com/en/1.11/topics/python3/#str-and-unicode-methods

from django.db import models
class Publisher(models.Model):
    name = models.CharField(max_length=30)
    address = models.CharField(max_length=50)
    city = models.CharField(max_length=60)
    state_province = models.CharField(max_length=30)
    country = models.CharField(max_length=50)
    website = models.URLField()
    def __str__(self):  #python2 使用__unicode__
        return u'%s %s' % (self.name, self.address)

Django的模型不只是为对象定义了数据库表的结构,还定义了对象的行为。 str() 就是一个例子来演示模型知道怎么显示它们自己。

插入和更新数据

p = Publisher(name='Apress',
... address='2855 Telegraph Ave.',
... city='Berkeley',
... state_province='CA',
... country='U.S.A.',
... website='http://www.apress.com/')
>>>p.save()
>>> p.name = 'Apress Publishing'
>>> p.save()

注意,并不是只更新修改过的那个字段,所有的字段都会被更新。 这个操作有可能引起竞态条件,这取决于你
的应用程序。 请参阅后面的“更新多个对象”小节以了解如何实现这种轻量的修改(只修改对象的部分字
段)。

选择对象

当然,创建新的数据库,并更新之中的数据是必要的,但是,对于 Web 应用程序来说,更多的时候是在检索
查询数据库。 我们已经知道如何从一个给定的模型中取出所有记录:

Publisher.objects.all()

相当于sql语句:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher;

Django在选择所有数据时并没有使用 SELECT* ,而是显式列出了所有字段。 设计的时候就是这样:
SELECT* 会更慢,而且最重要的是列出所有字段遵循了Python 界的一个信条: 明言胜于暗示。

Publisher.objects.all() 这行的每个部分:

  • 首先,我们有一个已定义的模型Publisher 。没什么好奇怪的: 你想要查找数据, 你就用模型来获得数
    据。

  • 然后,是objects属性。它被称为管理器,管理器管理着所有针对数据包含、还有最重要的数据查询的表格级操作。所有的模型都自动拥有一个objects 管理器;你可以在想要查找数据时使用它。
  • 最后,还有all() 方法。这个方法返回返回数据库中所有的记录。尽管这个对象 看起来 象一个列表(list),它实际是一个 QuerySet 对象, 这个对象是数据库中一些记录的集合。

数据过滤(filter)

我们很少会一次性从数据库中取出所有的数据;通常都只针对一部分数据进行操作。 在Django API中,我们
可以使用filter() 方法对数据进行过滤:

>>> Publisher.objects.filter(name='Apress')
<QuerySet [<Publisher: Apress 2855 Telegraph Avenue>]>

filter() 根据关键字参数来转换成 WHERE SQL语句。 前面这个例子 相当于这样:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE name = 'Apress';

你可以传递多个参数到 filter() 来缩小选取范围:

Publisher.objects.filter(country="U.S.A.", state_province="CA")

模糊查询

>>>Publisher.objects.filter(name__contains="press")
<QuerySet [<Publisher: Apress 2855 Telegraph Avenue>]>

其他的一些查找类型有:icontains(大小写无关的LIKE),startswith和endswith, 还有range(SQLBETWEEN查
询)

获取单个对象(get)

上面的例子中filter() 函数返回一个记录集,这个记录集是一个列表。 相对列表来说,有些时候我们更需要
获取单个的对象, get() 方法就是在此时使用的:

>>>Publisher.objects.get(name="Apress")
<Publisher: Apress 2855 Telegraph Avenue>

如果查询没有返回结果也会抛出异常:

Publisher.objects.get(name='Aprs')

books.models.Publisher.DoesNotExist: Publisher matching query does not exist.

DoesNotExist 异常 是 Publisher 这个 model 类的一个属性,即 Publisher.DoesNotExist。在的应用
中,可以捕获并处理这个异常,像这样:

try:    p = Publisher.objects.get(name='Apress')except Publisher.DoesNotExist:    print("Apress isn't in the database yet.")else:    print("Apress is in the database.")

如果结果是多个对象,会导致抛出异常:

>>>Publisher.objects.get(country="U.S.A.")

books.models.Publisher.MultipleObjectsReturned: get() returned more than one Publisher -- it returned 2!

数据排序

Publisher.objects.order_by("name")

<QuerySet [<Publisher: Apress 2855 Telegraph Avenue>, <Publisher: O'Reilly 10 Fawcett St.>]>

如果需要以多个字段为标准进行排序(第二个字段会在第一个字段的值相同的情况下被使用到),使用多个参
数就可以了,如下:

Publisher.objects.order_by("state_province", "address")

逆向排序,在前面加一个减号 ‐ 前缀

>>> Publisher.objects.order_by("‐name")

大多数时间你通常只会对某些 字段进行排序。 在这种情况下,Django让你可以指定模型的缺省排序方式:

class Publisher(models.Model):
    name = models.CharField(max_length=30)
    address = models.CharField(max_length=50)
    city = models.CharField(max_length=60)
    state_province = models.CharField(max_length=30)
    country = models.CharField(max_length=50)
    website = models.URLField()
    def __unicode__(self):
    return self.name
    class Meta:
        ordering = ['name']

连锁查询

同时进行过滤和排序查询的操作

Publisher.objects.filter(country="U.S.A.").order_by("‐name")

转换成SQL查询就是 WHERE 和 ORDER BY 的组合:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE country = 'U.S.A'
ORDER BY name DESC;

限制返回的数据

不支持负索引

Publisher.objects.order_by('name')[0]

相当于

SELECT id, name, address, city, state_province, country, website
FROM books_publisher
ORDER BY name
LIMIT 1;
Publisher.objects.order_by('name')[0:2]

相当于

SELECT id, name, address, city, state_province, country, website
FROM books_publisher
ORDER BY name
OFFSET 0 LIMIT 2;

更新多个对象

save()方法,这个方法会更新一行里的所有列。 而某些情况下,我们只需要更新行里的某几列。

>>> p = Publisher.objects.get(name='Apress')
>>> p.name = 'Apress Publishing'
>>> p.save()

同于如下SQL语句(假设Apress的ID为52):

SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE name = 'Apress';
UPDATE books_publisher SET
name = 'Apress Publishing',
address = '2855 Telegraph Ave.',
city = 'Berkeley',
state_province = 'CA',
country = 'U.S.A.',
website = 'http://www.apress.com'
WHERE id = 52;

Django的save()方法更新了不仅仅是name列的值,还有更新了所有的列。 若name以外的列有可能会被其他的进程所改动的情况下,只更改name列显然是更加明智的。 更改某一指定的列,我们可以调用结果集(QuerySet)对象的update()方法:

Publisher.objects.filter(id=52).update(name='Apress Publishing')

与之等同的SQL语句变得更高效:

UPDATE books_publisher
SET name = 'Apress Publishing'
WHERE id = 52;

pdate()方法对于任何结果集(QuerySet)均有效,这意味着你可以同时更新多条记录。

update()方法会返回一个整型数值,表示受影响的记录条数。

>>> Publisher.objects.all().update(country='USA')
2

删除对象

删除数据库中的对象只需调用该对象的delete()方法即可

>>> p = Publisher.objects.get(name="O'Reilly")
>>> p.delete()

调用delete()方法同时删除多条记录

>>> Publisher.objects.filter(country='USA').delete()
>>> Publisher.objects.all().delete()

常用查询方法汇总

  1. all() : 调用者:objects管理器 返回queryset
  2. filter() :调用者:objects管理器 返回queryset
  3. get():调用者:objects管理器 返回查询到model对象 (注意:查询结果有且只有一个才执行)
  4. first(),last()方法:调用者:queryset 返回model对象
  5. exclude():调用者:objects管理器 返回queryset #排除
  6. order_by():由queryset对象调用,返回值是queryset
  7. count() :计数 :由queryset对象调用 返回int
  8. reverse():由queryset对象调用,返回值是queryset
  9. exists(): 由queryset对象调用 返回值布尔值 #存在性
  10. values()方法: 由queryset对象调用,返回值是 QuerySet
  11. values_list():由queryset对象调用,返回值是queryset
  12. distinct(): 由queryset对象调用,返回值是queryset #去重

values()方法

>>> Ebook.objects.filter(id = 1).values()
<QuerySet [{'id': 1, 'title': 'Python语言设计', 'publication_date': datetime.date(2016, 6, 13), 'price': Decimal('56.00'), 'publisher': '机械工业出版社'}]>
>>> Ebook.objects.filter(id = 1).values()[0]['title']
'Python语言设计'

values_list()方法

>>> Ebook.objects.filter(id = 1).values_list()
<QuerySet [(1, 'Python语言设计', datetime.date(2016, 6, 13), Decimal('56.00'), '机械工业出版社')]>
>>> Ebook.objects.filter(id = 1).values_list()[0][1]
'Python语言设计'
模糊查询

Ebook.objects.filter(price__in=[100,200,300])

Ebook.objects.filter(price__gt=100) 大于

Ebook.objects.filter(price__gte=100) 大于等于

Ebook.objects.filter(price__lt=100) 小于
Ebook.objects.filter(price__range=[100,200]) 100~200

Ebook.objects.filter(title__contains="python")

Ebook.objects.filter(title__icontains="python") i不区分大小写

Ebook.objects.filter(title__startswith="py")

Ebook.objects.filter(publication_date__year=2012)

猜你喜欢

转载自www.cnblogs.com/notfind/p/11691507.html