Наследование определенных атрибутов родительской модели в отношениях Foreignkey -> 'Self' в Django - PullRequest
2 голосов
/ 01 декабря 2009

У меня есть модель 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, чтобы при доступе к атрибутам я получал значения родительского объекта. Меня не волнует установка значений у ребенка.

Я думал об использовании методов модели, напр .::1008*

_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 ]

2 голосов
/ 01 декабря 2009

Возможно, __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)

Отказ от ответственности: я не пытался запустить это

0 голосов
/ 03 июня 2012

У меня была похожая проблема. Цель состояла в том, чтобы иметь класс, который позволял бы определенным полям принимать значение группы. Приведенный ниже код вводит эти функции в вашу модель:

  1. Если вы хотите, чтобы поле унаследовало значение группы, вам нужно всего лишь создать еще один BooleanField с именем «inherit_group _».
  2. Если вы установите значение наследуемого поля, для значения «inherit_group_» автоматически устанавливается значение «Ложь» (поскольку вы установили явное значение).
  3. Если вы установите для свойства «attribute_group_» значение «Истина», значение соответствующего поля возвращается обратно к значению группы.
  4. Все остальное обрабатывается, включая сохранение и инициализацию.

Одно предупреждение - если вы используете ModelForm с этой моделью, вам необходимо переопределить функцию сохранения, чтобы все атрибуты «attributeit_group_» устанавливались последними, поскольку, когда установлены соответствующие поля местоположения, «attributeit_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
0 голосов
/ 01 декабря 2009

Вы можете использовать дескриптор:

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

Возможно, это можно улучшить с помощью метакласса, который автоматически скрывает поля модели за дескриптором.

...