抽象基类

抽象基类在你将一些共同信息导入到多个其他模型的时候很有用。

你写你的基类,并在Meta类(元类)中设置abstract=True。该模型不用于生成任何数据库表。反而,当抽象基类用于其他模型的一个基类是,它的fields(字段)会被添加到那些子类中。

这个是错误的:抽象基类和其子类拥有同名的字段(Django会报异常)。

一个例子:

from django.db import models

name = models.CharField(max_length=100)

age = models.PositiveIntegerField()

class Meta:

abstract = True

class Student(CommonInfo):

home_group = models.CharField(max_length=5)

上例中,Student模型有3个字段,name,age和home_group.CommonInfo模型不能用于普通Django模型,因为这是个抽象基类。CommonInfo不会生成一个数据库表或者拥有一个管理器,不能被实例化和直接存储。


对于许多用户来说,这种继承模型类型正是你想要的。它提供了在Python级别分解共同信息的一种方法,同时只根据数据库等级的子模型来生成一个数据库表。


Meta继承:

   创建抽象基类的时候,Django 会将你在基类中所声明的有效的 Meta 内嵌类做为一个属性。

如果子类没有声明自己的Meta类,它将继承父类的Meta类。如果子类想要扩展父Meta类,可以子类化。


例子:

from django.db import models

class CommonInfo(model.Model):

#...

class Meta:

abstrasct = True

ordering = ['name']

class Student(CommonInfo):

#...

   class Meta(CommonInfo.Meta):

 db_table = ‘student_info’

Django对抽象基类的Meta类做了一个调整:在安装Meta属性前,设置abstract=False.这意味着抽象基类的子类本身不会自动变成抽象类。当然,你可以通过从其他抽象基类继承的方法来生成一个抽象基类。你只要记住,每次明确地设置abstract=True。

对抽象基类而言,有些属性放在Meta内嵌类中没有意义。例如,包含db_table意味着所有子类(那些没有Meta内嵌类的子类)将会使用相同的数据库表,当然这也不是你想要的。


对于related_name 和 related_query_name要特别小心

当你正在ForeignKey或者MangToManyField中正使用related_name或者related_query_name时,你总要给字段(field)明确一个unique reverse name(唯一反向名称)和query time。这通常会在抽象基类中导致出现问题,因为Django会将基类字段添加进子类当中,而每个子类的字段属性值基本相同。这样,在使用ForeignKey或者ManyToManyField反向指定时,就无法确定指向哪个子类了。


为解决这个问题,当你(仅)在抽象基类中使用related_time或者related_query_name时,部分值应该包含'%(app_label)s'和‘%(class)s’。

‘%(class)s’会被子类名字取代;

‘%(app_label)s’会被子类所在App的名字取代。

举例:

假定有个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",

related_query_name = "%(app_label)s_%(class)ss",

)

class Meta:

abstract = True

class ChildA(Base):

pass

class ChildB(Base):

pass

在另一个app,rare/models.py中:

from common.models import Base

class ChildB(base):

pass

那么,common.ChildA.m2m字段的反向名称common_childa_related,

   common_ChildB.m2m字段的反向名称为commom_childb_related;

   rare app中,rare.ChildB.m2m字段的反向名称为rare_childb_related.


如果你没有在抽象基类中为某个关联字段定义related_name属性,那么默认的反向名称就是子类名称加上‘_set’,它能否正常工作取决于你是否在子类中定义了同名字段。

例如,上面代码中,如果去掉related_name属性,在ChildA中,m2m字段的反向名称就是childa_set;

而ChildB的m2m字段的反向名称就是childb_set.


Multi-table继承 多表继承

这是Django支持的第二种继承方式。使用这种继承方式时,同一层级下的每个子model本身是一个model。

每个子model都有自己的数据库表,可单独查询与创建。继承关系在子model和它的每个父类之间采用链接方式。(通过自动创建的OneToOneField)。

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(default=False)

serves_pizza = models.BooleanField(default=False)

Place的所有字段在Restaurant中都可以使用,尽管数据驻留在不同的数据库表中。所以下面这些都是可能的:

>>>Place.objects.filter(name="Bob's Cafe")

>>>Restaurant.objects.filter(name="Bob's Cafe")


如果你有一个Place,同时也是Restaurant,你可以使用小写的model名称,从Place对象得到Restaurant对象:

>>>p = Place.objects.get(id=12)

#If p is restaurant object, this will give the child class:

>>>p.restaurant

<Restaurant:...>

如果上例中p不是一个Restaurant,(它已经直接有Place对象创建或者是一些其他类的父类)。引用p.restaurtant将抛出Restaurant.DoesNotExist异常:

>>> from myapp.models import Place,Restaurant

>>>p=Place.objects.create(name='Place',addres=‘Place’)

>>>p.restaurant

DoesNotExist:Place has no restaurant.

自动创建的Restaurant的OneToOneField字段链接Place如下:

place_ptr = models.OneToOneField(Placemon_delete=models.CASCADE,parent_link=Terue,)

你可以重载Restaurant的字段,通过在自己的OneToOneField中申明parent_link=True。



Meta和多表继承

在多表继承中,子类继承父类的Meta内嵌类没什么不清楚的。所有Meta选项已经对父类起作用了,再次使用只会起反作用(这与使用抽象基类的情况正好相反,因为抽象基类并没有属于它自己的内容)


待续。。。。









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值