访问时自动创建OneToOneField
我有两个模型:
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