I've got a number of unmanaged models that I'm trying to develop some factories for so I can get some tests put together. The issue is that on a couple of them, they have db_column
names and that is throwing an error for me in the factory.
My models look like this:
class Identity(models.Model):
id = models.IntegerField(db_column="identityID", primary_key=True)
person_id = models.IntegerField(db_column="personID")
birth_date = models.DateField(db_column="birthdate")
gender = models.CharField(db_column="gender", max_length=1)
class Meta(object):
# This is 'True' when testing so it's treated like a normal model
managed = getattr(settings, "UNDER_TEST", False)
db_table = "identity"
class Person(models.Model):
id = models.IntegerField(db_column="personID", primary_key=True)
identity = models.ForeignKey(
Identity, db_column="currentIdentityID", on_delete=models.PROTECT
)
ticket_number = models.IntegerField(db_column="ticketNumber")
class Meta(object):
# This is 'True' when testing so it's treated like a normal model
managed = getattr(settings, "UNDER_TEST", False)
db_table = "person"
class YearTerm(models.Model):
active = models.BooleanField(default=False)
name = models.CharField(max_length=50)
created_by = models.ForeignKey(
Person, on_delete=models.PROTECT
)
class Meta:
# This is 'True' when testing so it's treated like a normal model
managed = getattr(settings, "UNDER_TEST", False)
db_table = "[ALTS].[yearterm]"
My factories look like this:
class IdentityFactory(factory.django.DjangoModelFactory):
class Meta:
model = Identity
@classmethod
def _setup_next_sequence(cls):
try:
return Identity.objects.latest("id").id 1
except Identity.DoesNotExist:
return 1
id = factory.Sequence(lambda n: n)
person_id = factory.Sequence(lambda n: n)
birth_date = factory.fuzzy.FuzzyDateTime(timezone.now())
gender = factory.Faker("random_element", elements=[x[0] for x in GENDERS])
class PersonFactory(factory.django.DjangoModelFactory):
class Meta:
model = Person
@classmethod
def _setup_next_sequence(cls):
try:
return Person.objects.latest("id").id 1
except Person.DoesNotExist:
return 1
id = factory.Sequence(lambda n: n)
identity = factory.RelatedFactory(
IdentityFactory,
person_id=factory.SelfAttribute("..id"),
)
ticket_number = factory.Faker("random_int", min=1000, max=40000)
class YearTermFactory(factory.django.DjangoModelFactory):
class Meta:
model = YearTerm
django_get_or_create = ("name",)
active = Iterator([True, False])
name = FuzzyChoice(["Occasionally", "Sometimes", "Always"])
created_by = SubFactory(PersonFactory)
My test case is extremely simple:
class TestCaseYearTerm(TestCase):
def test_create(self):
"""
Test the creation of a YearTerm model using a factory
"""
year_term = YearTermFactory.create()
self.assertEqual(YearTerm.objects.count(), 1)
But I get the following error:
django.db.utils.IntegrityError: NOT NULL constraint failed: person.currentIdentityID
I feel like this is because I specify the db_column
name in the model, but I'm not sure how to fix that in FactoryBoy or to get factory boy to add a specific name to the factories attributes when creating them.
Any help would be appreciated!
CodePudding user response:
The models can be improved. OP has Person
and Identity
models and wants that one Person
corresponds to only one Identity
(like a User / Person Profile). OP can achieve that with a OneToOneField. So, one can edit OP's Identity model to
class Identity(models.Model):
person = models.OneToOneField(Person, on_delete=models.CASCADE)
Note that I'm not using person_id
. Using _id in Django Model Fields for OneToOneField and Foreign is an anti-pattern. Also, in OP's Person
model, delete the identity field since it's not needed anymore. OP will need to run makemigrations
after this.
Then, OP's factories will get simpler
import factory
from factory import SubFactory
from factory.django import DjangoModelFactory
class IdentityFactory(DjangoModelFactory):
person = SubFactory(PersonFactory)
class Meta:
model = Identity
Don't forget to remove identity from PersonFactory
as well.
If OP wants to keep things as they are, without many model changes, something I would advise against (but then again it depends on the goals/context), then OP can just add database='special_db'
to the factories Meta
, like OP mentioned here.