New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DjangoModelFactory's "_setup_next_sequence" assumes that pk is an integer #57
Comments
This part of the code hasn't meaningfully changed since 1.3.0, where it was: @classmethod
def _setup_next_sequence(cls):
"""Compute the next available PK, based on the 'pk' database field."""
try:
return 1 + cls._associated_class._default_manager.values_list('pk', flat=True
).order_by('-pk')[0]
except IndexError:
return 1 The v2.0.0 changed the default type of If your pk is non-numeric, the supported way of handling this case is to override the class FooFactory(factory.DjangoModelFactory):
FACTORY_FOR = models.Foo
@classmethod
def _setup_next_sequence(cls):
return 1 |
I ran into this problem too. Many of my Django models have non-integer PKs. Eventually I just monkey-patched it to catch the additional exception # Patch DjangoModelFactory to avoid TypeError if PK isn't an IntegerField
@classmethod
def _setup_next_sequence(cls):
"""Compute the next available PK, based on the 'pk' database field."""
model = cls._associated_class # pylint: disable=E1101
manager = cls._get_manager(model)
try:
return 1 + manager.values_list('pk', flat=True
).order_by('-pk')[0]
except (IndexError, TypeError):
return 1
factory.DjangoModelFactory._setup_next_sequence = _setup_next_sequence
del _setup_next_sequence However now that I think more about it, the code could be a bit safer by separating the exception handling. Something like this (untested): @classmethod
def _setup_next_sequence(cls):
"""Compute the next available PK, based on the 'pk' database field."""
model = cls._associated_class # pylint: disable=E1101
manager = cls._get_manager(model)
try:
last_key = manager.values_list('pk', flat=True
).order_by('-pk')[0]
except IndexError:
return 1
if isinstance(last_key, int):
return last_key + 1
return 1 |
@agriffis That's indeed a great idea, and should work much better for all uses. |
Fixed in 83461f0. |
Why did |
What do you mean, got deleted? It is still on master: Lines 436 to 443 in ec4211a
|
Yeah, it is in the base class, but the
|
I still don’t understand the issue. DjangoModelFactory inherits from Python 3.9.1 (default, Feb 6 2021, 06:49:13)
[GCC 10.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from factory.django import DjangoModelFactory
>>> DjangoModelFactory._setup_next_sequence
<bound method BaseFactory._setup_next_sequence of <class 'factory.django.DjangoModelFactory'>> It should be possible to override it in subclasses. A quick edit in the test suite confirms that. diff --git a/tests/test_django.py b/tests/test_django.py
index ad68610..e7285e7 100644
--- a/tests/test_django.py
+++ b/tests/test_django.py
@@ -49,7 +49,13 @@ class StandardFactory(factory.django.DjangoModelFactory):
foo = factory.Sequence(lambda n: "foo%d" % n)
-class StandardFactoryWithPKField(factory.django.DjangoModelFactory):
+class DjangoModelFactory(factory.django.DjangoModelFactory):
+ @classmethod
+ def _setup_next_sequence(cls):
+ breakpoint()
+ return 12
+
+class StandardFactoryWithPKField(DjangoModelFactory):
class Meta:
model = models.StandardModel
django_get_or_create = ('pk',) |
Issue is when you have already some data in the database and use factories to insert fixtures, then the ids and keys need to not to start with the 0 included. |
Thanks for precising the use case. |
|
This does not call |
Actually, even the provided example does not fail for me, here’s the patch I’m using: diff --git a/tests/test_django.py b/tests/test_django.py
index ad68610..97f083e 100644
--- a/tests/test_django.py
+++ b/tests/test_django.py
@@ -1015,3 +1015,15 @@ class DjangoCustomManagerTestCase(django_test.TestCase):
# Our CustomManager will remove the 'arg=' argument,
# invalid for the actual model.
ObjFactory.create(arg='invalid')
+
+ def test_57(self):
+ class MyFactory(factory.django.DjangoModelFactory):
+ class Meta:
+ model = models.StandardModel
+
+ foo = factory.Sequence(lambda n: "foo%d" % n)
+
+ standard = models.StandardModel.objects.create(pk=0, foo="4")
+ created_by_factory = MyFactory.create()
+ self.assertEqual(standard.pk, 0)
+ self.assertEqual(created_by_factory.pk, 1) |
Sorry for bad example. I would like the following test to pass.
Currently it fails with
Basically the starting number of the sequence should be setup according to the database state. Do you think it makes any sense? |
No :) The goal of If you wish to have it start at a given value, why don't you use However, I strongly suggest letting your database generate the PK itself — otherwise, it is your job to define how your factory can choose values that are guaranteed not to conflict with those already existing in the DB. |
Thanks. After all I think like this is specific to the use case and everybody should write his own code. |
Offending code is here:
This problem didn't exist in factory_boy 1.3. My field that was using a non-integer PK is using a sequence, which worked fine previously:
I haven't dug into the code enough to know much about the changes that caused this problem, but ultimately I'd like to be able to use the sequence for the field again.
The text was updated successfully, but these errors were encountered: