Почему метод create в моем пользовательском менеджере не работает?

Я пытаюсь создать пользовательский менеджер для класса model в моем проекте Django.

Классы моделей Story и Plot имеют взаимно однозначную связь. Я хочу, чтобы новый экземпляр Plot создавался автоматически при создании нового экземпляра Story. Прямо сейчас я делаю это с помощью функции просмотра, но при тестировании создания объекта Story без функции просмотра это не удается.

Я знаю, что есть несколько способов добиться этого, но я решил использовать пользовательский менеджер, чтобы добавить эту логику после создания, переопределив метод objects.create. Соответствующий код приведен ниже:


class Story(models.Model):
... other fields
    objects = models.Manager()


class Plot(models.Model):
    ... other fields
    story = models.OneToOneField(Story, on_delete=models.CASCADE, null=True, default=None, related_name='plot')


class StoryManager(models.Manager):
    def create(self, **kwargs):
        story = super().create(**kwargs)
        plot = Plot.objects.create(
            ... other kwargs,
            story_id=story.id
        )
        return story


# Assign the story manager to the Story model
Story.objects = StoryManager()

Теперь я получаю следующую обратную трассировку при выполнении тестов, использующих метод Story.objects.create:


File "C:\Users\jacob\documents\programming\story-builder\storybuilder\app\models.py", line 221, in create
    story = super().create(**kwargs)
  File "C:\Users\jacob\documents\programming\story-builder\.venv\Lib\site-packages\django\db\models\manager.py", line 87, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "C:\Users\jacob\documents\programming\story-builder\.venv\Lib\site-packages\django\db\models\query.py", line 669, in create
    self.model._meta._reverse_one_to_one_field_names
    ^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute '_meta'

Я новичок в концепции ORM Django и в том, как она работает под капотом, поэтому, возможно, это даже не рекомендуемый способ автоматического создания объекта при создании другого объекта. В своих исследованиях, проведенных до сих пор, я встречал рекомендации по переопределению метода сохранения первой модели, что нецелесообразно, поскольку метод сохранения определен перед соответствующим классом модели.

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

Но о чем вы думаете?

Я полагаю, что это из-за вашего странного исправления атрибута objects. Он должен использовать диспетчер по умолчанию, пока не будет исправлен. Если сделать это правильно, это просто сработает:

class StoryManager(models.Manager):
    def create(self, **kwargs):
        story = super().create(**kwargs)
        Plot.objects.create(story_id=story.id)
        return story


class Story(models.Model):
    objects = StoryManager()


class Plot(models.Model):
    story = models.OneToOneField(
        Story, on_delete=models.CASCADE, null=True, default=None, related_name="plot"
    )

Я полагаю, что это из-за вашего странного исправления атрибута objects. Он должен использовать диспетчер по умолчанию, пока не будет исправлен. Если сделать это правильно, это просто сработает:

class StoryManager(models.Manager):
    def create(self, **kwargs):
        story = super().create(**kwargs)
        Plot.objects.create(story_id=story.id)
        return story


class Story(models.Model):
    objects = StoryManager()


class Plot(models.Model):
    story = models.OneToOneField(
        Story, on_delete=models.CASCADE, null=True, default=None, related_name="plot"
    )

tests.py

class TestStory(TestCase):
    def test_story(self):
        story = Story.objects.create()
        self.assertEqual(story.plot.story, story)
Вернуться на верх