Skip to content

Instantly share code, notes, and snippets.

@pstiasny
Created March 25, 2015 17:13
Show Gist options
  • Save pstiasny/daa836e44b5282ac0a68 to your computer and use it in GitHub Desktop.
Save pstiasny/daa836e44b5282ac0a68 to your computer and use it in GitHub Desktop.
Circular reference with Django ORM and Postgres without breaking NOT NULL FK constraints
from django.db import connection, models
class PrefetchIDMixin(object):
def prefetch_id(self):
# <https://djangosnippets.org/snippets/2731/>
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
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
# <http://postgresql.nabble.com/DEFERRABLE-NOT-NULL-constraint-tp5743655p5743779.html>
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(),
['<Detail: gun>', '<Detail: Zed>'])
self.assertEqual(m.main_thing, Detail.objects.get(name='Zed'))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment