在Django中的foreignkey->'self'关系中继承特定的父模型属性

时间:2009-12-01 08:04:11

标签: python django django-models

我有一个Django模型:

class Foo(models.Model):
    name = models.CharField(unique=True)
    attribute1 = models.FloatField(null=True, blank=True)
    attribute2 = models.FloatField(null=True, blank=True)
    attribute3 = models.BooleanField(null=True, blank=True)
    attribute4 = models.CharField(null=True, blank=True)
    inherit = models.ForeignKey('self', related_name='children', null=True, blank=True)

我希望当inherit不为空/空时,属性1和属性2等从父对象inherit继承,这样当我访问属性时,我得到的值为家长。我不关心在孩子身上设定价值。

我考虑过使用模型方法,例如:

_attribute1 = models.FloatField(null=True, blank=True)
get_attribute1(self):
    if self.inherit:
        return self.inherit._attribute1
    else:
        return self._attribute1
set_attribute1(self, value):
    if not self.inherit:
        self._attribute1 = value
attribute1 = property(get_attribute1, set_attribute1)

但是因为我有大约10个属性,所以看起来很难看。有更好的方法吗?

3 个答案:

答案 0 :(得分:2)

也许__getattr____setattr__在这里是个不错的选择。

class Foo(models.Model):
    name = models.CharField(unique=True)
    inherit = models.ForeignKey('self', related_name='children', null=True, blank=True)

    _attribute1 = models.FloatField(null=True, blank=True)
    _attribute2 = models.FloatField(null=True, blank=True)
    _attribute3 = models.BooleanField(null=True, blank=True)
    _attribute4 = models.CharField(null=True, blank=True)

    def __getattr__(self, name):
        if self.inherit and hasattr(self.inherit, name):
            return getattr(self.inherit, name, None)
        elif hasattr(self, '_'+name):
            return getattr(self, '_'+name, None)
        return super(Foo, self).__getattr__(name)

    def __setattr__(self, name, value):
        if self.inherit and hasattr(self.inherit, name):
            return setattr(self.inherit, name, value)
        elif hasattr(self, '_'+name):
            return self.__dict__[name] = value
        return super(Foo, self).__setattr__(name, value)

免责声明:我没有尝试运行此

答案 1 :(得分:0)

您可以使用描述符:

class InheritedAttribute(object):
    def __init__(self, name):
        self.attname = '_' + name

    def __get__(self, instance, owner):
        if instance.inherit:
            return getattr(instance.inherit, self.attname)
        else:
            return getattr(instance, self.attname)

    def __set__(self, instance, value):
        setattr(instance, self.attname, value)

模型看起来像这样:

class Foo(models.Model)
    name = models.CharField(unique=True)
    _attribute1 = models.FloatField(null=True, blank=True)
    _attribute2 = models.FloatField(null=True, blank=True)
    _attribute3 = models.BooleanField(null=True, blank=True)
    _attribute4 = models.CharField(null=True, blank=True)
    inherit = models.ForeignKey('self', related_name='children', null=True, blank=True)
    attribute1 = InheritedAttribute('attribute1')
    attribute2 = InheritedAttribute('attribute2')
    attribute3 = InheritedAttribute('attribute3')
    attribute1 = InheritedAttribute('attribute4')

这可能会通过自动隐藏描述符后面的模型字段的元类来增强。

答案 2 :(得分:0)

我有类似的问题。目标是建立一个允许某些字段采用该组值的类。下面的代码将这些功能介绍给您的模型:

  1. 如果要使字段继承组值,则只需创建另一个名为“inherit_group _”的BooleanField。
  2. 如果设置了可继承字段的值,则“inherit_group_”的值将自动设置为False(因为您已设置了显式值)。
  3. 如果将“inherit_group_”设置为True,则关联字段的值将返回到组的值。
  4. 处理其他所有事情,包括保存和初始化。
  5. 一个警告 - 如果你在这个模型中使用ModelForm,你需要覆盖save函数,以便所有的“inherit_group_属性都被设置,因为当设置了相关的位置字段时,”inherit_group_“将如上所述设置为False。该代码也在下面。这整个事情可能最好通过创建新类来处理 - InheritableModel和InheritableModelForm。虽然我太懒了。)。

    以下是模型的代码:

    _inheritable_fields = []
    
    def __init__(self, *args, **kwargs):
        super(Location, self).__init__(*args, **kwargs)
        # Change iheritable field values to group value if current value is
        # None
        self._inheritable_fields = [ 
            fname for fname in dir(self)
            if hasattr(self, 'inherit_group_%s' % fname)]
    
        # Make sure that all fields are in the correct state given the
        # inherit_group values
        [setattr(self, 'inherit_group_%s' % fname, getattr(self,
            'inherit_group_%s' % fname))
            for fname in self._inheritable_fields]
    
    def __setattr__(self, name, val):
        super(Location, self).__setattr__(name, val)
    
        if name == "group" and val:
            # A new group was specified. Update all fields that are currently
            # inheriting from the group
            [models.Model.__setattr__(self, fname, getattr(self.group, fname))
                for fname in self._inheritable_fields
                if getattr(self, 'inherit_group_%s' % fname)]
    
        elif name in self._inheritable_fields:
            # An inheritable field value changed. Update its inheritance state
            models.Model.__setattr__(self, 'inherit_group_%s' % name, False)
    
        elif name.startswith('inherit_group_'):
            field_name = re.sub('^inherit_group_', '', name)
            if val and field_name in self._inheritable_fields:
                # An inheritance state (e.g., inherit_group_name) was changed.
                # Change value back to group value
                if hasattr(self, 'group'):
                    models.Model.__setattr__(self, field_name, 
                        getattr(self.group, field_name))
                else:
                    models.Model.__setattr__(self, field_name, None)
    
    def save(self, *args, **kwargs):
        # Set all fields using the inherited value to None for DB storage.
        val_from_group = [ 
            fname for fname in self._inheritable_fields
            if getattr(self, 'inherit_group_%s' % fname)]
        [models.Model.__setattr__(self, fname, None) for fname in val_from_group]
    
        super(Location, self).save(*args, **kwargs)
    
        # Return the fields changed above back to the group values.
        [models.Model.__setattr__(self, fname, getattr(self.group, fname))
            for fname in self._inheritable_fields
            if getattr(self, 'inherit_group_%s' % fname)]
    

    以下是ModelForm的代码:

    def save(self, commit=True):
        location = super(LocationForm, self).save(commit=False)
    
        # location.inherit_group_x has to be set last as it'll be set to
        # False when it's associated field is set
        [setattr(location, 'inherit_group_%s' % fname,
            self.cleaned_data['inherit_group_%s' % fname])
            for fname in location._inheritable_fields]
    
        if commit:
            location = super(LocationForm, self).save()
    
        return location