Django学习之models

1 篇文章 0 订阅
1 篇文章 0 订阅

Django学习之models

The Django Book第五章学习笔记

学习环境:
  • PyCharm 2019.2 专业版
  • Python 3.7
  • Django 2.24
一点小感想
  • 感觉Pycharm对代码的格式检查很是严格,尤其是块与块之间(比如类的定义)之间须空两行。
  • Project目录改名,直接在文件浏览器改名——这证明是错误的决定,最后我决定还是清空PycharmProjects目录下的所有文件,重新开始。不过,新建Project需要搭建新的工作平台,要下载安装很多东西,特别是Django要重新下载安装,数据库驱动也要重新下载……。

在settings.py中设置如下:

DATABASES = {
    'default': {
        # 'ENGINE': 'django.db.backends.sqlite3',
        # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        'ENGINE': 'django.db.backends.postgres_psycopg2',
        'NAME': 'test',
        'USER': 'username',
        "PASSWORD": 'password',
        "HOST": 'ip',
        'PORT': ,
    }
}

创建books app

python manage.py startapp books

出现错误:

django.core.exceptions.ImproperlyConfigured: 'django.db.backends.postgres_psycopg2' isn't an available database backend.
Try using 'django.db.backends.XXX', where XXX is one of:
    'mysql', 'oracle', 'postgresql', 'sqlite3'

没有安装psycopg2,安装一下:

pip install psycopg2

重新执行4.命令,还是同样错误,原来是

'ENGINE': 'django.db.backends.postgres_psycopg2',

设置错误,应为

'ENGINE': 'django.db.backends.postgresql_psycopg2',

当初是听了PyCharm自己的语法检查改的,原来是错的。

然后在books的models.py中输入以下代码:

from django.db import models

# Create your models here.


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()

settings.py中设置:

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

然后在终端中执行:

> python manage.py makemigrations books

显示:

(venv) C:\Users\admin\PycharmProjects\mysite>python manage.py makemigrations books
Migrations for 'books':
  books\migrations\0001_initial.py
    - Create model Author
    - Create model Publisher
    - Create model Book

通过运行makemigrations命令,可以通知Django程序已经作出改动,要求它验证通过,以便正式将我们的app编译。

现在我们来看看如果我们migrate books model,会发生些什么?执行命令:

> python manage.py sqlmigrate books 0001

显示:

BEGIN;
--
-- Create model Author
--
CREATE TABLE "books_author" ("id" serial NOT NULL PRIMARY KEY, "first_name" varchar(30) NOT NULL, "last_name" varchar(40) NOT NULL, "email" varchar(254) NOT NULL);
--
-- Create model Publisher
--
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" v
archar(30) NOT NULL, "country" varchar(50) NOT NULL, "website" varchar(200) NOT NULL);
--
-- Create model Book
--
CREATE TABLE "books_book" ("id" serial NOT NULL PRIMARY KEY, "title" varchar(100) NOT NULL, "publication_date" date NOT NULL, "publisher_id" integer NOT NULL);
CREATE TABLE "books_book_authors" ("id" serial NOT NULL PRIMARY KEY, "book_id" integer NOT NULL, "author_id" integer NOT NULL);
ALTER TABLE "books_book" ADD CONSTRAINT "books_book_publisher_id_189e6c56_fk_books_publisher_id" FOREIGN KEY ("publisher_id") REFERENCES "books_publisher" ("id") DEFERRABLE I
NITIALLY DEFERRED;
CREATE INDEX "books_book_publisher_id_189e6c56" ON "books_book" ("publisher_id");
ALTER TABLE "books_book_authors" ADD CONSTRAINT "books_book_authors_book_id_ed3433e7_fk_books_book_id" FOREIGN KEY ("book_id") REFERENCES "books_book" ("id") DEFERRABLE INITI
ALLY DEFERRED;
ALTER TABLE "books_book_authors" ADD CONSTRAINT "books_book_authors_author_id_984f1ab8_fk_books_author_id" FOREIGN KEY ("author_id") REFERENCES "books_author" ("id") DEFERRAB
LE INITIALLY DEFERRED;
ALTER TABLE "books_book_authors" ADD CONSTRAINT "books_book_authors_book_id_author_id_8714badb_uniq" UNIQUE ("book_id", "author_id");
CREATE INDEX "books_book_authors_book_id_ed3433e7" ON "books_book_authors" ("book_id");
CREATE INDEX "books_book_authors_author_id_984f1ab8" ON "books_book_authors" ("author_id");
COMMIT;

这些都是我们运行正式migrate命令后,Django要做的。总共要建4个表。

好了,我们正式开始我们的migration吧!执行:

> python manage.py migrate

数据库就这样简简单单地建立起来了。
三个步骤:

  • 在models.py中进行表的定义(class)
  • 运行 python manage.py makemigrations 来验证这些定义
  • 运行python manage.py migrate 来实施这些定义,在数据库中创建相应的表。

数据的插入与保存

好了,我们现在可以在Django(API)中访问我们的数据了。在Terminal中运行(不能在Python Console中执行):

> python manage.py shell

在终端中依次输入以下代码:

(venv) C:\Users\admin\PycharmProjects\mysite> python manage.py shell
Python 3.7.4 (tags/v3.7.4:e09359112e, Jul  8 2019, 20:34:20) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from books.models import Publisher
>>> p1 = Publisher(name='张三叶', address='上海杨浦区国定路10号', city='上海市', state_province='上海', country='中国',website='http://www.hotmail.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)>]>
>>>

这里我抄一段Django Book的的说明:

  • 首先,导入Publisher模型类, 通过这个类我们可以与包含 出版社 的数据表进行交互。
  • 接着,创建一个Publisher 类的实例并设置了字段name, address 等的值。
  • 调用该对象的 save() 方法,将对象保存到数据库中。 Django 会在后台执行一条 INSERT 语句。
  • 最后,使用Publisher.objects 属性从数据库取出出版商的信息,这个属性可以认为是包含出版商的记录集。 这个属性有许多方法, 这里先介绍调用Publisher.objects.all() 方法获取数据库中Publisher 类的所有对象。这个操作的幕后,Django执行了一条SQL SELECT 语句。

这个已经说的很清楚了。下面的命令与上面等效,是一步到位的做法,不用调用save()方法。

>>> p1 = Publisher.objects.create(name='Apress',
...     address='2855 Telegraph Avenue',
...     city='Berkeley', state_province='CA', country='U.S.A.',
...     website='http://www.apress.com/')
>>> 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/')
>>> publisher_list = Publisher.objects.all()
>>> publisher_list

数据的访问、过滤与查询

好了在数据库中,表books_publisherk中就有了两条记录,现在我们可以用以下方式来获取表中的各个字段的值:

>>> p1.name
'张三叶'
>>> p1.address
'上海杨浦区国定路10号'

上面我们在调用Publisher.objects.all()方法时,仅仅简单地返回了

<QuerySet [<Publisher: Publisher object (1)>, <Publisher: Publisher object (2)>]>

这太不直观了,我们可以通过在models.py中添加一些代码来解决这个问题,这样原来代码就变成如下样子:

from django.db import models

# Create your models here.


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):
        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 __str__(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,on_delete=models.CASCADE,)
    publication_date = models.DateField()

    def __str__(self):
        return self.title

添加了一个__str__(self)方法。这个方法在人机交互调试时,很有用的。
除了上述系统内部方法外,我们也可在models中添加自己的方法。例如在class Book(models.Model)中加入以下代码:

# 100天内出版的书,算是最近出版的。
    def was_published_recently(self):
        return self.publication_date >= timezone.now() - datetime.timedelta(days=100)

当然,为此我们要引入两个库:一个是Python的标准库datetime,专业处理时间问题,一个是django的表示时区的类timezone。代码如下:

import datetime
from django.utils import timezone

关于timezone的详细内容可以参考django.utils.timezone
好,我们保存,然后重新运行 python manage.py shell——运行前记得要先退出哦。

>python manage.py shell
>>> from books.models import Publisher

试试API新方法——数据过滤:

>>> Publisher.objects.filter(id=2)

id是关键字段
我们多参数过滤:

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

返回:

<QuerySet [<Publisher: O'Reilly>]>

还有模糊过滤

>>> Publisher.objects.filter(name__contains="press")

这个name__contains并非字段名(或子类名),其中双下划线____contains 相当于SQL语句中的 LIKE 。如果翻译成标准SQL语句,应该是:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE name LIKE '%press%';

其他的一些查找类型有:icontains(大小写无关的LIKE),startswithendswith, 还有range(SQLBETWEEN查询)。
上面的例子中filter() 函数返回一个记录集,这个记录集是一个列表。 相对列表来说,有些时候我们更需要获取单个的对象, get() 方法就是在此时使用的:

>>> from books.models import Publisher
>>> Publisher.objects.get(address="北京市")
<Publisher: 中央编译出版社>
>>>

我们还可以更复杂一些——获取今年(2019年)出版的书:

>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> from books.models import Book
>>>Book.objects.get(publication_date__year=current_year)
<Book: 古希腊神话与传说>

get()方法只能返回一个对象(一个值),如果结果超过一个,会抛出异常:

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

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

>>> Publisher.objects.get(name="Penguin")
Traceback (most recent call last):
    ...
DoesNotExist: Publisher matching query does not exist.

可以用以下方式处理异常:

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."

排序问题

  • order_by()方法
>>> Publisher.objects.order_by("name")
<QuerySet [<Publisher: O'Reilly>, <Publisher: 中央编译出版社>]>

多个字段用短号分隔。还有逆向排序:

>>> Publisher.objects.order_by("-name")
<QuerySet [<Publisher: 中央编译出版社>, <Publisher: O'Reilly>]>

当然,也可以在models.py中设定:

class Publisher(models.Model):
    name = models.CharField(max_length=30)
......

**class Meta:**
        **ordering = ['name']**

结合起来,我们可以这样做:

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

限制返回的记录数

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

指定返回数据

>>> Publisher.objects.order_by('name')
<QuerySet [<Publisher: O'Reilly>, <Publisher: 三联书店>, <Publisher: 中华书局>, <Publisher: 中央编译出版社>]>
>>> Publisher.objects.order_by('name')[3]
<Publisher: 中央编译出版社>

指定返回按顺序第4条记录。

更新多个对象

在“插入和更新数据”小节中,我们有提到模型的save()方法,这个方法会更新一行里的所有列。 而某些情况下,我们只需要更新行里的某几列。例如说我们现在想要将中华书局的名称由原来的”中华书局更改为中华书局出版社。若使用save()方法,如:

>>> p = Publisher.objects.get(name='中华书局')
>>> p.name = '中华书局出版社'
>>> p.save()

这等同于如下SQL语句:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE name = '中华书局';

UPDATE books_publisher SET
    name = '中华书局出版社',
    address = '北京市',
    city = '北京',
    state_province = '北京,
    country = '中国.',
    website = 'http://www.apress.com'
WHERE id = 4;

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

>>> Publisher.objects.filter(id=4).update(name='中华书局出版社')

这等同于

UPDATE books_publisher
SET name = '中华书局出版社'
WHERE id = 4;

update()方法对于任何结果集(QuerySet)均有效,这意味着你可以同时更新多条记录。 以下示例演示如何将所有Publisher的country字段值全部改为’中华人民共和国’:

>>> Publisher.objects.all().update(country='中华人民共和国')
4

update()方法会返回一个整型数值,表示受影响的记录条数。 在上面的例子中,这个值是4。

删除对象

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

>>> p = Publisher.objects.get(name="O'Reilly")
>>> p.delete()
>>> Publisher.objects.all()
<QuerySet [<Publisher: 中央编译出版社>, <Publisher: 三联书店>, <Publisher: 中华书局>]>

此外,我们还可以用以下方法部分,甚至清空一个表(类)中的全部数据:

>>> Publisher.objects.filter(country='USA').delete()
>>> Publisher.objects.all().delete()
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值