我正在考虑与django的内置User
或Group
建立关系的最佳方式。
By'或'我的意思是模型实例必须由User
或Group
专有。
我认为通过查看上面的模型,这个问题应该很容易理解。
这是我目前的实施。我一直在看GenericRelations,但他们没有看到 适用于限制在如此少数型号的人。
编辑:使用抽象模型重构。
class OwnedModel(models.Model):
_owner_user = models.ForeignKey(User, null=True, related_name='%(class)s')
_owner_group = models.ForeignKey(Group, null=True, related_name='%(class)s')
class Meta:
abstract = True
@property
def owner(self):
return self._owner_user or self._owner_group
@owner.setter
def owner(self, value):
if isinstance(value, User):
self._owner_user = value
if self._owner_group:
self._owner_group = None
elif isinstance(value, Group):
self._owner_group = value
if self._owner_user:
self._owner_user = None
else:
raise ValueError
class RemoteAccess(OwnedModel):
SSH = 's'
SUPPORTED_PROTOCOLS = (
(SSH, "ssh"),
)
host = models.CharField(_('host name'), max_length=255, validators=[full_domain_validator])
protocol = models.CharField(_('protocol'), max_length=1, choices=SUPPORTED_PROTOCOLS, default=SSH)
credential = models.OneToOneField(RemoteCredential)
我当前实施的主要问题是:
User
或Group
?
__init__
覆盖是否可以成为最佳选择?谢谢!
答案 0 :(得分:1)
我会覆盖save()方法,并定义自定义异常
def save(self, *args, **kwargs):
if self._owner_user is None and self._owner_group is None:
raise NoOwner
super(OwnedModel, self).save(*args, **kwargs)
覆盖 init 可能会在某些情况下导致问题,除非您在保存之前没有所有者(例如在表单等中)。
我认为没有更简洁的方法来坚持使用Django用户/组。
答案 1 :(得分:0)
根据您的程序,有多种选择可以实现这一点。一种方法是使用继承。
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,并且您将遇到列表问题。要解决一些问题,你需要向所有者提供一些关于他的孩子的额外知识(以第3范式和封装原则为代价)。
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)
哟可能会美化这一点,但我认为这段代码应该得到这个想法。此解决方案还有一个缺点,您需要绕过所有者模型的调用
行为(这可能会被缩短),你失去3nf形式的数据库结构。从应用程序的其他部分,您需要避免直接调用用户和组模型(在所有权的上下文中),并使用抽象层,
这可能有时会导致AbstarctOwnerBehavior接口增长,从设计的角度来看,我们最好保持接口小。如果没有发送post_init信号,你也会遇到问题。
但是如果你使用abstarction层 - 你会获得利润,如果删除其他情况则不必要,这会简化代码,使其更加健壮,设计良好时,这种行为很容易测试(因为他们没有额外的if-else路径) )和extendable。
所以没有一个'最佳'的解决方案适合所有情况,这取决于你的工程判断。