Skip to content

Commit

Permalink
Clarify .build() issue with Django>1.8 (Ref #198).
Browse files Browse the repository at this point in the history
From 1.8 onwards, this crashes:

>>> a = MyModel()  # Don't save
>>> b = MyOtherModel(fkey_to_mymodel=a)

In turn, it breaks:

    class MyModelFactory(factory.django.DjangoModelFactory):
        class Meta:
            model = MyModel

    class MyOtherModelFactory(factory.django.DjangoModelFactory):
        class Meta:
            model = MyOtherModel
        fkey_to_mymodel = factory.SubFactory(MyModelFactory)

    MyOtherModelFactory.build()  # Breaks

The error message is: Cannot assign "MyModel()": "MyModel" instance isn't saved in the database.

See https://code.djangoproject.com/ticket/10811 for details.
  • Loading branch information
rbarrois committed Apr 25, 2015
1 parent 9b8ad9b commit 0e3cdff
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 0 deletions.
8 changes: 8 additions & 0 deletions docs/orms.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ All factories for a Django :class:`~django.db.models.Model` should use the
once all post-generation hooks have run.


.. note:: Starting with Django 1.8, it is no longer possible to call ``.build()``
on a factory if this factory uses a :class:`~factory.SubFactory` pointing
to another model: Django refuses to set a :class:`~djang.db.models.ForeignKey`
to an unsaved :class:`~django.db.models.Model` instance.

See https://code.djangoproject.com/ticket/10811 for details.


.. class:: DjangoOptions(factory.base.FactoryOptions)

The ``class Meta`` on a :class:`~DjangoModelFactory` supports extra parameters:
Expand Down
9 changes: 9 additions & 0 deletions tests/djapp/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ class StandardSon(StandardModel):
pass


class PointedModel(models.Model):
foo = models.CharField(max_length=20)


class PointingModel(models.Model):
foo = models.CharField(max_length=20)
pointed = models.OneToOneField(PointedModel, related_name='pointer', null=True)


WITHFILE_UPLOAD_TO = 'django'
WITHFILE_UPLOAD_DIR = os.path.join(settings.MEDIA_ROOT, WITHFILE_UPLOAD_TO)

Expand Down
26 changes: 26 additions & 0 deletions tests/test_django.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,32 @@ def test_auto_sequence(self):
self.assertEqual(1, obj.pk)


@unittest.skipIf(django is None, "Django not installed.")
class DjangoRelatedFieldTestCase(django_test.TestCase):

@classmethod
def setUpClass(cls):
super(DjangoRelatedFieldTestCase, cls).setUpClass()
class PointedFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.PointedModel
foo = 'ahah'

class PointerFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.PointingModel
pointed = factory.SubFactory(PointedFactory, foo='hihi')
foo = 'bar'

cls.PointedFactory = PointedFactory
cls.PointerFactory = PointerFactory

def test_direct_related_create(self):
ptr = self.PointerFactory()
self.assertEqual('hihi', ptr.pointed.foo)
self.assertEqual(ptr.pointed, models.PointedModel.objects.get())


@unittest.skipIf(django is None, "Django not installed.")
class DjangoFileFieldTestCase(unittest.TestCase):

Expand Down

0 comments on commit 0e3cdff

Please sign in to comment.