#
# models.py
#
from django.db import connection, models
class PrefetchIDMixin(object):
def prefetch_id(self):
#
cursor = connection.cursor()
cursor.execute(
"SELECT nextval('{0}_{1}_{2}_seq'::regclass)".format(
self._meta.app_label.lower(),
self._meta.object_name.lower(),
self._meta.pk.name,
)
)
row = cursor.fetchone()
cursor.close()
self.pk = row[0]
class Master(PrefetchIDMixin, models.Model):
name = models.CharField(max_length=20)
main_thing = models.OneToOneField('Detail', related_name='main_thing_of')
class Detail(models.Model):
name = models.CharField(max_length=20)
master = models.ForeignKey('Master')
def __unicode__(self): return self.name
#
# tests.py
#
from django.test import TestCase
from django.db import transaction
from .models import Master, Detail
class CircularReference(TestCase):
def test_adding_with_circular_reference(self):
# Postgres will defer validation of foreign key constraints
# until the end of the transaction
with transaction.atomic():
m = Master(name='Zardoz')
# NOT NULL constraints can't be defered in postgres, so
# foreign key fields need to be populated beforehand
#
m.prefetch_id()
Detail.objects.create(master=m, name='gun')
m.main_thing = Detail.objects.create(master=m, name='Zed')
m.save()
m = Master.objects.get()
self.assertQuerysetEqual(
m.detail_set.order_by('name').all(),
['', ''])
self.assertEqual(m.main_thing, Detail.objects.get(name='Zed'))