Python网络编程05----django与数据库的交互

介绍

Django为多种数据库后台提供了统一的调用API,在Django的帮助下,我们不用直接编写SQL语句。Django将关系型的表(table)转换成为一个类(class)。而每个记录(record)是该类下的一个对象(object)。我们可以使用基于对象的方法,来操纵关系型数据库。


设置数据库

设置数据库需要修改 settings.py文件
如果使用的数据库是mysql:
[python]  view plain copy
  1. DATABASES = {  
  2.     'default': {  
  3.         'ENGINE' :  "django.db.backends.mysql",  
  4.         'NAME' : "djangodb",  
  5.         'HOST' : "localhost",  
  6.         'USER' : "root",  
  7.         'PASSWORD' : "123456",  
  8.     }  
  9. }  
PORT如果不设置,会使用默认端口3306
如果没有安装mysql,你也可以使用sqlite。SQLite非常适合测试,甚至可以部署在没有大量并发写入的情况下。
由于SQLite使用的本地文件系统作为存储介质并且用原生的文件系统权限来做访问控制,像主机、端口、用户、密码这种信息统统不需要
只需要输入如下信息:
[python]  view plain copy
  1. DATABASES = {  
  2.     'default': {  
  3.         'NAME': r'C:\mysite\db\test.db',              #db目录需要自己创建  
  4.         'ENGINE''django.db.backends.sqlite3',  
  5.     }  
  6. }              

设计Model

在传统的关系型数据库中,数据模型是表。在Django下,一个表为一个类。表的每一列是该类的一个属性。在models.py中,我们创建一个只有一列的表,即只有一个属性的类:
from django.db import models

class Test(models.Model):                         #用于定义数据模型的类
    name = models.CharField(max_length=100)       #name列,数据类型为char,长度为100
    def __str__(self):                            #2.*使用__unicode__(self)
        return self.name
命令Django同步数据库。Django根据models.py中描述的数据模型,在MySQL中真正的创建各个关系表:
$python manage.py syncdb         //需要注意的是,syncdb并不会修改已经存在的数据库,他仅仅保证模型类都有对应的表

Python网络编程05----django与数据库的交互_第1张图片

设置View

事先向数据库中插入两条数据

下面我们从数据库中取出数据,并返回给http请求。在blog/views.py中,添加视图。对于对应的请求,我们将从数据库中读取所有的记录,然后返回给客户端:

# -*- coding: utf-8 -*-
from django.http import HttpResponse

from blog.models import Test

def outStr(request):
    blog_list = Test.objects.all()
    blog_str  = map(str, blog_list)
    return HttpResponse("<p>" + ' '.join(blog_str) + "</p>")

为了让http请求能找到上面的程序,在blog/urls.py增加url链接:
from django.conf.urls import patterns, include, url

urlpatterns = patterns('',
    # Examples:
    # url(r'^$', 'myweb.views.home', name='home'),
    url(r'^$', 'blog.views.outStr'),
)

输入python manage.py runserver运行服务器
访问127.0.0.1:8000/blog
Python网络编程05----django与数据库的交互_第2张图片

知识点

model字段类型清单

AutoField:一个自动递增的整型字段,添加记录时它会自动增长。你通常不需要直接使用这个字段;如果你不指定主键的话,系统会自动添加一个主键字段到你的model。(参阅自动主键字段)
BooleanField:布尔字段,管理工具里会自动将其描述为checkbox。
CharField:字符串字段,单行输入,用于较短的字符串,如要保存大量文本, 使用 TextField,CharField有一个必填参数:
                   CharField.max_length:字符的最大长度,django会根据这个参数在数据库层和校验层限制该字段所允许的最大字符数。
TextField:一个容量很大的文本字段, admin 管理界面用 <textarea>多行编辑框表示该字段数据。
CommaSeparatedIntegerField:用于存放逗号分隔的整数值。类似 CharField,必须maxlength 参数。
DateField:日期字段,admin 用一个文本框 <input type=”text”> 来表示该字段数据(附带一个 JavaScript 日历和一个”Today”快捷按键。有下列额外的可选参数:
                  auto_now:当对象被保存时,自动将该字段的值设置为当前时间.通常用于表示 “last-modified” 时间戳;
                  auto_now_add:当对象首次被创建时,自动将该字段的值设置为当前时间.通常用于表示对象创建时间。
DateTimeField:类似 DateField 支持同样的附加选项。
EmailField:一个带有检查 Email 合法性的 CharField,不接受 maxlength 参数。
FileField:一个文件上传字段。 要求一个必须有的参数: upload_to, 一个用于保存上载文件的本地文件系统路径。 这个路径必须包含 strftime formatting, 该格式将被上载文件的 date/time 替换(so that uploaded files don’t fill up the given directory)。在一个 model 中使用 FileField 或 ImageField 需要以下步骤:在你的 settings 文件中, 定义一个完整路径给 MEDIA_ROOT 以便让 Django在此处保存上传文件。 (出于性能考虑,这些文件并不保存到数据库。) 定义 MEDIA_URL 作为该目录的公共 URL。 要确保该目录对 WEB 服务器用户帐号是可写的。在你的 model 中添加 FileField 或 ImageField, 并确保定义了 upload_to 选项,以告诉 Django 使用 MEDIA_ROOT 的哪个子目录保存上传文件。你的数据库中要保存的只是文件的路径(相对于 MEDIA_ROOT)。 出于习惯你一定很想使用 Django 提供的 get_<fieldname>_url 函数。举例来说,如果你的 ImageField 叫作 mug_shot, 你就可以在模板中以 {{ object。get_mug_shot_url }} 这样的方式得到图像的绝对路径。
FilePathField:选择指定目录按限制规则选择文件,有三个参数可选, 其中”path”必需的,这三个参数可以同时使用, 参数描述:
                          path:必需参数,一个目录的绝对文件系统路径。 FilePathField 据此得到可选项目。 Example: “/home/images”;
                          match:可选参数, 一个正则表达式, 作为一个字符串, FilePathField 将使用它过滤文件名。 注意这个正则表达式只会应用到 base filename
                                         而不是路径全名。 Example: “foo。*\。txt^”, 将匹配文件 foo23.txt 却不匹配 bar.txt 或 foo23.gif;
                          recursive:可选参数, 是否包括 path 下全部子目录,True 或 False,默认值为 False。
FloatField:浮点型字段。 必须提供两个 参数, 参数描述:
                     max_digits:总位数(不包括小数点和符号)
                     decimal_places:小数位数。如:要保存最大值为 999 (小数点后保存2位),你要这样定义字段:models.FloatField(…,max_digits=5,
                                                      decimal_places=2),要保存最大值一百万(小数点后保存10位)的话,你要这样定义:
                                                      models.FloatField(…,max_digits=19, decimal_places=10)
ImageField:类似 FileField, 不过要校验上传对象是否是一个合法图片。它有两个可选参数:height_field 和 width_field,如果提供这两个参数,则图片将按提供的高度和宽度规格保存。 该字段要求 Python Imaging 库。
IntegerField:用于保存一个整数。
IPAddressField:一个字符串形式的 IP 地址, (如 “202.1241.30″)。
NullBooleanField:类似 BooleanField, 不过允许 NULL 作为其中一个选项。 推荐使用这个字段而不要用 BooleanField 加 null=True 选项。 admin 用一个选择框 <select> (三个可选择的值: “Unknown”, “Yes” 和 “No” ) 来表示这种字段数据。
PositiveIntegerField:类似 IntegerField, 但取值范围为非负整数(这个字段应该是允许0值的…可以理解为无符号整数)
SlugField:是一个报纸术语. slug 是某个东西的小小标记(短签), 只包含字母,数字,下划线和连字符.它们通常用于URLs。
SmallIntegerField:类似 IntegerField, 不过只允许某个取值范围内的整数。(依赖数据库)
TimeField:时间字段,类似于 DateField 和 DateTimeField。
URLField:用于保存 URL。 若 verify_exists 参数为 True (默认), 给定的 URL 会预先检查是否存在(即URL是否被有效装入且没有返回404响应)。
XMLField:XML字符字段,校验值是否为合法XML的 TextField,必须提供参数:
                     schema_path:校验文本的 RelaxNG schema 的文件系统路径。

指定主键与外键:

默认AutoField对象id为主键如果你想要有更多的控制主键,只需要在模型的某个变量上指定primary_key=True就行。这个变量会取代id成为这张表的主键。
unique=True也能保证变量的唯一性,但它并不是主键。


对于外键看下面的例子
from django.db import models

class Author(models.Model):
    name=models.CharField(max_length=100)

class Book(models.Model):
    title=models.CharField(max_length=100)
    author=models.ForeignKey(Author)
值得注意的是如果不把引用的类定义放在前面,就要写成author=models.ForeignKey(‘Author’)。而且以上都两个类在同一个文件中定义的情况,如果不在同一个类的话就要写成(‘blog.Author’)

但是上面的代码还有问题,要知道一本书可以对应多个作者,一个作者可以对应多本书,这是一个多对多的关系。如果像上面这样构建数据库,books_Book表就会出现大量重复的行。这时我们就需要用ManyToManyField来帮忙了。
from django.db import models
class Author(models.Model):
    name=models.CharField(max_length=100)

class Book(models.Model):
    title=models.CharField(max_length=100)
    author=models.ManyToManyField(Author)
对应的sql语句为
Python网络编程05----django与数据库的交互_第3张图片

也就是说创建了三个表。
这比你自己构建三个类来创建3个表方便多了,比如你想知道一本书的所有作者和一个作者的所有书,代码就下
book.authors.all()  
author.book_set.all() 

Model的继承

Django 中的 model 继承和 Python 中的类继承非常相似,只不过你要选择具体的实现方式:让父 model 拥有独立的数据库还是让父 model 只包含基本的公共信息,而这些信息只能由子 model 呈现。

Django中有三种继承关系:
1.通常,你只是想用父 model 来保存那些你不想在子 model 中重复录入的信息。父类是不使用的也就是不生成单独的数据表,这种情况下使用 抽象基类继承 Abstract base classes。
2.如果你想从现有的Model继承并让每个Model都有自己的数据表,那么使用 多重表继承Multi-table inheritance。
3.最后,如果你只想在 model 中修改 Python-level 级的行为,而不涉及字段改变。 代理 model(Proxy models) 适用于这种场合。

抽象类继承(Abstract base classes)

如果你想把某些公共信息添加到很多 model 中,抽象基类就显得非常有用。你编写完基类之后,在 Meta 内嵌类中设置 abstract=True  ,该类就不能创建任何数据表。然而如果将它做为其他 model 的基类,那么该类的字段就会被添加到子类中。抽象基类和子类如果含有同名字段,就会导致错误(Django 将抛出异常)。
class CommonInfo(models.Model):
  name = models.CharField(max_length=100)
  age = models.PositiveIntegerField()

  class Meta:
    abstract = True

class Student(CommonInfo):
  home_group = models.CharField(max_length=5)
相当于
CREATE TABLE "myapp_student" (
    "id" integer NOT NULL PRIMARY KEY,
    "name" varchar(100) NOT NULL,
    "age" integer unsigned NOT NULL,
    "home_group" varchar(5) NOT NULL
)
当基类中使用 related_name参数来设置Foreignkey这样的关联变量时,就需要用一些字符窜格式化来避免子类的名字发生冲突,具体方法是在属性值中包含  '%(app_label)s' 和 '%(class)s'字符串。

1.'%(class)s'会被子类的名字取代。
2.'%(app_label)s'会被子类所在的app的名字所取代。

多表继承(Multi-table inheritance)

这是 Django 支持的第二种继承方式。使用这种继承方式时,同一层级下的每个子 model 都是一个真正意义上完整的 model 。每个子 model 都有专属的数据表,都可以查询和创建数据表。继承关系在子 model 和它的每个父类之间都添加一个链接 (通过一个自动创建的 OneToOneField-------->和Foreignkey类似 来实现)。 例如:
class Place(models.Model):
  name = models.CharField(max_length=50)
  address = models.CharField(max_length=80)

class Restaurant(Place):
  serves_hot_dogs = models.BooleanField()
  serves_pizza = models.BooleanField()
相当于
BEGIN;
CREATE TABLE "myapp_place" (
  "id" integer NOT NULL PRIMARY KEY,
  "name" varchar(50) NOT NULL,
  "address" varchar(80) NOT NULL
)
;
CREATE TABLE "myapp_restaurant" (
  "place_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "myapp_place" ("id"),

  "serves_hot_dogs" bool NOT NULL,
  "serves_pizza" bool NOT NULL
)
;
Place 里面的所有字段在 Restaurant 中也是有效的,只不过数据保存在另外一张数据表当中。 所以下面两个语句都是可以运行的:
>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")

代理model (Proxy models)

使用 多表继承(multi-table inheritance) 时,model 的每个子类都会创建一张新数据表,通常情况下,这正是我们想要的操作。这是因为子类需要一个空间来存储不包含在基类中的字段数据。但有时,你可能只想更改 model 在 Python 层的行为实现。比如:更改默认的 manager ,或是添加一个新方法。
而这,正是代理 model 继承方式要做的:为原始 model 创建一个代理(proxy)。你可以创建,删除,更新代理 model 的实例,而且所有的数据都可以象使用原始 model 一样被保存。不同之处在于:你可以在代理 model 中改变默认的排序设置和默认的 manager ,更不会对原始 model 产生影响。
声明代理 model 和声明普通 model 没有什么不同。设置Meta 内置类中 proxy 的值为 True,就完成了对代理 model 的声明。
举个例子,假设你想给 Django 自带的标准 User model (它被用在你的模板中)添加一个方法:
class Person(models.Model):
  first_name = models.CharField(max_length=30)
  last_name = models.CharField(max_length=30)

class MyPerson(Person):
  class Meta:
    proxy = True

  def do_something(self):
    # ...
    pass
相当于
CREATE TABLE "myapp_person" (
    "id" integer NOT NULL PRIMARY KEY,
    "first_name" varchar(30) NOT NULL,
    "last_name" varchar(30) NOT NULL
)
;
MyPerson 类和它的父类 Person操作同一个数据表。特别的是,Person 的任何实例也可以通过 MyPerson 访问,反之亦然:
>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")
<MyPerson: foobar>
你也可以 使用代理 model 给 model 定义不同的默认排序设置。Django 自带的 User model 没有定义排序设置(这是故意为之,是因为排序开销极大,我们不想在获取用户时浪费额外资源)。你可以利用代理对 username 属性进行排序,这很简单:
class OrderedPerson(Person):
    class Meta:
        ordering = ["last_name"]
        proxy = True
普通的 User 查询,其结果是无序的;而 OrderedUser 查询的结果是按 username 排序。

值得注意的是
1. 查询集只返回请求时所使用的 model。无论你何时查询Person 对象,Django 都不会返回 MyPerson 对象。针对 Person 对象的查询集只返回 Person 对象。代理对象的精要就在于依赖原始 User 的代码仅对它自己有效,而你自己的代码就使用你扩展的内容。不管你怎么改动,都不会在查询 Person 时得到 MyPerson。
2. 代理 model 必须继承自一个非抽象基类。你不能继承自多个非抽象基类,这是因为一个代理 model 不能连接不同的数据表。代理 model 也可以继承任意多个抽象基类,但前提是它们没有定义任何 model 字段。
3.如果你没有在代理 model 中定义任何 manager ,代理 model 就会从父类中继承 manager 。如果你在代理 model 中定义了一个 manager ,它就会变成默认的 manager ,不过定义在父类中的 manager 仍是有效的。







上一讲:Python网络编程04----初识Django
下一讲: Python网络编程06----django数据库的增删改查



如果有什么疑问欢迎到我的微信公众号提问~
Python网络编程05----django与数据库的交互_第4张图片

你可能感兴趣的:(python,网络编程)