Created
March 25, 2015 17:13
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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