django是一个开源web框架,学习起来里面细节很多。
因为我现在看的项目catmaid 5d就是基于django这个框架开发的。现在catmaid 5d资料不全,而且代码有各种bug。就需要回过头从catmaid 5d源码入手。在调试的过程中,经常会进入到django的源码里。所有就很有必要学习django的很多特性。熟练运用django,理解django工作的流程。
我这里用的数据库是PostgreSQL 9.5
设置日期型和数字型字段可选
django模型代码,对应为SQL里的CREATE TABLE语句
但现在,我们的模块类开始成为一个富含Author对象属性和行为的集合了。
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField(blank=True, verbose_name='e-mail') #blank=True allows email to be null
# Add the string representation of the model
def __unicode__(self):
return u'%s %s' % (self.first_name, self.last_name)
上述python代码对应的SQL语句为:
CREATE TABLE "books_author" (
"id" serial NOT NULL PRIMARY KEY,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(40) NOT NULL,
"email" varchar(75) NOT NULL
)
;
在这里,为了消除歧义,Django生成CREATE TABLE语句自动为每个字段显式加上NOT NULL。
在大多数情况下,这种默认的行为对你的应用程序来说是最佳的,因为它可以使你不再因数据一致性而头痛。 而且它可以和Django的其它部分工作得很好。如在管理工具中,如果你留空一个字符型字段,它会为此插入一个空字符串(而不是NULL)。
重点来了:
但是,其它数据类型有例外:日期型、时间型和数字型字段不接受空字符串。 如果你尝试将一个空字符串插入日期型或整数型字段,你可能会得到数据库返回的错误,这取决于那个数据库的类型。 (PostgreSQL比较严禁,会抛出一个异常;MySQL可能会也可能不会接受,这取决于你使用的版本和运气了。)在这种情况下,NULL是唯一指定空值的方法。 在Django模块中,你可以通过添加null=True来指定一个字段允许为NULL。
因此,这说起来有点复杂: 如果你想允许一个日期型(DateField、TimeField、DateTimeField)或数字型(IntegerField、DecimalField、FloatField)字段为空,你需要使用null=True 和 blank=True。
接下来看代码就会明白了:
python模型
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField(blank=True)
# Add the string representation of the module
def __unicode__(self):
return self.title
注意上面的:
publication_date = models.DateField(blank=True)
这是一个日期型属性,也同时表示Book数据表的一个列
下面是对应PostgreSQL的SQL代码:
在django中,用命令
python manage.py sqlall books
其中books是app的名称
这个命令就可以输出books这个app里所有模型的SQL语句
对应的SQL语句:
CREATE TABLE "books_book" (
"id" serial NOT NULL PRIMARY KEY,
"title" varchar(100) NOT NULL,
"publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id") DEFERRABLE INITIALLY DEFERRED,
"publication_date" date NOT NULL
)
;
注意这句:
“publication_date” date NOT NULL
也就是默认创建的CREATE TABLE里的列都是NOT NULL
即使是:
publication_date = models.DateField(blank=True)
这样加了blank=True
再看对比:
python模型代码:
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField(blank=True, null=True)
# Add the string representation of the module
def __unicode__(self):
return self.title
对应的SQL语句:
CREATE TABLE "books_book" (
"id" serial NOT NULL PRIMARY KEY,
"title" varchar(100) NOT NULL,
"publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id") DEFERRABLE INITIALLY DEFERRED,
"publication_date" date
)
;
这时注意:
“publication_date” date
没有NOT NULL这个约束了
因此,结论就是:
就是在python代码加上
publication_date = models.DateField(blank=True, null=True)
null=True
就是在创建SQL语句时,去掉NOT NULL这个约束
而blank=True是在admin中,手动给一个表添加一条记录时,如果有一个属性是blank=True,那么当不填该属性时,django会自动给这个列填上空字符串。
另外需要注意的是:
添加null=True比添加blank=True复杂。因为null=True改变了数据的语义,即改变了CREATE TABLE语句,把publication_date字段上的NOT NULL删除了。 要完成这些改动,我们还需要更新数据库。3
出于某种原因,Django不会尝试自动更新数据库结构。所以你必须执行ALTER TABLE语句将模块的改动更新至数据库。 像先前那样,你可以使用manage.py dbshell进入数据库服务环境。 以下是在这个特殊情况下如何删除NOT NULL:
ALTER TABLE books_book ALTER COLUMN publication_date DROP NOT NULL;
(注意:上面SQL语法是PostgreSQL特有的。)
自定义字段标签
首先说一下,什么是字段标签。初看名字不知道具体指代什么含义。坑爹呀
看个图吧:
在上图中,Change author中的author就是数据表名称。这些就明白了,这是数据表author中的一条记录。
红框圈中的就是author数据表的一个列了。
First name对应列名称。
这里的First name, Last name, E-mail就表示字段标签
所以,字段标签就是对应数据库表里的列。
自定义就是说可以自己对字段标签重新个起名字而已。
首先说一下,django默认生成字段标签的规则:
在编辑页面中,每个字段的标签都是从模块的字段名称生成的。 规则很简单:
用空格替换下划线;首字母大写。例如:Book模块中publication_date的标签是Publication date。
So,既然已经有了默认的生成标签的规则,为什么还要自定义呢??
我猜,可能是应为有时候用默认的规则生成的标签太丑了→_→
下面是文档里的原话:
然而,字段名称并不总是贴切的。有些情况下,你可能想自定义一个标签。 你只需在模块中指定verbose_name。
举个例子,说明如何将Author.email的标签改为e-mail,中间有个横线。
增加verbose_name=’e-mail’
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField(blank=True, verbose_name='e-mail') #blank=True allows email to be null
# Add the string representation of the model
def __unicode__(self):
return u'%s %s' % (self.first_name, self.last_name)
这时,就变成了下面这个样子:
请注意,你不必把verbose_name的首字母大写,除非是连续大写(如:”USA state”)。Django会自动适时将首字母大写,并且在其它不需要大写的地方使用verbose_name的精确值。
最后还需注意的是,为了使语法简洁,你可以把它当作固定位置的参数传递。 这个例子与上面那个的效果相同。
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField('e-mail', blank=True)
但这不适用于ManyToManyField 和ForeignKey字段,因为它们第一个参数必须是模块类。 那种情形,必须显式使用verbose_name这个参数名称。
比如下面的代码中,
authors = models.ManyToManyField(Author)
第一个参数是模块类Author
如果要自定义字段标签就需要显式使用verbose_name
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField(blank=True, null=True)
# Add the string representation of the module
def __unicode__(self):
return self.title
自定义ModelAdmin类
迄今为止,我们做的blank=True、null=True和verbose_name修改其实是模块级别,而不是管理级别的。 也就是说,这些修改实质上是构成模块的一部分,并且正好被管理工具使用,而不是专门针对管理工具的。
除了这些,Django还提供了大量选项让你针对特别的模块自定义管理工具。 这些选项都在ModelAdmin classes里面,这些类包含了管理工具中针对特别模块的配置。
自定义列表
让我们更深一步:自定义Author模块的列表中的显示字段。 列表默认地显示查询结果中对象的__unicode__()。 在第五章中,我们定义Author对象的__unicode__()方法,用以同时显示作者的姓和名。
books/models.py里的class Author
books/admin.py里注册Author模块
在admin中显示效果
即 列表默认地显示查询结果中对象的__unicode__()。 在第五章中,我们定义Author对象的__unicode__()方法,用以同时显示作者的姓和名。
再看看books这个app的admin的效果
对应的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()
#Add the string representation of the module
#__unicode__() method tells Python how to display the object in unicode mode
def __unicode__(self):
return self.name
#Specifies the default ordering of the model objects
class Meta:
ordering = ['name']
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField(blank=True, verbose_name='e-mail') #blank=True allows email to be null
# Add the string representation of the model
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(blank=True, null=True)
# Add the string representation of the module
def __unicode__(self):
return self.title
可以看到admin管理工具自动给每个模块名称加了s, 如Author ->Authors
并且给模块首字母改为大写。
看下面的例子就明白了
所对应的books/models.py代码
class aaaaauthor代码
class aaaaauthor(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField(blank=True, verbose_name='e-mail') #blank=True allows email to be null
# Add the string representation of the model
def __unicode__(self):
return u'%s %s' % (self.first_name, self.last_name)
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()
#Add the string representation of the module
#__unicode__() method tells Python how to display the object in unicode mode
def __unicode__(self):
return self.name
#Specifies the default ordering of the model objects
class Meta:
ordering = ['name']
class aaaaauthor(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField(blank=True, verbose_name='e-mail') #blank=True allows email to be null
# Add the string representation of the model
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(aaaaauthor)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField(blank=True, null=True)
# Add the string representation of the module
def __unicode__(self):
return self.title
现在回到自定义列表这个正题:
我们可以在这基础上改进,添加其它字段,从而改变列表的显示。 这个页面应该提供便利,比如说:在这个列表中可以看到作者的邮箱地址。如果能按照姓氏或名字来排序,那就更好了。
为了达到这个目的,我们将为Author模块定义一个ModelAdmin类。 这个类是自定义管理工具的关键,其中最基本的一件事情是允许你指定列表中的字段。 打开admin.py并修改:
from django.contrib import admin
from mysite.books.models import Publisher, Author, Book
**class AuthorAdmin(admin.ModelAdmin):**
**list_display = ('first_name', 'last_name', 'email')**
**search_fields = ('first_name', 'last_name')**
admin.site.register(Publisher)
**admin.site.register(Author, AuthorAdmin)**
admin.site.register(Book)
接下来对应的内容在参考书里说的比较详细,在这里就不重复了。
其中**引起来的部分是修改的内容
修改前:
修改后:
弄好了这个东东,再刷新author列表页面,你会看到列表中有三列:姓氏、名字和邮箱地址。 另外,点击每个列的列头可以对那列进行排序。
接下来,让我们为Book列表页添加一些过滤器。
添加的代码:
list_filter = (‘publication_date’,)
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publication_date',)
date_hierarchy = 'publication_date'
ordering = ('-publication_date',)
fields = ('title', 'authors', 'publisher')
另外一种过滤日期的方式是使用date_hierarchy选项,如:
date_hierarchy = ‘publication_date’
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publication_date',)
**date_hierarchy = 'publication_date'**
请注意,date_hierarchy接受的是* 字符串* ,而不是元组。因为只能对一个日期型字段进行层次划分。