web应用中,主观逻辑(视图处理)经常牵涉与数据库的交互。数据库驱动网站在后台连接数据库,从中取数据,然后甩漂亮界面展现在web端。许多复杂的网站都提供以上两种功能的结合(如Amazon.com),而Python天生具备简单强大的数据库查询执行方法,很适合开发这类数据库驱动网站。

他就是下面介绍的Django数据库层!

【 硬 编 码 链 接 数 据 库 】

cat ~/HelloWorld/HelloWorld/view.py

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

弊端:此方法虽可用,但是我们将参数配置直接硬编码到视图函数,显然犯了前两节相同的错误;如果我们改一个参数、改一种数据库、换个执行语句等等,都会引起大范围的改动。

那我们能不能像上节那样创建模板,其实django提供了更简单直接的方式:数据库API

===============================================================================================

MVC架构模式、MTV 开发模式

Django 设计遵循松耦合原则,修改某部分不影响其他部分。视图函数中通过了模板系统将业务逻辑和表现逻辑分隔,数据库层也应如此。

MVC 架构模式:即数据存取逻辑(model)、表现逻辑(view)、业务逻辑(controller)的组合

其中 C 由框架自行处理,django更关注的是模型(model)、模板(template)、视图(views),也被称MTV框架

M:数据存取,处理与数据相关所有事务;

T:表现层,处理与表现相关的决定,比如页面如何显示;

V:视图,处理存取模型和调取模板的逻辑,可看做模型与模板的桥梁。

注: django认为视图用来展现数据,控制器是如何展示;其他框架认为控制器用来展现数据,视图决定如何展现。

================================================================================================

上面所说的数据库API,其实就是更改django配置文件,通过配置文件的接口进数据库。

数据库DATABASE_ENGINE适配器
PostgreSQLpostgresqlhttp://www.djangoproject.com/r/python-pgsql/1/
PostgreSQLpostgresql_psycopg2http://www.djangoproject.com/r/python-pgsql/
MySQLmysqlhttp://www.djangoproject.com/r/python-mysql/
SQLitesqlite3http://www.djangoproject.com/r/python-sqlite/
Oracleoraclehttp://www.djangoproject.com/r/python-oracle/
vim ~/HelloWorld/HelloWorld/settings.py

DATABASES = {
    'default': {
    'ENGINE': 'django.db.backends.mysql',
    'NAME': 'django',
    'USER': 'django',
    'PASSWORD': 'django123',
    'HOST':'localhost',
    'PORT':'3306',
}
}

之后去数据库建一个django库,并给django用户赋值。然后测试一下(没有报错信息即配置成功,并能链接到mysql的django库了,关于django报错信息,请参考下一节):

启动shell界面:python manage.py  shell

>>> from django.db import connection

>>> cursor = connection.cursor()

【应用程序app 与 模型model.py】

第 一 个 app 

Django规定:如果使用了django数据库层(或模型),必须要创建django app,把模型放在里面;应用程序app是项目project组成部分。

python manage.py startapp mysql_django    #创建mysql_django app

wKioL1hcETyAMEeSAAAWixnqf4s271.png

创建好app,将app加入到HelloWorld project中

vim ~/HelloWorld/HelloWorld/settings.py

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
**   'mysql_django',      #逗号结尾,也可用点语法 HelloWorld.mysql_django
]

第 一 个 模 型 

其实django模型就是以python代码表述数据库中表结构

vim ~/HelloWorld/mysql_django/models.py

from django.db import models

class Publisher(models.Model): #CREATE TABLE "books_publisher"("id" serial NOT NULL PRIMARY KEY,
    name = models.CharField(max_length=30)   #"name" varchar(30) NOT NULL,
   address = models.CharField(max_length=50) #"address" varchar(50) NOT NULL,
   city = models.CharField(max_length=60)    #"city" varchar(60) NOT NULL,
   state_province = models.CharField(max_length=30) #"state_province" varchar(30) NOT NULL,
   country = models.CharField(max_length=50) #"country" varchar(50) NOT NULL,
   website = models.URLField()        #"website" varchar(200) NOT NULL );
                        
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)
   publication_date = models.DateField()

代码解读:

1、首先每个数据模型都是django.db.models.Model的子类,父类Model包含所有与数据库交互的必要方法;

2、每个模型相当于数据库表,每个属性即字段,类型(CharField)相当于字段类型(varchar)

3、Publisher举例,python语法即等于右边的数据库语法。

4、定义了出版商、作者、书籍的一些内容。

当完成以上模型建表后,并将其app加入到了项目,接下来就开始执行建表了

a、检查语法错误(django1.9.0版本以下用 python manage.py validate)

python  manage.py  check   

b、生成数据库迁移文件(在app下的migrations下生成0001文件,更改后执行,生成0002....迁移文件)

python  manage.py  makemigrations mysql_django

c、同步数据库(老版本用python manage.py syncdb,即把迁移文件转换语法同步数据库[库名app名_类名])

python  manage.py  migrate

d、转换成 SQL 语言(老版本 python manage.py sqlall mysql_django,查看转换后的mysql语法)

python  manage.py  sqlmigrate  mysql_django  0001

【Python 与 数 据 库 API】

进入:python manage.py shell

>>> from mysql_django.models  import Publisher      #导入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()                    #调用了save()方法,才能真正将p1插入数据库

>>> p2 = Publisher.objects.create(name="O'Reilly",

...     address='10 Fawcett St.', city='Cambridge',

...     state_province='MA', country='U.S.A.',

...     website='http://www.oreilly.com/')    #objects.create()方法可直接将数据插入数据库

>>> publisher_list = Publisher.objects.all()    #从数据库取出出版商信息==select

>>> publisher_list

[<Publisher: Publisher object>, <Publisher: Publisher object>]


插入两个数据后,讲道理Publisher.objects.all()方法会取出Publisher类的对象,但是却没有得到有用信息;

解决此问题:要为Publisher对象添加一个__unicode__()方法,他告诉python将对象以unicode方式显示,如下:

vim ~/HelloWorld/mysql_django/models.py

from django.db import models

class Publisher(models.Model):
     name = models.CharField(max_length=30)
     ......................
     website = models.URLField()
**      def __unicode__(self):
**       return self.name
class Author(models.Model):
     .........................
**    def __unicode__(self):
**       return u'%s %s' % (self.first_name, self.last_name)**
class Book(models.Model):
     .........................
**    def __unicode__(self):
**       return self.title

============================================================================================

unicode对象 是能处理上百万不同类的字符串的python字符串。普通的python字符串是经过编码的,穿插调用的话就会出现常见的???等乱七八糟的字符,这就是编码的问题;而unicode对象没有编码,使用它可以不用考虑编码的问题。所以请确保每个模型都定义了__unicode__()方法。

关于unicode:http://www.joelonsoftware.com/articles/Unicode.html 

再次进入shell解释器内,试验:>>> publisher_list

[<Publisher: Apress>, <Publisher: O'Reilly>]

============================================================================================

1、数据查看、过滤操作:

>>> Publisher.objects.filter(name='Apress')
[<Publisher: Apress>]          #filter == where name='Apress'
>>> Publisher.objects.filter(country="U.S.A.", state_province="CA")
[<Publisher: Apress>]          #逗号转换成了 and
>>> Publisher.objects.filter(name__contains="press")
[<Publisher: Apress>]          #__contains  = where name LIKE '%press%';
>>> Publisher.objects.get(name="Apress")
<Publisher: Apress>           #get()方法获取单个对象,filter()函数返回一个记录集(列表)

(icontains[大小写无关的LIKE]、startwith/endswith、range[BETWEEN])


2、数据排序操作:
>>> Publisher.objects.order_by("name")     
[<Publisher: Apress>, <Publisher: O'Reilly>]     #按字母排序
>>> Publisher.objects.order_by("state_province", "address")
[<Publisher: Apress>, <Publisher: O'Reilly>]     #第一个字段相同,按第二个排序
>>> Publisher.objects.order_by("-name")
[<Publisher: O'Reilly>, <Publisher: Apress>]     #-号,表示逆向排序

vim ~/HelloWorld/mysql_django/models.py   

class Publisher(models.Model):
    def __unicode__(self):
      return self.name
**   class Meta:              
**     ordering = ['name']  #Meta类,默认会以name字段进行排序

3、连锁查询:
>>> Publisher.objects.filter(country="U.S.A.").order_by("-name")
[<Publisher: O'Reilly>, <Publisher: Apress>]  #检索country="U.S.A."降序排列
>>> Publisher.objects.order_by('name')[0:2]   #取数据的特定子集=offset 0 limit 2,查询语句也可以加
>>> Publisher.objects.order_by('name')[0]    #[0]=limit 1;不支持负索引[-1],但可用降序[0]实现
<Publisher: Apress>


4、增加更改操作:(name、save、id、update)

添加数据:>>> p3 = ....name='Apress' ...

获取数据:>>> p2 = Publisher.objects.get(name="Apress")

更改数据:>>> p2.name = 'Apress Publishing'

保存数据:>>> p2.save()    #他会update此id全部字段,不只是name字段;也有轻量级更改的方法。

看主键id:>>> p2.id      #我们可根据get到的数据查id,也可直接进数据库查

更改数据:>>> Publisher.objects.filter(id=1).update(name='Apress Publishing')

批量更改:>>> Publisher.objects.all().update(country='USA')    #返回数值即更改的条数

5、删除命令操作:

获取数据:>>> p = Publisher.objects.get(name="O'Reilly")      #先得到数据再删除

删除数据:>>> p.delete()

直接删除:>>> Publisher.objects.filter(country='USA').delete()  #删除所有country='USA'的

删除全部:>>> Publisher.objects.all().delete()            #不加all()方法删除不了

查看数据:>>> Publisher.objects.all()


当然,以上的操作都是直接在shell解释器中操作,我们也可以写到视图函数中,页面化查看。

-------------------------------------------------------------------------------------------------