Django学习笔记-第一个应用程序

Django学习笔记-第一个应用程序

Project和APP的区别:
  • 一个是配置,一个是代码;

  • 一个project包含很多个Django app以及对它们的配置;

  • 技术上,project的作用是提供配置文件,比方说哪里定义数据库连接信息,安装的app列表,TEMPLATE_DIRS,等;

  • 一个app是一套Django功能的集合,通常包括模型和视图,按python的包结构的方式存在;

  • Django本身内建有一些app,eg:注释系统、自动管理界面,app的一个关键点是它们很容易移植到其他project和被多个project复用。

总的来说,就是对于如何架构Django代码并没有快速成套的规则。 如果你只是建造一个简单的Web站点,那么可能你只需要一个app就可以了; 但如果是一个包含许多不相关的模块的复杂的网站,例如电子商务和社区之类的站点,那么你可能需要把这些模块划分成不同的app,以便以后复用。

注意:
Django系统对app有一个约定:如果你使用了Django的数据库层(模型),就必须创建一个Django app,模型必须存放在apps中。

开始本例:

在’mysite’项目文件下输入下面的命令来创建’books’ app:
python manage.py startapp books

在Python代码里定义模型:

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

第一个模型:

数据库结构:书籍/作者/出版商
假定:

  • 一个作者有姓,有名及Email地址;

  • 出版商有名称,地址,所在城市,省,国家,网站;

  • 书籍有书名和出版日期;

  • 书籍有一个或多个作者(和作者是多对多的关联关系[many-to-many]),只有一个出版商(和出版商是一对多的关联关系[one to many],也被称作外键[foreign key]);

第一步,用python代码来描述它们,在models.py添加如下代码:

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.ManyToMany(Author)
    publisher = models.ForeignKey(Publisher)
    publication_date = models.DateField()

注意:

每个模型都相当于单个数据库表,每个属性也是这个表中的一个字段。属性名就是字段名,它的类型(eg:CharFiled)相当于数据库的字典类型(eg:varchar)。

以Publisher模块为例,它等同于下面这张表(用SQL语句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
);

注意:

  1. 每个数据表对应一个类,这条规则的例外情况是多对多关系。eg:在本例中,Book有一个多对多字段叫做authors。该字段表明一本书籍有一个或多个作者,但Book数据库表却并没有authors字段,相反,Django创建了一个额外的表(多对多连接表)来处理书籍和作者之间的映射关系。

  2. 本例中并没有显式地为这些模型定义任何主键,除非你单独指明,否则Django会自动为每个模型生成一个自增长的整数主键字段每个Django模型都要求有单独的主键。

  3. 在INSTALLED_APPS的末尾也需添加一个逗号,因为这是个单元素的的元组,每一个tuple元素后面加一个逗号,不管它是不是只有一个元素,这样是为了避免忘加逗号。

  4. 在ISNTALLED_APPS中的每个app都使用Python的路径描述,包的路径,用小数点“.”间隔。

接下来,就可以创建数据库表了,首先,用如下命令对模型有效性进行验证:

python manage.py validate

但是在新版的Django里,则是使用如下命令进行验证:

python manage.py check

如果有错误,错误输出会给出非常有用的搓洗信息来帮助修正模型。

如果没有错误,系统会返回:

system check identified no issues (0 silenced)
系统检查没有错误(0沉默)

模型确认无误,运行下面的命令来生成CREATE TABLE语句:

python manage.py makemigrations books
Migrations for 'books':
  0001_initial.py:
    - Create model Author
    - Create model Book
    - Create model Publisher
    - Add field publisher to book
python manage.py sqlmigrate books 0001
BEGIN;
--
-- Create model Author
--
CREATE TABLE `books_author` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `first_name` varchar(30) NOT NULL, `last_name` varchar(40) NOT NULL, `email` varchar(254) NOT NULL);
--
-- Create model Book
--
CREATE TABLE `books_book` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `title` varchar(100) NOT NULL, `publication_date` date NOT NULL);
CREATE TABLE `books_book_authors` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `book_id` integer NOT NULL, `author_id` integer NOT NULL);
--
-- Create model Publisher
--
CREATE TABLE `books_publisher` (`id` integer AUTO_INCREMENT 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);
--
-- Add field publisher to book
--
ALTER TABLE `books_book` ADD COLUMN `publisher_id` integer NOT NULL;
ALTER TABLE `books_book` ALTER COLUMN `publisher_id` DROP DEFAULT;
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`);
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`);
ALTER TABLE `books_book_authors` ADD CONSTRAINT `books_book_authors_book_id_8714badb_uniq` UNIQUE (`book_id`, `author_id`);
CREATE INDEX `books_book_2604cbea` ON `books_book` (`publisher_id`);
ALTER TABLE `books_book` ADD CONSTRAINT `books_book_publisher_id_189e6c56_fk_books_publisher_id` FOREIGN KEY (`publisher_id`) REFERENCES `books_publisher` (`id`);

COMMIT;

注意:

上述两条命令:python manage.py makemigrations books、python manage.py sqlmigrate books 0001,并没有在数据库中真正创建数据表,只是把SQL语句段打印出来,这样是为了方便看到Django会做什么,如果需要创建数据表,可以把SQL语句复制到数据库客户端执行,不过Django提供了一种更为简易的提交SQL语句至数据库的方法:python manage.py migrate

该命令仅在1.9版本之后存在,之前是使用syncdb命令,不再适用新版本了,执行上述命令后,会看到如下内容:

python manage.py migrate
Operations to perform:
  Apply all migrations: admin, contenttypes, books, auth, sessions
Running migrations:
  Rendering model states... DONE
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying books.0001_initial... OK
  Applying sessions.0001_initial... OK

再次运行该命令,什么也没发生,因为你没有添加新的模型或者添加新的app,所以再次运行该命令也是安全的,因为它不会重复执行SQL语句。

运行python manage.py dbshell将直接进入SQL客户端,本例使用的是MySQL所以运行后,会直接进入到mysql的客户端。

基本数据访问

一旦创建了模型,Django自动为这些模型提供了高级的Python API,运行python manage.py shell并输入如下内容:

python manage.py shell
Python 2.7.10 (default, Oct 14 2015, 16:09:02) 
[GCC 5.2.1 20151010] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> 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
[<Publisher: Publisher object>, <Publisher: Publisher object>]
>>> 

上述代码做的工作:

  • 首先,导入Publisher模型类,通过这个类我们可以与包含出版社的数据表进行交互;

  • 接着,创建一个Publisher类的实例并设置了字段”name、address”等的值;

  • 调用该对象的save()方法,将对象保存到数据库中,Django会在后台执行一条INSERT语句;

  • 最后,使用”Publisher.objects”属性从数据库获取出出版商信息,这个属性有很多方法,其中”Publisher.objects.all()”方法用于获取数据库中”Publisher”类的所有对象,这个操作的幕后,Django会在后台执行一条SELECT语句。

如果需要一步完成对象的创建与存储至数据库,就使用”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/')
>>> 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
添加模块的字符串表现

当我们打印Publisher的时候,并没有得到想要的信息,解决这个问题很简单,只需要为Publisher对象添加一个方法__unicode__(),该方法会告诉Python如何将对象以unicode的方式显示出来,修改books下的models.py:

from __future__ import unicode_literals

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 __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)
    publication_date = models.DateField()

    def __unicode__(self):
        return self.title

Publisher和Book对象的__unicode__()方法简单地返回各自的名称和标题,Author对象的__unicode__()方法则是将first_name和last_name字段值以空格连接后再返回。

如果__unicode__()方法返回的并不是unicode对象而是一个整型数字,则会抛出”TypeError”并提示:“coercing to Unicode: need string or buffer, int found”。

注意:

Django在其内部的各个方面都使用到了Unicode对象。模型对象中,检索匹配方面的操作使用的是Unicode对象,视图函数之间的交互使用的是Unicode对象,模板的渲染也是用的Unicode对象。 通常,我们不必担心编码是否正确,后台会处理的很好。

再次验证修改后的models的效果:

root@blackromantic:/home/zxz/djcode/mysite# python manage.py shell
Python 2.7.10 (default, Oct 14 2015, 16:09:02) 
[GCC 5.2.1 20151010] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from books.models import Publisher
>>> publisher_list = Publisher.objects.all()
>>> publisher_list
[<Publisher: Apress>, <Publisher: O'Reilly>]

最后,__unicode__()也是一个很好的例子来演示我们怎么添加行为到模型里,Django的模型不只是为对象定义了数据库表的结构,还定义了对象的行为。

插入和更新数据

直接利用Publisher来更新全部数据,在调用save()方法后,就保存至数据库,相当于SQL里的INSERT语句;

直接利用Publisher来更新某一条数据,在调用save()方法后,就保存至数据库,相当于SQL里的UPDATE语句。

选择对象

创建新的数据库,并更新之中的数据是很必要的,但是对于web应用程序来说,更多的时候是在检索查询数据库。

例如,我们已经知道如何从一个给定的模型中取出所有记录:

>>> Publisher.objects.all()
[<Publisher: Apress>, <Publisher: O'Reilly>]

这相当于SQL语句:

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

Publisher.objects.all():
- 首先,有一个已定义的模型Publisher,这是必须的,要查找数据,就需要模型来获得数据;
- 其次,是objects属性,它被称为管理器,管理器管理者所有针对数据包含、还有最重要的数据查询的表格级操作,所有的模型都自动拥有一个objects管理器,在想要查找数据时使用它;
- 最后,是all()方法,这个方法返回数据库中所有的记录,尽管这个对象看起来像一个列表,它实际上是一个QuerySet对象,这个对象是数据库中一些记录的集合。

数据过滤

通常我们只针对一部分数据进行操作,在DjangoAPI中,使用”filter()”方法对数据进行过滤:

>>> from books.models import Publisher
>>> Publisher.objects.filter(name='Apress')
[<Publisher: Apress>]
>>> 

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: Apress>]

转换成SQL语句就是:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE country = 'U.S.A.'
AND state_province = 'CA';

SQL缺省的=操作符是精确匹配的,其他类型的查找也可以使用:

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

name和contains之间有双下划线,Django也使用双下划线来表明进行一些特殊操作,这里的contains部分会被Django翻译成SQL的LIKE语句:

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

其他类型还有:

  • icontains:大小写无关的LIKE

  • startwith、endwith、range:相当于SQL的BETWEEN查询

获取单个对象

如果我们要获取单个的对象,可以使用”get()”方法:

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

这样返回的就是单个对象,而不是列表(更准确的说,QuerySet)。
如果返回的不是单个对象,而是多个对象,就会抛出异常:

>>> Publisher.objects.get(country="U.S.A")
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/manager.py", line 122, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 391, in get
    (self.model._meta.object_name, num)
MultipleObjectsReturned: get() returned more than one Publisher -- it returned 2!
>>> 

如果查询的结果并不存在,也会抛出异常:

>>> Publisher.objects.get(name="Penguin")
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/manager.py", line 122, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 387, in get
    self.model._meta.object_name
DoesNotExist: Publisher matching query does not exist.
>>> 

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

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

在Django应用中,如果希望根据某字段的值对检索结果拍学,比如按字母顺序,可以使用”order_by()”方法:

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

相当于SQL语句:

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

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

>>> Publisher.objects.order_by("state_province", "address")
[<Publisher: Apress>, <Publisher: O'Reilly>]

还可以指定逆向排序,只需要再前面加一个减号前缀:

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

虽然这种方式很灵活但是每次都要用”order_by()”显得有点啰嗦,大多数时间只会对某些字段进行排序,在这种情况下,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']

class Meta内嵌于Publisher这个类的定义中,可以在任意一个模型类中使用Meta类,来设置一些与特点模型相关的选项,如果你设置了这个选项,那么除非你检索时特意额外地使用了order_by(),否则,当你使用Django的数据库API去检索时,Publisher对象的相关返回默认地都会按name字段排序。

连锁查询

如果同时进行过滤和排序查询的操作,可以简单地写成这种“链式”的形式:

>>> Publisher.objects.filter(country='U.S.A').order_by("-name")
[<Publisher: O'Reilly>, <Publisher: Apress>] 

转换成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;
限制返回的数据

取出固定数目的记录,比如只想显示第一个,可以使用标准的Python列表裁剪语句:

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

相当于SQL语句:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher
ORDER BY name
LIMIT 1;

同理,还可以使用Python的range-slicing语法来取出数据的特定子集:

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

相当于SQL语句:

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

但是不支持Python的负索引negative slicing:

>>> Publisher.objects.order_by('name')[‐1]
Traceback (most recent call last):
...
AssertionError: Negative indexing is not supported.

但是可以使用其他的方法,比如,稍微修改order_by()语句:

>>> Publisher.objects.order_by('‐name')[0]
更新多个对象

save()方法会更新一行里的所有列,但是如果我们想更新一行里的某几列,比如将Apress Publisher的名称由原来的“Apress”更改为“Apress Publishing”,可以调用结果集(QuerySet)对象的update()方法:

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

注意:这里假设Apress的ID为52
相当于SQL语句:

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

update()方法对于任何结果集(QuerySet)均有效,这意味着可以同时更新多条记录,例如将所有的Publisher的country字段值由’U.S.A’更改为’USA’:

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

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

删除对象

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

>>> p = Publisher.objects.get(name="O'Reilly")
>>> p.delete()
>>> Publisher.objects.all()
[<Publisher: Apress Publishing>]

同样可以在结果集上调用delete()方法同时删除多条记录:

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

删除数据时要谨慎,为了预防误删除掉某一个表内的所有数据,Django要求在删除表内所有数据时显示使用all():

>>> Publisher.objects.delete()
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'Manager' object has no attribute 'delete'

一旦使用all()方法,所有数据将会被删除,切记!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值