2009-10-30 2 views
39

У меня две модели в разных приложениях: modelA и modelB. У них есть отношения «один к одному». Есть ли способ, которым django может автоматически создавать и сохранять ModelB при сохранении modelA?Может ли Django автоматически создать связанную модель «один-к-одному»?

class ModelA(models.Model): 
    name = models.CharField(max_length=30) 

class ModelB(models.Model): 
    thing = models.OneToOneField(ModelA, primary_key=True) 
    num_widgets = IntegerField(default=0) 

Когда я сохраняю новую модель, я хочу, чтобы она автоматически сохранялась в ModelB. Как я могу это сделать? Есть ли способ указать это в ModelA? Или это невозможно, и мне просто нужно создать и сохранить ModelB в представлении?

Отредактировано, что модели находятся в разных приложениях.

+1

Возможный дубликат [Создать экземпляр OneToOne для создания модели] (http://stackoverflow.com/questions/5608001/create-onetoone-instance-on-model-creation) –

ответ

0

Вы можете использовать post_save-hook, который запускается после сохранения записи. Дополнительную документацию по сигналам django см. В разделе here. На странице this page вы найдете пример того, как применить крючок к вашей модели.

13

Самый простой способ заключается в override the save method из Modela:

class ModelA(models.Model): 
    name = models.CharField(max_length=30) 

    def save(self, force_insert=False, force_update=False): 
     is_new = self.id is None 
     super(ModelA, self).save(force_insert, force_update) 
     if is_new: 
      ModelB.objects.create(thing=self) 
+0

Проблема с этим способом, к сожалению, прерывается, если у вас есть встроенная форма в админе и использовать ее для создания экземпляра ModelB одновременно - она ​​попытается создать два ModelB и умереть ужасно. –

+0

Да, но я бы подумал, что это взломать. – Dmitry

+3

Можете быть более надежными в будущем, не назвав args супер. Я предлагаю редактировать. – hobs

36

Посмотрите на AutoOneToOneField в django-annoying. Из документов:

from annoying.fields import AutoOneToOneField 

class MyProfile(models.Model): 
    user = AutoOneToOneField(User, primary_key=True) 
    home_page = models.URLField(max_length=255) 
    icq = models.CharField(max_length=255) 

(Джанго-раздражает большая небольшая библиотека, которая включает в себя драгоценные камни, как в render_to декоратора и get_object_or_None и функции Get_Config)

+0

Стоит отметить, что создание нового пользователя на панели администратора не сразу создаст MyProfile. Он создается ленивым способом (первый раз, когда вы действительно обращаетесь к этому объекту профиля). – BUZZY

1

Я думаю, что вы хотите использовать django's model inheritance. Это полезно, если вы выполняете следующее утверждение: ModelA - это ModelB (например, «Ресторан - это местоположение»).

Вы можете определить:

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

class ModelA(ModelB): field2 = models.CharField(...)

Now you can create an instance of ModelA and set field2 and field1. If this model is saved it will also create an instance of ModelB which gets the value of field1 assigned. This is all done transparently behind the scenes.

Though you can do the following:

a1 = ModelA() 
a1.field1 = "foo" 
a1.field2 = "bar" 
a1.save() 
a2 = ModelA.objects.get(id=a1.id) 
a2.field1 == "foo" # is True 
a2.field2 == "bar" # is True 
b1 = ModelB.objects.get(id=a1.id) 
b1.field1 == "foo" # is True 
# b1.field2 is not defined

0

Просто создать функцию, которая создает и возвращает пустую Modela и установить именованный аргумент по умолчанию на «вещь» для этой функции.

23

Как указано в m000, ваши модели существуют в разных приложениях. Часто вы используете приложения, которые вы не пишете, поэтому для обеспечения обновлений вам нужен развязанный способ создания логически связанных моделей. Это предпочтительное решение, на мой взгляд, и мы используем его в очень большом проекте.

с помощью сигналов:

В вашем models.py:

from django.db.models import signals 


def create_model_b(sender, instance, created, **kwargs): 
    """Create ModelB for every new ModelA.""" 
    if created: 
     ModelB.objects.create(thing=instance) 

signals.post_save.connect(create_model_b, sender=ModelA, weak=False, 
          dispatch_uid='models.create_model_b') 

Вы можете создать отдельное приложение, чтобы держать этот models.py файл, если оба приложения находятся вне-полки.

+1

+1 для этого. Уловка в том, что модели принадлежат к разным приложениям. Это соответствует используемому случаю для сигналов: «разрешить отключение приложений получать уведомления, когда действия происходят в другом месте в рамках». Другие предлагаемые решения работают, но вносят ненужную зависимость A-> B, по существу объединяющую два приложения. Сигналы позволяют A оставаться развязанными от B. – m000

+0

@ m000 Спасибо за это! Если вы не возражаете, я обновлю описание своего решения, как вы его суммировали очень хорошо. – Dmitry

+0

Почему 'слабый = ложный'? –

1

Я знаю, что немного поздно, но я придумал более чистое и элегантное решение. Рассмотрим этот код:

class ModelA(models.Model): 
    name = models.CharField(max_length=30) 

    @classmethod 
    def get_new(cls): 
     return cls.objects.create().id 



class ModelB(models.Model): 
    thing = models.OneToOneField(ModelA, primary_key=True, default=ModelA.get_new) 
    num_widgets = IntegerField(default=0) 

Конечно, вы можете использовать лямбда-а, до тех пор, как вы вернуть целое число идентификатор связанного объекта :)

0

Я собрал несколько разных ответов (потому что ни один из них не работал прямо из коробки для меня) и придумал это. Думал, что это довольно чисто, поэтому я делюсь этим.

from django.db.models.signals import post_save 
from django.dispatch import receiver 

@receiver(post_save, sender=ModelA) 
def create_modelb(sender, instance, created, **kwargs): 
    if created: 
     if not hasattr(instance, 'modelb'): 
      ModelB.objects.create(thing=instance) 

Используется сигнал как @Dmitry.И как @ daniel-roseman прокомментировал ответ @ jarret-hardie, Django Admin иногда пытается создать связанный объект для вас (если вы измените значение по умолчанию в встроенной форме), на котором я столкнулся, таким образом, проверка hasattr. Хороший отзыв декоратора от ответа @ shadfc в Create OneToOne instance on model creation