-
Итак, я на днях поразбирался с наследованием в Django.
from django.db import models from django.contrib.contenttypes import generic # Create your models here. class Parent(models.Model): name = models.CharField(max_length=30) def __unicode__(self): return 'Parent: ' + self.name class Child1(Parent): number = models.IntegerField() def __unicode__(self): return 'Child1: ' + self.name class Child2(Parent): num = models.IntegerField() class second(models.Model): parent = models.ForeignKey(Parent)Создаю Child1 и second, присваиваю second.parent Child1. Это работает. Но проблема в том, что когда я обращаюсь к second.parent, я попадаю именно на экземпляр класса Parent, с его методами и полями. А хотелось то конечно, чтобы вызывался именно
__unicode__Child1, а не Parent.Видимо так сделано просто. И в самом деле - как ORM должен вычислить, что вот этот конкретный Parent на самом деле его потомок Child1. Как вы думаете, должна ли быть возможность получить именно потомка? Я знаю, что можно получить потомка через parent.child1, но откуда же мне знать какого именно потомка получать?
Самым простым решением смотрится добавление к предку поля generic.GenericForeignKey и заполнение его потомком.
Хотелось бы прочитать мыли сообщества на эту тему.
-
Но проблема в том, что когда я обращаюсь к second.parent, я попадаю именно на экземпляр класса Parent, с его методами и полями.
Не верю :-). Если сделать ровно так:
c = Child1(name='Child1', number=1) c.save() s = second(parent=c) s.save() print s.parentТо позовется
Child1.__unicode__. Потому что живой объект класса конечно знает, к какому классу он относится.Но я подозреваю, что проблема в другом. Если потом сделать так:
s = second.objects.get(pk=s.id) print s.parentТо это уже будет объект класса Parent. Потому что при создании объекта s из базы он понятия не имеет, каким именно наследником он когда-то был проинициализирован, у него остается только ссылка на parent. Сохранение объекта в базу не является его полной десериализацией и вся питоновая метаинформация о том, какому классу он принадлежит, теряется. Это частный случай проявления объектно-реляционного несовпадения импеданса, из-за которого ORM никогда не бывают полностью прозрачными. Надо просто это учитывать. Проще всего — вообще не пользоваться наследованием моделей без крайней надобности.
-
Ну а если убрать несовпадение? Чтобы по s.parent.successor получать именно потомка, а не перебирать всех возможных.
Для этого, как я понимаю, надо информацию о потомке сохранять в parent. Как вы думаете, стоит сделать такую опцию? Как мне видится, надо будет добавить поле (можно просто информацию о классе наследника, наверное) к предку, и по обращению к successor отдавать именно потомка или самого предка, если к потомку он не относится. Конечно же возможность должна быть опциональной, так как это добавит оверхед к данным.
-
Не, несовпадение — это другое. Оно означает, что в целом есть принципиальная разница между реляционной моделью и объектной, и она приводит ко всяким неудобствам, вроде вот этого. Конечно, этот конкретный случай можно решить — например, да, хранением поля с типом. Но я бы подумал, а стоит ли вообще этим заниматься, потому что это выглядит как-то излишне сложно.
-
Зато удобно, кому то может пригодится. Может быть попробую в свободное время сделать патч к джанге.
-
Проблема в том, что у одного класса потомков то может быть несколько.
Я сейчас как раз потихоньку пишу специальный blog application, в котором используется наследование для создания постов разного типа. И для рендеринга придется решать проблему вызова метода потомка.
Пока что придумал такое решение в виде декоратора:
def virtual(func): '''Find a child object and call method against it instead os original 'func'. Apply this decorator to any method of the base class. ''' def wrap(self, *args, **kwargs): if getattr(self, '_child_object', None) is None: sub_objects = CollectedObjects() self._collect_sub_objects(sub_objects) setattr(self, '_child_object', sub_objects.items()[0][1].values()[0]) child_method = getattr(self._child_object, func.__name__) if getattr(child_method, '_is_virtual_wrap', False): return func(self, *args, **kwargs) return child_method(*args, **kwargs) wrap.__doc__ = func.__doc__ wrap.__name__ = func.__name__ setattr(wrap, '_is_virtual_wrap', True) return wrap -
а нельзя в родителе записывать свое имя (т.е. имя родителя) и после наследования (уже в потомке) в __init__ его менять?
хотя не понятно, что делать при множественном наследовании, но это изврат: в моделях джанги использоваться множественное наследование.



