django学习之Model(二)

时间:2024-01-17 08:27:38

继续(一)的内容:

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(一)中的所有带有链接的地方,都是需要接下来学习。

加油!