Django开发入门——数据库模型

为什么要用数据库?
在当代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':''
    }
}

  • ENGINE告诉Django使用哪个数据库引擎
    数据库引擎设置
设置数据库所需适配器
postgresqlPostgreSQLpsycopg1.x版,[http://www.djangoproject.com/r/python-%20pgsql/1/]
postgresql_psycopg2PostgreSQLpsycopg2.x版,[http://www.djangoproject.com/r/python-%20pgsql/]
mysqlMySQLMySQLdb,[http://www.djangoproject.com/r/python-mysql/]
sqlite3SQLite如果使用python2.5+则不需要适配器,[http://www.djangoproject.com/r/python-sqlite/]
oracleOraclecx_Oracle,[http://www.djangoproject.com/r/python-oracle/]

可以通过右侧链接免费获取这些适配器。Linux中会提供式和的包。

  • NAME将数据库名称告知Django
  • 如果使用SQLite,请对数据库文件指定完整的文件系统路径。
  • USER告诉Django用哪个用户连接数据库,如果是SQlite,空白即可。
  • PASSWORD告诉Django连接用户的密码,如果是SQlite,空白即可。
  • HOST告诉Django连接哪一台数据的数据库服务器,如果连接本地空白即可。
    这里写图片描述
    第一个应用程序
    创建一个Django app一个包含模型、视图和Django代码,并且形式为独立Python包的完整Django应用。
    系统对app有一个约定:如果使用了Django的数据库层,碧玺徐创建一个Django app。模型必须存放在apps中。

创建一个新的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
<QuerySet [<Publisher: Publisher object>, <Publisher: Publisher object>]>

我们可以看到数据库中现在有两个对象,如果想看到字符串形式,可以给每个类添加**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,重新进入导包,运行结果为

<QuerySet [<Publisher: Apress>, <Publisher: Reilly>, <Publisher: Apress>, <Publisher: Reilly>]>

选择对象
Publisher.objects.all()
objects是模型的属性,它被称为管理器。所有的模型都自动拥有一个管理器。all()方法实际上是一个
QuerySet对象,这个对象是数据库中一些记录的集合。所有数据库查询都遵循一个通用模式。
数据过滤
使用‘filter()’方法进行过滤:

>>> p=Publisher.objects.filter(name='Apress')
>>> p
<QuerySet [<Publisher: Apress>, <Publisher: Apress>]>

#符合条件的第一个对象
>>> p=Publisher.objects.filter(name='Apress').first()
>>> p
<Publisher: Apress>

#符合条件对象的个数
>>> p=Publisher.objects.filter(name='Apress').count()
>>> p
2

获取单个对象
filter()方法获取的是一个记录集,是一个列表。get()方法则会返回单个对象,如果返回多个或者对象不存在则会抛出异常。
数据排序

>>> Publisher.objects.order_by('name')
<QuerySet [<Publisher: Apress>, <Publisher: Apress>, <Publisher: Reilly>, <Publisher: Reilly>]>

逆向排序
给要排序的关键字前面加上‘-’

>>> Publisher.objects.order_by('-name')
<QuerySet [<Publisher: Reilly>, <Publisher: Reilly>, <Publisher: Apress>, <Publisher: Apress>]>

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: Reilly>
>>> Publisher.objects.order_by('-name')[0:2]
<QuerySet [<Publisher: Reilly>, <Publisher: Reilly>]>
>>> 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()
<QuerySet [<Publisher: Apress Publish>, <Publisher: Reilly>, <Publisher: Apress Publish>, <Publisher: Reilly>]>

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
<Publisher: Apress>
>>> b.publisher.website
'http://www.apress.com/'

对于用“Foreigney”来定义的关系来说,在关系另一端也能反向的追溯回来。比如通过publisher对象获取books。

>>> from books.models import *
>>> p=Publisher.objects.get(name='Reilly')
>>> p.book_set.all()
<QuerySet [<Book: python>]>

book_set只是一个查询集合(QuerySet),所以它想Queryset一样能实现数据过滤和分切。

访问多对多值(Many-to-Many Values)
多对多和外键工作方式相同,只不过我们处理的是Queryset而不是模型。

>>> from books.models import *
>>> b=Book.objects.get(id=3)
>>> b.authors.all()
<QuerySet [<Author: M sk>]>
>>> b.authors.filter(first_name='M')
<QuerySet [<Author: M sk>]>
>>> b.authors.filter(first_name='Q')
<QuerySet []>

反向查询,

>>> a=Author.objects.get(first_name='M',last_name='sk')
>>> a.book_set.all()
<QuerySet [<Book: java>, <Book: C++>, <Book: Go>]>

更改数据库模式

  • 在模型中更改字段
  • 执行
python manage.py makemigrations

生成更改的补丁文件

  • 将更改的内容记录到数据库
python manage.py  migrate

Managers

模块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<datetime.date(1945,8,1):
            return "Pre-boomer"

        return "Post-boomer"
>>> 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()来返回。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值