2013-08-08 2 views
2

У меня есть модель Person, в которой хранятся все данные о людях. У меня также есть модель Client, которая расширяет Person. У меня есть еще одна расширяющаяся модель OtherPerson, которая также расширяет модель Person. Я хочу создать Клиент, указывающий на Person, и ТАКЖЕ создать запись OtherPerson, которая указывает на то, что Person. В принципе, я хочу, чтобы один объект Person рассматривался как Client и OtherPerson, в зависимости от текущего вида. Возможно ли это с ORM Django, или мне нужно как-то написать Raw-запрос для создания этого сценария. Я вполне уверен, что это возможно со стороны базы данных, потому что оба дочерних класса просто указывают на родительский класс Person со своим полем person_ptr_id.Django: Два разных дочерних класса указывают на один и тот же родительский класс

Проще говоря, если я создаю Client (и, таким образом, в Person), можно также создать OtherPerson объект, используя базу Person из Client. Таким образом, я могу рассматривать их как Client ИЛИ как OtherPerson, и сохранение одного будет влиять на поля Person каждого из них?

«Горизонтальный» полиморфизм?

Вот урезанная версия моих моделей в случае, уточняет:

class Person(models.Model): 
    """ 
     Any person in the system will have a standard set of details, fingerprint hit details, some clearances and items due, like TB Test. 
    """ 
    first_name = models.CharField(db_index=True, max_length=64, null=True, blank=True, help_text="First Name.") 
    middle_name = models.CharField(db_index=True, max_length=32, null=True, blank=True, help_text="Middle Name.") 
    last_name = models.CharField(db_index=True, max_length=64, null=True, blank=True, help_text="Last Name.") 
    alias = models.CharField(db_index=True, max_length=128, null=True, blank=True, help_text="Aliases.") 
    . 
    . 
    <some person methods like getPrintName, getAge, etc.> 

class Client(Person): 
    date_of_first_contact = models.DateField(null=True, blank=True) 
    . 
    . 
    <some client methods> 


class OtherPerson(Person): 
    active_date = models.DateField(null=True, blank=True) 
    termination_date = models.DateField(null=True, blank=True) 
    . 
    . 
    <some other person methods> 

ответ

2

Хорошо, я не хочу отвечать на свой вопрос, тем более, что это своего рода повторение (Django model inheritance: create sub-instance of existing instance (downcast)?

@Daniel Роземан меня из варенья СНОВА. Должен любить этого парня!

person = Person.objects.get(id=<my_person_id>) 
client = Client(person_ptr_id=person.id) 
client.__dict__.update(person.__dict__) 
client.save() 
other_person = OtherPerson(person_ptr_id=person.id) 
other_person.__dict__.update(person.__dict__) 
other_person.save() 

Если у меня есть существующий Client и хотите сделать OtherPerson из них, который мой точный потребительной случай, я просто делаю это:

client_id = <ID of Client/Person I want to create an OtherPerson with> 
p = Person.objects.get(id=client_id) 
o = OtherPerson(person_ptr_id=p.id) # Note Person.id and Client.id are the same. 
o.__dict__.update(p.__dict__) 
o.save() 

Теперь человек появляется в качестве клиента на экране клиента и в качестве другого пользователя на экране другого человека. Я могу получить версию OtherPerson Person, у которой есть все детали и функции OtherPerson, или я могу получить клиентскую версию этого Лица, которая имеет все данные и функции Клиента.

+0

Это пита, о которой я упомянул. В следующий раз не используйте подклассы, где OneToOneField будет работать лучше. –

+0

Почему не следует использовать наследование здесь? Клиент - это лицо, другое лицо - лицо. Для меня это выглядит как недостающая функция в Django. –

1

То, что вы делаете, это не возможно, как вы это делаете, Django имеет определенные правила наследования

Единственные возможные схема:

class Parent(models.Model): 
    class Meta: 
     abstract = True # MUST BE !!! This results in no relation generated in your DB 

    field0 = models.CharField(... 
    ... 

    # here you're allowed to put some functions and some fields here 


class Child(models.Model): 
    field1 = models.CharField(... 
    ... 

    # Anything you want, this model will create a relation in your database with field0, field1, ... 


class GrandChild(models.Model): 
    class Meta: 
     proxy = True # MUST BE !!! This results in no relation generated in your DB 

    # here you're not allowed to put DB fields, but you can override __init__ to change attributes of the fields: choices, default,... You also can add model methods. 

Это связано с тем, что в большинстве DBGS нет наследования БД. Таким образом, вам необходимо сделать родительский класс abstract!

+0

Обратите внимание, что у вас может быть много классов abastract (но не только наследует от 1-го метода, найденного слева направо), многие классы Child (и, следовательно, множество таблиц в вашей БД) и многие прокси (и, следовательно, многие админы, ...) – Ricola3D

+0

Спасибо за ответ, Ricola3D. К сожалению, родительская таблица 'Person' уже существует, поэтому я не могу перейти к абстрактной таблице. Как вы знаете, я могу взять объект Person, который существует, и использовать их как супер для нового дочернего объекта «Клиент»? – Furbeenator

+0

ли ваша клиентская модель нуждается в том, чтобы хранить дополнительные поля в базе данных? – Ricola3D

0

Вы не можете сделать это с помощью подкласса. Когда вы подкласса Person, вы неявно говорите Django, что будете создавать подклассы, а не Person объектов. Это PITA, чтобы взять Person и трансформировать его в OtherPerson позже.

Вместо этого вы, скорее всего, захотите использовать OneToOneField. Оба Client и OtherPerson должны быть подклассами models.Model:

class Client(models.Model): 
    person = models.OneToOneField(Person, related_name="client") 
    # ... 

class OtherPerson(models.Model): 
    person = models.OneToOneField(Person, related_name="other_person") 
    # ... 

Затем вы можете сделать что-то вроде:

pers = Person(...) 
pers.save() 
client = Client(person=pers, ...) 
client.save() 
other = OtherPerson(person=pers, ...) 
other.save() 

pers.other.termination_date = datetime.now() 
pers.other.save() 

См https://docs.djangoproject.com/en/dev/topics/db/examples/one_to_one/ больше.

+0

Мне очень нравится идея экстраполяции O2O на подклассы, к сожалению, у меня уже есть сотни объектов Person, которые уже созданы. Я нашел предложенный патч для создания дочерних объектов с родительским классом, определенным в конструкторе (https://code.djangoproject.com/ticket/7623). Это именно то, что я хочу сделать, но мне 5 лет, поэтому я не уверен, что это сработает. Спасибо за ваш ответ, это было бы прекрасно для будущих реализаций и было бы полезно знать; эта парадигма определенно превосходит то, что у меня есть. Надеюсь, я смогу найти способ обойти мой ранее созданный сценарий. – Furbeenator

+0

Ну, если ты застрял, ты застрял. FYI, я раньше конвертировал вещи из подкласса в не-подкласс, а южная миграция трудно понять. –

Смежные вопросы