django学习之Model(二)
继续(一)的内容:
1-跨文件的Models
在文件头部import进来,然后用ForeignKey关联上:
from django.db import models
from geography.models import ZipCode class Restaurant(models.Model):
# ...
zip_code = models.ForeignKey(ZipCode)
2-field名字的约束
1)-不能是Python预留字
2)-不能有连续的2个下划线,例如foo__bar,2个连续下划线是django的query查询的语法。
field的名称不必匹配database中的column的名称。SQL的保留字也可以作为model中field的名称,因为django躲开了SQLquery查询中的所有的database的table名称和column名称。django使用database engine的引用语法。
3-自定义的一些field类型
如果常用的model field不能满足需要,可以自定义一些field。完备的资料参考Writing custom model fields.
4-Meta选项
可以给model加一些内容,用内置的class Meta来实现:
from django.db import models class Ox(models.Model):
horn_length = models.IntegerField() class Meta:
ordering = ["horn_length"]
verbose_name_plural = "oxen"
Meta里放的东西可以是任何非field的东西(应该是一些数据,方法在def的函数中定义,下边将会提到),例如 verbose_name 也可以放进来。Meta不是必须的,可以参考 model option reference.
5-Model中的一些方法
Manager的方法能处理一些广义上的普适的事情,更具体的事情可以在model中自己定义方法来处理:
from django.db import models class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField() def baby_boomer_status(self):
"Returns the person's baby-boomer status."
import datetime
if self.birth_date < datetime.date(1945, 8, 1):
return "Pre-boomer"
elif self.birth_date < datetime.date(1965, 1, 1):
return "Baby boomer"
else:
return "Post-boomer" def _get_full_name(self):
"Returns the person's full name."
return '%s %s' % (self.first_name, self.last_name)
full_name = property(_get_full_name)
最后一个方法是一个property.
model instance reference 中会有完整的自动产生的方法,下边给出一些常用到的,需要重写的 方法:
__str__()
python 3中与__unicode__()相同
__unicode__()
Python神奇的方法,可以把任何对象转换成unicode编码的字符串。值得注意的是,这个方法通常会用在想要把一个对象呈现在可交互的控制面板(console)中。通常都会自定义重写来用,默认的一般没啥用。
get_absolute_url()
这个是告诉django如何来计算URL的,任何一个有唯一URL的对象,都应该定义这个方法。
6-重写预定义的model的方法
还有一类封装了database中的方法的model方法,这些model中的方法也可以自定义。尤其是当你经常想改变save()或delete()。例如,想在save()的时候做点其他的事情,就可以自定义save(),在里面添加相应的方法,如下:
from django.db import models class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField() def save(self, *args, **kwargs):
do_something()
super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.
do_something_else()
也可以阻止save发生,灵活度很高:
from django.db import models class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField() def save(self, *args, **kwargs):
if self.name == "Yoko Ono's blog":
return # Yoko shall never have her own blog!
else:
super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.
特别要注意,记得在重写方法中一定要调用superclass方法,上例中的
super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.
否则,默认的(被你的重写覆盖掉的)方法是不会执行的,这样数据就不能被保存。同样重要的还有,传递的变量*args, **kwargs. django总是会用添加新的变量的方式来拓展预置的方法的能力。如果在自定义的方法中用到了*args和**ksargs,要保证所写的代码能够自动支持这些变量,当他们被添加的时候。(*args和**kwargs参考这篇博客)
提示:重写的model中的方法在批量操作时不会被调用。delete()在用QuerySet进行批量操作时就没必要,为了保证自定义的delete方法被执行,可以用pre_delete或post_delelte。不幸的是,creating或updating在批量操作时都不能用,同理save(), pre_save, post_save.注意这只是说的被重写了的方法。
7-操作自定义的SQL
另一种常见的形式是在model 的方法中写自定义的SQL声明。更多信息参考using raw SQL
8-Model的继承
跟Python的类的继承相似,别忘了在文件的头部写上:django.db.models.Model
django中有3种常见的继承:
1)-父类只用来被继承,父类并不自己生成database table。请参考Abstract base classes
把一些共同的信息放到基类(base class)中去,然后在base class 中的Meta里,令abstract=True,这样,这个base class 就不会去创建database table了,然后用子类去继承这个抽象基类:
from django.db import models 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)
注意:base class中的field名称与子类中的field名称不能重复。
同样对于Meta也可以继承:
from django.db import models class CommonInfo(models.Model):
# ...
class Meta:
abstract = True
ordering = ['name'] class Student(CommonInfo):
# ...
class Meta(CommonInfo.Meta):
db_table = 'student_info'
可能会有疑问,这样不是把基类的abstract=True也继承下来了,然后这个子类又成为了abstract了吗?不会的,django会自己处理掉这个abstract,除非你就是要再来个abstract类,然后再去创建子类的子类,这样你需要自己打开abstract=True. 当然,基类中不能出现如db_table这样的参数,因为所有它的子类都会继承这个参数,然后生成了相同名称的database table.
另外还要注意related_name. 如下:
#given an app common/models.py from django.db import models class Base(models.Model):
m2m = models.ManyToManyField(OtherModel, related_name="%(app_label)s_%(class)s_related") class Meta:
abstract = True class ChildA(Base):
pass class ChildB(Base):
pass
#given another app rare/models.py from common.models import Base class ChildB(Base):
pass
如果没有用到related_name的话,则rare下的ChildB和common下的ChildB会产生相同的database table的名字。%(app_label)s会用所在app的名字替换,%(class)s会用所在的子类的名字替换。在上述例子中,common.ChildB.m2m的reverse name就是common_childb_related.rare.ChildB.m2m的reverse name就是rare_childb_related.如果没有related_name,django会在syncdb的时候报错。另外就是,related_name是在base class中使用的。如果没有用related_name,则子类中的field的revers name会是class name_set,例如,childa_set,childb_set.
2)-父类不但用来被继承,而且自己也能够生成database table,则参考Multi-table inheritance:
from django.db import models 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()
因为父类子类分别自己生成database table,所以下边这两个query都能够访问到数据:
>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")
如果在已经保存数据的database table中,有一个table,是从Place继承来的Restaurant,则下边的query可以执行:
>>> p = Place.objects.get(id=12)
# If p is a Restaurant object, this will give the child class:
>>> p.restaurant
<Restaurant: ...>
前提是已知id=12的table中restaurant继承自place,否则,会有Restaurant.DoesNotExist的错误。
在这种继承关系中,Meta的继承是不被允许的。因为基类中已经使用了Meta中的变量,如果子类能够继承,则在子类中同时出现了2次这个变量,则产生了冲突。(我觉得这里有必要了解django的model是怎样去联系sql语句生成database table的,以及python中继承的语法和原理,这样就能清楚明白为什么子类不继承基类的Meta.)但是有些个别的Meta中的变量还是可以继承的,如ordering和get_lates_by.例如,如果不想子类按照基类的ordering方法排序的话,可以这样写:
class ChildModel(ParentModel):
# ...
class Meta:
# Remove parent's ordering effect
ordering = []
相当于重写,重新置为空。
由于这种继承是隐藏调用了OneToOneField把子类与基类联系起来,所以很容易从基类找到子类。但是如果我在子类中用ManyToManyField来联系一个别的类的话,那么需要用related_name来区分,否则reverse的时候会因为有很多相同的名称而找不到:
class Supplier(Place):
# Must specify related_name on all relations.
customers = models.ManyToManyField(Restaurant, related_name='provider')
django会自动调用OneToOneField来连接子类和基类,如果想要自己控制此连接,可以在自己写一个OneToOneField并令parent_link=True,来表明此子类连接到基类。
3)-最后,如果只是想修改一下一个model在Python层面的一些行为,而不是去更改model的field,则参考Proxy models.
在2)中的继承,不同的子类都要建立属于自己的database table,也就是有自己的数据库空间来放数据。现在有这么一种子类,该子类中只有操作方法,操作的对象(即数据,database table)还是基类中的数据,这种继承叫做Proxy models. 与2)中的继承方法相同,只是没有新加field,同时proxy=True即可,如下:
from django.db import models 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
MyPerson与基类Person操作同一个database table,也就是Person生成的database table.所以下述代码:
>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")
<MyPerson: foobar>
也可以写一个区别于基类的排序方法,当调用proxy model时,对基类数据执行proxy model中的排序方法:
class OrderedPerson(Person):
class Meta:
ordering = ["last_name"]
proxy = True
可以理解为,基类model和proxy model 它们里边定义的方法都是独立的,没有继承关系,只是操作的数据对象是一个,就是基类model建立的database table,所以用哪个model访问就会返回对应的方法处理后的数据。例如,上例中OrderedPerson就会返回按照last_name排序的数据库。
同时,对于非abstract类型的基类,proxy model只能继承一个,不能继承自多个,因为proxy model不提供对于不同的database table中的rows的连接。对于abstract类型的基类,可以继承自多个,因为abstract类型的model本身并不没有建立database table. proxy model 可以继承他们没有定义的来自非abstract的基类中的Meta的信息。
proxy 继承与 unmanaged models之间的区别:
1、可以设置Meta.managed=Flase,来指定model的fields. 也可以通过小心的设置Meta.db_table来创建一个unmanaged model, 来覆盖一个存在的model,并且加入一些python 的方法。但这样会特别琐碎和重复性。
2、对于proxy models来说,是继承了基类的managers,包括默认的manager。但是当涉及到multi-table继承时,proxy model子类并不继承基类的managers, 更多信息可参考 manager documentation
当这两种特点放在一块考虑的时候,会把API变得异常复杂,所以总的原则如下:
1、如果想要给一个存在的model或者database table创建镜像,但是比不需要原始table的column的时候,可以令Meta.managed=False,此方法一般对于不再django控制下的database views 和 tables比较有用。
2、如果只是想改动以下python层面的一些行为方法,但是原始的field要保持不变,就令Meta.managed=True,这样,proxy model就会准确的拷贝了原model建立的database table及数据。
(这一部分没有理解好,留在后边学习)
9-多个继承
就像Python的继承一样,django也可以从多个基类来继承,但是注意,当多个基类中含有相同的名字的信息时(例如 Meta),子类只继承来自第一个基类的(Meta)。通常不需要而且不建议用多个基类来继承,因为当出现错误时,寻找起来会很困难。
不允许在子类中通过重新定义来隐藏基类的field
虽然在Python中可以这样,但是在django中会一直禁止这样做,例如在基类中有一个field name是author,则在子类中,禁止出现author的field name了。这个限制条件只是针对field的实例(即在django中),在Python中还是按照python的语法去写。所以说,如果用python语句去更改table当中column的名字,使子类与基类的某个column名字相同,例如都是author,这样是可以的,因为你是用python去写的,而不是在django中用model去生成的。如果在django中重写了基类中的field name, 会出现FieldError的错误。
django中Models的语法部分到此有了基础的理解了,本文以及django学习之Model(一)中的所有带有链接的地方,都是需要接下来学习。
加油!
django学习之Model(二)的更多相关文章
- Django学习笔记(二):使用Template让HTML、CSS参与网页建立
Django学习笔记(二):使用Template让HTML.CSS参与网页建立 通过本文章实现: 了解Django中Template的使用 让HTML.CSS等参与网页建立 利用静态文件应用网页样式 ...
- Django学习笔记(二)App创建之Model
通过实例学习, 构建一个投票(Polls)Application, 目标结果包含两个site, 一个site用来显示投票问题以及投票结果(即将展示出来的网站), 另一个site用来管理Poll实例的增 ...
- django学习之Model(一)
认认真真学Django,从现在开始. 学习资料来源于官方网站:https://docs.djangoproject.com/en/1.6/ 1-新建一个models.py from django.db ...
- django 学习之DRF (二)
Django学习之DRF02 Serializer序列化器之反序列化操作 1.调⽤序列化器进⾏验证 0.准备序列化器 class BookInfoSerializer(serial ...
- Django 学习笔记(二)
Django 第一个 Hello World 项目 经过上一篇的安装,我们已经拥有了Django 框架 1.选择项目默认存放的地址 默认地址是C:\Users\Lee,也就是进入cmd控制台的地址,创 ...
- Django学习笔记之二
一.使用Django自带的后端管理平台 1.后台创建管理员 python manage.py createsuperuser Email address: admin@example.com Pass ...
- django学习之Model(五)MakingQuery
接着上篇. 10-一次更新多个对象 有时想要对QuerySet中的所有对象的某一个field来设定一个值,这时候可以像下边这样用update(): # Update all the headlines ...
- django学习之Model(三)QuerySet
接下来主要学习Models中的Making queries 写好models.py后,django会自动提供一个数据库的抽象API,来实现CRUD(create, retrieve, update, ...
- django 学习之model操作(想细化)
一.Field选项 null=True 数据库为空 blank=True admin相关为空 choices:choices意味着静态数据的变化不会太大. db_column: 用于此字段的数据库的列 ...
随机推荐
- Java中有关构造函数的一道笔试题解析
Java中有关构造函数的一道笔试题解析 1.详细题目例如以下 下列说法正确的有() A. class中的constructor不可省略 B. constructor必须与class同名,但方法不能与c ...
- 2.3.9 用NPOI操作EXCEL--通过NPOI获得公式的返回值
前面我们学习了通过NPOI向Excel中设置公式,那么有些读者可能会问:“NPOI能不能获取公式的返回值呢?”,答案是可以!一.获取模板文件中公式的返回值如在D盘中有一个名为text.xls的Exce ...
- 如何通过java反射将数据库表生成实体类?
首先有几点声明: 1.代码是在别人的基础进行改写的: 2.大家有什么改进的意见可以告诉我,也可以自己改好共享给其他人: 3.刚刚毕业,水平有限,肯定有许多不足之处: 4.希望刚刚学习java的同学能有 ...
- Spring AOP报错
八月 01, 2016 10:08:48 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRe ...
- eclipse导出doc文档
选中需要导出的项目, 1 点击eclipse上面的Project,选择Generate javadoc..., 2 然后配置 javadoc command,比如我本地的路径为: C:\Program ...
- office2013破解工具
终于找到破解工具了,每次打开word文档都弹出限期激活对话框,真的是相当烦躁啊,先把破解工具奉上!!!! 工具名: HEU-KMS-ActivatorV1.1绿色版 链接: office2013 和 ...
- 【转】论文、会议、期刊评价|Indicate paper, conference, Journal
转自“浙江大学计算机学院软硬件协同设计实验室”:http://multicore.zju.edu.cn/fatlab/Indicate-paper.htm 1 体系结构领域,排名为 ...
- Linux学习:find、chmod、ps命令
下面介绍下linux下find.chmod.ps这三个常见命令的使用. 这每个命令都有很多可选的参数,不同参数体现的功能不一样.我们这里不一一介绍各种参数的含义,只介绍最常见的使用场景. 一.find ...
- Spring Cache抽象详解
缓存简介 缓存,我的理解是:让数据更接近于使用者:工作机制是:先从缓存中读取数据,如果没有再从慢速设备上读取实际数据(数据也会存入缓存):缓存什么:那些经常读取且不经常修改的数据/那些昂贵(CPU/I ...
- 破解企业QQ对个人QQ登陆的限制(原创)
运行系统:WIN7 破解时间:2014-02-19 破解思路:自从2013-11-11的1.85版企业qq更新后,网上流传的破解方法(运行文件BeatQQEIM.bat)已经不起作用了,可以同时登陆, ...