做web开发,编码往往以建模开始,下面以django1.5为标准,扯扯写一个地道的django model.(django1.4绝大部分可以兼容)
1.首先要对Field Options有个基本的认识(属性是面向model类而言的,字段是面向数据库而言的)
model由属性field组成,不同field类型,有不同的options,先了解所有field都公共的options。官网有比较多的options,选取常规开发能用到的几个说一说。
null选项, 表示数据库层面的检验,能否为null,默认为False,就是说数据库里这个属性对应的字段默认不能存储null
blank选项,能否为空值empty value(不是None空对象),None存到数据库就是null,这个选项与数据库无关,是一个纯django层面的检验,比如form的校验,能否允许empty value就取决于这个选项
choices选项,指定这个属性的值只能是哪几种可能,具体写法看文档https://docs.djangoproject.com/en/1.5/ref/models/fields/#django.db.models.Field.choices
db_column选项,指定数据库表里这个属性对应字段的名字,默认是属性名字,无特殊需要,不用理会。如果实际需要更改,比如使用一个已经存在的数据库,确保名字可用
db_index选项,是否为这个属性对应字段添加索引,即使有这个需要,unique可以胜任
default,不用说了,赋默认值
editable,是否可编辑,默认true,如果不可编辑,将不会再model-form和admin中的添加修改出现
error_messages,该属性用户输入不符合要求时,提示信息,默认已有,除非不满足你的要求,不用理会。
help_text,帮助信息,当用户输入填写表单时,提示用户这个该怎么输入
verbose_name,官网有个解释很好A human-readable name for the field。简略的说:属性名cat,verbose_name=U"猫",那么页面上就显示猫,不是cat
2.写好每一个属性
1.数值字符串类型:大整数使用 BigIntegerField
一般IntegerField
确定值很小 可以节约空间使用 SmallIntegerField
浮点数,高精度浮点,字符串,布尔,IP,邮箱,URL,等等分别使用其对应类型即可,另外有些带Positive修饰,检验正负,总之一句话:请使用最精确的类型。
此外CommaSeparatedIntegerField不是整数,别被名字骗了,本质还是字符串
大量文本使用TextField,个人觉得百K是上限,再大过M就不适合了
更大,那就自定义类型,指向底层数据库支持的大数据类型,这个比较简单。
2.日期时间类型:DateField,TimeField,DateTimeField
如果你要记录首次添加时间,auto_now_add=True
如果你要记录最后更新时间,auto_now=True,这两个就不要自己动脑筋实现了
但是注意QuerySet批量更新时,不是依次调用每个对象的更新函数,不会触发auto_now,批量更新其他字段时记得连带更新最后时间
3.上传文件,使用FileField相关文档太长,简要概括
upload_to指定上传目录,storage如何管理你上传的文件,默认就够用。有几个可选,看看差异即可。
但是,models类读取文件属性和普通属性有所区别:
读取文件属性返回值是一个FieldFile对象,代表你上传的文件,这个对象的url属性表示文件路径,open,close,delete,save方法用于操作文件。
此外FilePathField与文件基本无关,也别被名字骗了,本质是个字符串,只不过要求这个字符串表示的文件路径正确有效,有些选项可以控制文件或目录,不详述。
ImageField是FileField子类,只不过加了几个与图片相关的控制参数,用法一样
4.外键,比如
1.class Comment(models.Model):
target = models.ForeignKey(XXX)#XXX另一个model 类,表示对XXX的一个评论
如果外键要指向自己,比如要表示对评论的评论,使用self,或者自己的类名parent =models.ForeignKey('self')或者models.ForeignKey('Comment')
parent就表示上一级评论,使用字符串表示类名,在程序运行时,这个类名必须能在ContentType找到,简单点说,就是存在并且被django加载了。不了解django的ContentType没关系,不需要用django内置的一些玩意就用不着。
2.related_name,指定反向查找时的key,举例:
上例中xxx.comment_set.all()可以取出该XXX的所有评论,是因为默认related_name就是类名Comment的全小写comment+"_set",默认就够使用了
什么时候必须指定呢?上面的model添加两个属性
from_user =models.ForeignKey(User, related_name='from_me')
to_user =models.ForeignKey(User, related_name='to_me')
此时如不指定related_name就会报错,因为django不知道该如何反向取值了。如果按照上面分别指定了related_name,那么外键反向取值,如下
user.from_me.all()就能取出我发出的所有评论
user.to_me.all()取出所有发给我的评论
3.limit_choices_to 没啥说的,限制外键取值范围
4.to_field,是指外键指向另一个表(模型)的哪一个字段(属性),默认指向主键id.可以修改指向别的字段(属性),不过确保其唯一性
5.on_delete是说外键指向的对象删除时,本对象应该采取的措施,默认级联删除。
一共有cascade, protect, set_null, set_default, set(), do_nothing等选择,详见https://docs.djangoproject.com/en/1.5/ref/models/fields/#django.db.models.ForeignKey.on_delete
5.多对多
跟外键差不多,只说不同的。
1. symmetrical,对称性。比如User有个多对多键friends指向User,我添加你做朋友,自动地,你也是我的朋友,这就是对称性,默认为true,开启。
可以设置False,关闭后,我添加你做朋友,我的好友列表有你。你的好友列表没有我。
2.through,过渡表。django默认的多对多实现是采用中间过渡表完成。比如A多对多B,那么有张中间表
id:中间表自己的主键
a_id:指向A表主键
b_id指向B表主键
用这张过渡表记录AB的多对多关系。很遗憾,这张表就只有三个字段,不能记录其他信息,比如我是什么时间添加你做朋友。想要存储额外信息到过渡表,就该 使用through了。官网有个例子https://docs.djangoproject.com/en/1.5/topics/db/models/#intermediary-manytomany
3.db_table,过渡表表名。如上文,AB多对多过渡表表名默认为a_b,二者类名下划线链接,再小写。你可以指定其他名字。
6.一对一,跟外键几乎一样,多一个选项(options),叫parent_link,没啥大作用。给个地址看看官网解释https://docs.djangoproject.com/en/1.5/ref/models/fields/#django.db.models.OneToOneField.parent_link
3.最后一点了,Meta设置
abstract 用来指定是否是抽象类,抽象类不会生成表,适合用于面向对象的基类
app_label默认不用指定,django自省即可(别问什么是自省啊,有百度可以问)。当你的model定义在models.py或者models包外时,或者是动态模型,那就必须指定了,值就是你的app名字即可。关于动态模型,详见django dynamic models and field injections
db_table默认值是“appName”+“_”+ “modelName”,可以指定别的表名。使用已存在的库表时有用。
get_latest_by,指定django的latest函数取最新记录时默认按什么排序
ordering指定模型默认按什么排序,注意此设置会反映到数据库层面,并且会有外键传递影响
permissions指定该模型需要额外创建的权限代码,默认add change delete 三种
unique_together 指定数据库表中哪几列必须组合起来唯一,不能全部相同。同联合主键。
index_together 指定数据库表中哪几列必须组合起来建立索引。与unique_together唯一区别在于不要求组合唯一。
verbose_name给你的模型取一个好理解的好阅读的名字在页面上显示,
verbose_name_plural是verbose_name的复数,默认verbose_name+“s“
proxy order_with_respect_to不常用,managed一般不需改变,有需要可看看文档
充分利用django内置的实现完成你的功能,不要自己苦思冥想去实现一些偏僻的功能,而且尽最大可能写最精确的代码,就OK了。