Может ли Django автоматически создать связанную модель «один на один»? - PullRequest
43 голосов
/ 31 октября 2009

У меня есть две модели в разных приложениях: модель A и модель B. У них есть личные отношения. Есть ли способ, которым django может автоматически создавать и сохранять ModelB при сохранении modelA?

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)

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

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

Ответы [ 8 ]

40 голосов
/ 31 октября 2009

Взгляните на AutoOneToOneField в django-раздражает . Из документов:

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)

(django-annoying - отличная небольшая библиотека, которая включает гемы, такие как декоратор render_to и функции get_object_or_None и get_config)

28 голосов
/ 06 января 2012

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

Используя сигналы:

В твоих моделях.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, если оба приложения уже в продаже.

14 голосов
/ 31 октября 2009

Самый простой способ - переопределить метод сохранения для 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)
3 голосов
/ 01 сентября 2017

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

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)

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

1 голос
/ 01 февраля 2018

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

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 в Создание экземпляра OneToOne при создании модели

0 голосов
/ 06 марта 2010

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

0 голосов
/ 31 октября 2009

Я думаю, что вы хотите использовать наследование модели Джанго . Это полезно, если верно следующее утверждение: ModelA - это ModelB (например, Restaurant - это местоположение).

Вы можете определить: <pre>class ModelB(models.Model): field1 = models.CharField(...)</p> <p>class ModelA(ModelB): field2 = models.CharField(...)

Теперь вы можете создать экземпляр ModelA и установить field2 и field1. Если эта модель сохранена, она также создаст экземпляр ModelB, который получает назначенное значение field1. Все это делается прозрачно за кадром.

Хотя вы можете сделать следующее: 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 голосов
/ 31 октября 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...