访问时自动创建OneToOneField

1 投票
1 回答
1278 浏览
提问于 2025-04-17 19:43

我有两个模型:

class Profile(models.Model):
    # (...)
    settings = models.OneToOneField('Settings', null=True)
    # (...)

class Settings(model.Model):
    # (...)

有没有办法在每次访问Profile#settings的时候,自动创建一个Settings的实例呢?

更新:我想要这样做的主要原因是,我已经有了Profile模型,我希望在用户使用到需要Profile#setting实例的地方时,能够懒惰地创建Setting的关系,而不需要关心它是否已经被创建。

更新:@miki725在下面提到,这个问题Django已经处理好了。我试了以下代码:

class SettingsTest(TestCase):
    def setUp(self):
        user = User.objects.create(username='felipe', email='something@aol.com')
        self.profile = Profile.objects.create(short_name='Felipe', name='Felipe',
            user=user)

    def test_settings(self):
        self.assertIsNotNone(self.profile.settings)

然后我得到了这个结果:

Creating test database for alias 'default'...
F
======================================================================
FAIL: test_settings (activity.tests.settings.SettingsTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/vagrant/activity/tests/settings.py", line 20, in test_settings
    self.assertIsNotNone(self.profile.settings)
AssertionError: unexpectedly None

----------------------------------------------------------------------
Ran 1 test in 0.003s

1 个回答

1

Django会自动处理这个问题!

>>> p = Profile.objects.get(...)
>>> isinstance(p.settings, Settings)
True

更新

如果你得到的是None,这意味着在Profile模型中的外键值是空的,因为你允许空值null=True。在这种情况下,你其实没法做什么:

>>> p = Profile(settings=None, ...)
>>> p.save()
>>> p = Profile.objects.get(pk=p.pk)
>>> p.settings # the settings can't be anything because you never assigned them
None

如果你需要这个来进行测试,那么你需要给Profile实例分配一些设置值:

class SettingsTest(TestCase):
    def setUp(self):
        user = User.objects.create(username='felipe', email='something@aol.com')
        self.settings = Settings.objects.create(...)
        self.profile = Profile.objects.create(short_name='Felipe',
                                              name='Felipe',
                                              user=user,
                                              settings=self.settings)

顺便提一下,从Django 1.5开始,你可以配置你的用户模型。在此之前,你需要创建一个个人资料模型,并且这个模型必须与用户模型有一对一的关系。现在这已经不再必要了。你可以直接在用户模型中添加字段。你可以在Django 1.5的发布文档中了解更多信息(https://docs.djangoproject.com/en/dev/releases/1.5/#configurable-user-model)。

更新

现在你的动机更清楚了,你可以通过这种比较“hack”的方法来实现。我觉得这不是最好的方法,但应该能奏效:

Profile(models.Model):
    def get_default_settings(self):
        settings = Settings.objects.create(...)
        self.settings_id = settings.pk
        return settings

    def __getattribute__(self, name):
        default = object.__getattribute__(self, name)
        if name == 'settings':
            if default is None:
                return self.get_default_settings()
            else:
                return default
        else:
            return default

撰写回答