【问题标题】:Inheriting specific parent model attributes in foreignkey->'self' relationship in Django在Django的foreignkey->'self'关系中继承特定的父模型属性
【发布时间】:2010-12-21 22:23:30
【问题描述】:

我有一个 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 不是空/空白时,attribute1 和attribute2 等是从父对象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 个属性。有没有更好的方法来做到这一点?

【问题讨论】:

    标签: python django django-models


    【解决方案1】:

    也许__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)
    

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

    【讨论】:

      【解决方案2】:

      你可以使用描述符:

      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')
      

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

      【讨论】:

        【解决方案3】:

        我遇到了类似的问题。目标是有一个类,允许某些字段承担组的值。下面的代码将这些功能引入您的模型:

        1. 如果要让字段继承组值,只需创建另一个名为“inherit_group_”的 BooleanField。
        2. 如果您设置可继承字段的值,“inherit_group_”的值会自动设置为 False(因为您已设置显式值)。
        3. 如果将“inherit_group_”设置为 True,则关联字段的值将返回到组的值。
        4. 处理其他所有内容,包括保存和初始化。

        一个警告 - 如果您在此模型中使用 ModelForm,则需要覆盖保存功能,以便最后设置所有“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
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-04-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多