为什么要用数据库?
在当代Web应用中,主观逻辑经常要涉及于数据库的交互。数据库驱动网站在后台连接数据库服务器,从中取出一些数据,然后在Web页面用漂亮的格式展示出来,这个网站也可能会向访问者提供修改数据库数据的方法。本质上,每个产品页面都是的数据库中的数据以HTML格式进行展现。
数据库配置
我们要告诉FDjango是用什么数据库以及如何连接数据库。和模板的配置一样,数据库的配置也在settings.py文件中,数据库的配置。
# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
'USER':'',
'PASSWORD':'',
'HOST':'',
'PORT':''
}
}
设置 | 数据库 | 所需适配器 |
---|---|---|
postgresql | PostgreSQL | psycopg1.x版,[http://www.djangoproject.com/r/python-%20pgsql/1/] |
postgresql_psycopg2 | PostgreSQL | psycopg2.x版,[http://www.djangoproject.com/r/python-%20pgsql/] |
mysql | MySQL | MySQLdb,[http://www.djangoproject.com/r/python-mysql/] |
sqlite3 | SQLite | 如果使用python2.5+则不需要适配器,[http://www.djangoproject.com/r/python-sqlite/] |
oracle | Oracle | cx_Oracle,[http://www.djangoproject.com/r/python-oracle/] |
可以通过右侧链接免费获取这些适配器。Linux中会提供式和的包。
创建一个新的app,‘books’
在你的项目目录下使用命令创建
python manage.py startapp books
books
├── admin.py
├── apps.py
├── __init__.py
├── migrations
│ └── __init__.py
├── models.py
├── tests.py
└── views.py
在Django中使用python代码描述数据库模型和不使用SQL的原因。
一、运行时自动识别数据库会导致过载和有数据库完整性的问题。Django需要以某种方式知道数据库层内部信息,有两种实现方式。第一种是用Python明确地定义数据模型,第二种方式通过自省来自东侦测识别数据模型。
二、开发人员不用在SQL和python之间来回切换,提高了开发效率,
三、把数据库模型用代码的方式表述可以容易对它们进行版本控制,这样就很容易了解数据层的变动情况。
四、Django有专用的字段类型来描述Email地址、URL,而SQL则没有这些功能,高级的数据类型带来更高效和更好的代码复用。
五、可以避免不同数据库平套的兼容性问题。
第一个模型
构建一个书籍/作者/出版商 数据库结构
概念、字段、关系:
一个作者有姓名和email地址
出版商有名称、地址、所在城市、国家、省和网站
书籍有书名和出版日期,可以包含一个或多个作者,但只有一个出版商
DjangoPro/books/models.py
from django.db import models
# Create your models here.
# models.Model:包含所有必要的和数据库交互的方法,并提供
#一个简洁漂亮的定义数据库字段的语法。
class Publisher(models.Model):
"""出版商的类,对应数据库中的一个表"""
#出版商的名称,CharField字符型,max_length=30最大长度
name=models.CharField(max_length=30)
city=models.CharField(max_length=60)
state_province=models.CharField(max_length=50)
country=models.CharField(max_length=50)
#网址,数据类型为规定的URL的类型
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.Foreign(Publisher)
#出版日期,使用Django中的时间类型字段
publication_date=models.DateField()
不像Flask,Django中除非你单独指明,都则Django对自动为每模型生成一个字增长的整数主键字段id,每个Django模型要求有独立的主键。
激活模型
将books app添加到配置文件的已安装应用列表中
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'books',
]
INSTALLED_APPS告诉Django项目那些app出于激活状态。
在Django1.1中使用check来检验,
用来检测数据库变更和生成数据库迁移文件 ,在app下建立migrations目录,并记录所有关于model.py的改动,如果有改动,这个命令就会生成一个布丁文件用来记录改动。原文件中的数据不变。
python manage.py makemigrations
真正的作用于数据库文件,产生对应的表
python manage.py migrate
-*- coding: utf-8 -*-
Generated by Django 1.11 on 2018-08-09 02:36
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Author',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('first_name', models.CharField(max_length=30)),
('last_name', models.CharField(max_length=40)),
('email', models.EmailField(max_length=254)),
],
),
migrations.CreateModel(
name='Book',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=100)),
('publication_date', models.DateField()),
('authors', models.ManyToManyField(to='books.Author')),
],
),
migrations.CreateModel(
name='Publisher',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=30)),
('city', models.CharField(max_length=60)),
('state_province', models.CharField(max_length=50)),
('country', models.CharField(max_length=50)),
('website', models.URLField()),
],
),
migrations.AddField(
model_name='book',
name='publisher',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='books.Publisher'),
),
]
基本数据访问
>>> from books.models import Publisher
# 实例化一个出版商对象
>>> p1 = Publisher(name='Apress', address='2855 Telegraph Avenve',
... city='Berkeley', state_province='CA', country='USA',
... website='http://www.apress.com/')
# 将对象保存至数据库内,在创建对象时并未保存
>>> p1.save()
# 在创建时就可以保存对象到数据库
>>> p2 = Publisher.objects.create(name='Reilly', address='10 Fawcett St',
... city='Cambridge', state_province='MA', country='USA',
... website='http://www.oreilly.com/')
# Publisher.objects()从数据库中取出出版商信息,这个操作的幕后Django执行了一条SQL‘select’语句
>>> publisher_list = Publisher.objects.all()
>>> publisher_list
, ]>
我们可以看到数据库中现在有两个对象,如果想看到字符串形式,可以给每个类添加**unicode方法(python3中使用str**方法),它告诉python如何将对象以字符串形式显示出来
class Publisher(models.Model):
"""出版商的类,对应数据库中的一个表"""
#出版商的名称,CharField字符型,max_length=30最大长度
name=models.CharField(max_length=100)
address=models.CharField(max_length=50)
city=models.CharField(max_length=60)
state_province=models.CharField(max_length=50)
country=models.CharField(max_length=50)
#网址,数据类型为规定的URL的类型
website=models.URLField()
def __unicode__(self):
return self.name
class Author(models.Model):
"""关于作者的类"""
first_name=models.CharField(max_length=30)
last_name=models.CharField(max_length=40)
#数据类型为规定的邮箱的类型
email=models.EmailField()
def __unicode__(self):
return u'%s %s' %(self.first_name,self.last_name)
class Book(models.Model):
"""关于书籍的类"""
title=models.CharField(max_length=100)
#外键关联作者,多对多
authors=models.ManyToManyField(Author)
#外键关联出版商,一对一
publisher=models.ForeignKey(Publisher)
#出版日期,使用Django中的时间类型字段
publication_date=models.DateField()
def __unicode__(self):
return self.title
退出之前的shell,重新进入导包,运行结果为
, , , ]>
选择对象
Publisher.objects.all()
objects是模型的属性,它被称为管理器。所有的模型都自动拥有一个管理器。all()方法实际上是一个
QuerySet对象,这个对象是数据库中一些记录的集合。所有数据库查询都遵循一个通用模式。
数据过滤
使用‘filter()’方法进行过滤:
>>> p=Publisher.objects.filter(name='Apress')
>>> p
, ]>
#符合条件的第一个对象
>>> p=Publisher.objects.filter(name='Apress').first()
>>> p
#符合条件对象的个数
>>> p=Publisher.objects.filter(name='Apress').count()
>>> p
2
获取单个对象
filter()方法获取的是一个记录集,是一个列表。get()方法则会返回单个对象,如果返回多个或者对象不存在则会抛出异常。
数据排序
>>> Publisher.objects.order_by('name')
, , , ]>
逆向排序
给要排序的关键字前面加上‘-’
>>> Publisher.objects.order_by('-name')
, , , ]>
Meta缺省排序方式,在需要排序的数据库中指定一个类;
class Publisher(models.Model):
"""出版商的类,对应数据库中的一个表"""
#出版商的名称,CharField字符型,max_length=30最大长度
name=models.CharField(max_length=100)
address=models.CharField(max_length=50)
city=models.CharField(max_length=60)
state_province=models.CharField(max_length=50)
country=models.CharField(max_length=50)
#网址,数据类型为规定的URL的类型
website=models.URLField()
def __str__(self):
return self.name
class Meta(object):
ordering=['name']
内嵌于Publisher这个类的定义中。可以在任意一个模型类中使用它。Publisher对象的相关返回默认值惠安name字段排序,除非使用order_by()。
连锁查询
可以同时进行过滤和排序
>>> Publisher.objects.filter(country='USA').order_by('-name')
限制返回的数据
取出固定数目的数据
支持如python的类表类型的切片索引,但不支持负索引。
>>> Publisher.objects.order_by('-name')[0]
>>> Publisher.objects.order_by('-name')[0:2]
, ]>
>>> Publisher.objects.order_by('-name')[-1]
"Negative indexing is not supported."
AssertionError: Negative indexing is not supported.
更新数据
>>> p=Publisher.objects.filter(name='Apress').update(name='Apress Publish')
>>> Publisher.objects.all()
, , , ]>
update()还可以同时更改多条数据。返回的是更改数据的条数。
删除对象
>>> p=Publisher.objects.filter(name='Reilly')
>>> p.delete()
(2, {'books.Publisher': 2})
#2表示删除了两条
访问外键(Foreign Key)
当你获取一个ForeignKey字段时会得到相关的数据模型对象。
>>> b=Book.objects.get(id=3)
>>> b.publisher
>>> b.publisher.website
'http://www.apress.com/'
对于用“Foreigney”来定义的关系来说,在关系另一端也能反向的追溯回来。比如通过publisher对象获取books。
>>> from books.models import *
>>> p=Publisher.objects.get(name='Reilly')
>>> p.book_set.all()
]>
book_set只是一个查询集合(QuerySet),所以它想Queryset一样能实现数据过滤和分切。
访问多对多值(Many-to-Many Values)
多对多和外键工作方式相同,只不过我们处理的是Queryset而不是模型。
>>> from books.models import *
>>> b=Book.objects.get(id=3)
>>> b.authors.all()
]>
>>> b.authors.filter(first_name='M')
]>
>>> b.authors.filter(first_name='Q')
反向查询,
>>> a=Author.objects.get(first_name='M',last_name='sk')
>>> a.book_set.all()
, , ]>
更改数据库模式
python manage.py makemigrations
生成更改的补丁文件
python manage.py migrate
模块manager是一个对象Django模块通过它纪念性数据库查询,每个Django模块至少有一个manager,也可以创建自定义manager以定制数据库访问。
增加额外的Manager方法
为模块添加表级功能的首选方法。
#继承models.Manager,这个类只有一个方法,用来做统计
class BookManager(models.Manager):
"""定义一个查看书籍数量的类"""
def title_count(self,keyword):
return self.filter(title=keyword).count()
class Book(models.Model):
"""关于书籍的类"""
title=models.CharField(max_length=100)
#外键关联作者,多对多
authors=models.ManyToManyField(Author)
#外键关联出版商,一对一
publisher=models.ForeignKey(Publisher)
#出版日期,使用Django中的时间类型字段
publication_date=models.DateField(blank=True,null=True)
#赋值给objects属性,之后,objects的默认属性不再有效
objects=BookManager()
def __str__(self):
return self.title
>>> Book.objects.title_count('python')
1
修改初始Manager QuerySets
#返回指定作者的所有书
class DathBookManager(models.Manager):
def get_query_set(self):
return super(DathBookManager, self).get_queryset().filter(authors='M sk')
class Book(models.Model):
"""关于书籍的类"""
title=models.CharField(max_length=100)
#外键关联作者,多对多
authors=models.ManyToManyField(Author)
#外键关联出版商,一对一
publisher=models.ForeignKey(Publisher)
#出版日期,使用Django中的时间类型字段
publication_date=models.DateField(blank=True,null=True)
#赋值给objects属性,之后,objects的默认属性不再有效
# objects=BookManager()
#重新指定obdects的返回值
objects=models.Manager()
dahl_objects=DathBookManager()
需要明确的将objects设置成manager实例,如果不这么做,那么唯一可用的manager就只有dahl_objects。
如果使用自定义的Manager对象,Django遇到的第一个Manager会有一个特殊状态。Django将会把第一个Manager定义为默认的Manager,Django的许多应用将会明确地为模型使用这个Manager。
模型方法
给对象添加一个行级功能,那就自定义一个方法。
class Person(models.Model):
name=models.CharField(max_length=50)
birthday=models.DateField()
address=models.CharField(max_length=100)
city=models.CharField(max_length=100)
def __str__(self):
return self.name
def baby_boomer_status(self):
import datetime
if datetime.date(1945,8,1) <=self.birthday<=datetime.date(1964,12,31):
return "Baby boomer"
if self.birthday
>>> p=Person.objects.get(name='pern')
>>> p.birthday
datetime.date(2018, 8, 13)
>>> p.baby_boomer_status()
'Post-boomer'
执行原始SQL查询
可以为数据库写一些自定义SQL查询,通过导入django.db.connection来实现,它代表当前数据库连接。通过
connection.cursor()得到一个游标对象,然后使用cursor.execute(sql) 来执行SQL语句,用cursor.fetchone() 或者cursor.fetchall()来返回。