根据您的程序,有几种选择如何实现这一点。一种方法是使用继承。
class Owner(models.Model):
def create(self,instance):
raise NotImplementedError('Method create should be called from subclasses of owner')
class UserOnwer(Owner):
user = models.ForeignKey(User)
def create(self,instance):
self.user = instance
class GroupOnwer(Owner):
group = modelsForeignKey(Group)
def create(self,instance):
self.group = instance
class RemoteAccess(models):
....
owner = models.ForeignKey(Owner)
def get_onwer(): # shortcut implementation of this method must go to Owner.Manager
try:
return UserOnwner.objects.get(pk = self.owner_id) # or what is more common case
except UserOnwner.DoesNotExist:
return GroupOnwer.objects.get(pk = self.owner_id)
我需要说几句关于权衡你在这里支付的费用。在这种情况下,最多有两个查询可以获取 GroupOwner,并且您将遇到列表问题。为了解决一些问题,您需要向所有者提供一些关于他的孩子的额外知识(以第三范式和封装原则为代价)。
class Owner(models.Model):
owner_type = models.CharField(choices('user','group'))
@classmethod # factory here
def create(cls,instance):
if is_instance(instance,User):
user_owner = UserOnwer()
return user_owner.user = instance
if is_instance(instance,User):
group_owner = GroupOwner()
return group_owner.group = instance
class UserOnwer(Owner):
user = models.ForeignKey(User)
class GroupOnwer(Owner):
group = modelsForeignKey(Group)
class RemoteAccess(models):
....
owner = models.ForeignKey(Owner)
def get_onwer(): # shortcut implementation of this method must go to Owner.Manager
if self.owner.owner_type == 'user':
return UserOnwner.objects.get(pk = self.owner_id)
elif self.owner.owner_type == 'group':
return GroupOnwer.objects.get(pk = self.owner_id)
好的,这在某些情况下是合适的,但在某些情况下却不是。还有额外的查询,为了避免它我们可以分开存储级别和行为级别。
class Owner(models.Model):
owner_type = models.CharField(choices('user','group'))
user = models.ForeignKey(User,null=True,blank=True)
group = models.ForeignKey(Group,null=True,blank=True)
@classmethod
def create(cls,instance):
owner = Owner()
owner.set_owner(instance)
def set_owner(self,instance):
if is_instance(instance,User):
self.owner_type = 'user'
elif is_instance(instance,Group):
self.owner_type = 'group'
self.post_init()
self.owner_behavior.set_instance(instance)
def post_init(self): #factory is moved here
self.owner_behavior = AbstarctOwnerBehavior()
if self.owner_type == 'user':
self.owner_behavior = UserOnwerBehaviour(self)
elif self.owner_type == 'group':
self.owner_behavior = GroupOnwerBehaviour(self)
def get_owner(self):
return self.owner_behaviour.get_owner()
def title(self):
self.owner_behavior.printed_title()
class AbstarctOwnerBehavior(object):
def __init__(self,owner_instance):
self.owner_instance = owner_instance
def set_instance(self, instance):
raise NotImplementedError()
def get_instance(self):
raise NotImplementedError()
def printed_title(self):
raise NotImplementedError()
class UserOnwerBehaviour(OwnerBehaviour):
def get_instance(self):
return self.owner_instance.user
def set_instance(self, instance):
self.owner_instance.user = instance
def printed_title(self):
return self.owner_instance.user.username # note here
class GroupOnwerBehaviour(OwnerBehaviour):
def get_instance(self):
return self.owner_instance.group
def set_instance(self, instance):
self.owner_instance.group = group
def printed_title(self):
return self.owner_instance.group.name # and here
# and finally a sinal if you are afraid of overwriting __init__
from django.db.models.signals import post_init
from models import Owner
def owner_post_init_handler(sender, instance, **kwargs):
instance.post_init()
post_save.connect(owner_post_init_handler, sender=Owner)
哟可能会美化一点,但我认为这段代码足以理解这个想法。这个解决方案也有它的缺点,你需要绕过从 Owner 模型到行为的调用(这可能是快捷方式)并且你丢失了 3nf 形式的数据库结构。从应用程序的其他部分,您需要避免直接调用 User 和 Group 模型(在所有权上下文中),并使用抽象层,这有时可能会导致 AbstarctOwnerBehavior 接口增长,从设计的角度来看,我们最好保持接口小. 如果未发送 post_init 信号,您也会遇到问题。但是如果你使用抽象层——你会获得利润,不需要的 if-else 案例被删除,这会简化代码,使其更健壮,如果设计得当,这种行为很容易测试(因为它们没有额外的 if-else 路径) 和可扩展的。
因此,没有适合所有情况的“最佳”解决方案,这取决于您的工程判断。