首页 > 代码库 > django学习之Model(二)
django学习之Model(二)
继续(一)的内容:
1-跨文件的Models
在文件头部import进来,然后用ForeignKey关联上:
from django.db import modelsfrom geography.models import ZipCodeclass 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 modelsclass 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 modelsclass 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 modelsclass 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 modelsclass 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 modelsclass CommonInfo(models.Model): name = models.CharField(max_length=100) age = models.PositiveIntegerField() class Meta: abstract = Trueclass Student(CommonInfo): home_group = models.CharField(max_length=5)
注意:base class中的field名称与子类中的field名称不能重复。
同样对于Meta也可以继承:
from django.db import modelsclass 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.pyfrom django.db import modelsclass Base(models.Model): m2m = models.ManyToManyField(OtherModel, related_name="%(app_label)s_%(class)s_related") class Meta: abstract = Trueclass ChildA(Base): passclass ChildB(Base): pass
#given another app rare/models.pyfrom common.models import Baseclass 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 modelsclass 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 modelsclass 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及数据。
(这一部分没有理解好,留在后边学习)
多个继承
就像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(一)中的所有带有链接的地方,都是需要接下来学习。
加油!