Factory Boy subfactory over 'self'
Asked Answered
A

3

8

Does anyone know how to create the factory in factoryboy based on this models.py

class Halte(models.Model):

    koppel_halte1 = models.ForeignKey('self',
                                  related_name='koppel_halteA',
                                  verbose_name="Koppel Halte",
                                  help_text="geef hier een gekoppelde halte aan",
                                  null=True, blank=True)

    koppel_halte2 = models.ForeignKey('self',
                                  related_name='koppel_halteB',
                                  verbose_name="Koppel Halte",
                                  help_text="geef hier een gekoppelde halte aan",
                                  null=True, blank=True)

Notice the 'self'? (And YES this type of relation is necesarry.)

I have tried several things in FactoryBoy (SubFactory, RelatedFactory, SelfAtribute, PostGeneration) but I can't get it to work.

one of the attempts in a factories.py

class HalteFactoryA(factory.DjangoModelFactory):
    class Meta:
        model = models.Halte


class HalteFactoryB(factory.DjangoModelFactory):
    class Meta:
        model = models.Halte


class HalteFactory(factory.DjangoModelFactory):
    class Meta:
        model = models.Halte
    # todo: how to do this?? (see models.Halte)
    koppel_halte1 = factory.RelatedFactory(HalteFactoryA)
    koppel_halte2 = factory.RelatedFactory(HalteFactoryB)

Any advice?

Thank you.

Ankle answered 5/2, 2016 at 15:32 Comment(0)
S
11

@bakkal has it mostly right, but an important missing factor is that a target recursion depth must be specified, as stated in this issue: https://github.com/rbarrois/factory_boy/issues/173

# myproj/myapp/factories.py
class MyModelFactory(factory.Factory):
    class Meta:
        model = models.MyModel
    parent = factory.SubFactory('myapp.factories.MyModelFactory')

Then a recursion max depth needs to be added, or you get the infinite depth reached error (as noted by @Sjoerd van Poelgeest in the comments):

m = MyModelFactory(parent__parent__parent__parent=None)

In This case we are allowing a depth of 3 being created, and the last one will have a null parent.

Specialty answered 3/8, 2016 at 16:51 Comment(0)
N
1

Fully qualified model name in the model FK

To make it easier on the tools to introspect your model, instead of 'self', use the fully qualified model name:

koppel_halte1 = models.ForeignKey('yourapp.Halte', ...)
koppel_halte2 = models.ForeignKey('yourapp.Halte', ...)

Notice it's a string 'yourapp.Halte' and not yourapp.Halte.

Fully qualified factory name in the SubFactory

If you insist on using 'self' in the model you can use the fully qualified model name in your SubFactory

# yourapp/factories.py

class HalteFactory(factory.Factory):
    class Meta:
        model = yourapp.Halte

    koppel_halte1 = factory.SubFactory('yourapp.factories.HalteFactory')
    koppel_halte2 = factory.SubFactory('yourapp.factories.HalteFactory')
Nasal answered 5/2, 2016 at 15:34 Comment(4)
This might work, but how to create a factory boy instance from this. I'll update the question with a try of my own to clarifyAnkle
I may have updated the answer before your comment, if you run into issues take note of the snippet and the error and we can discussNasal
hmm.. that just crashed all tests with a maximum recursion depth (infinite loop)... I'm off for now. I'll try again next week.Ankle
Doing this alone will cause a maximum recursion depth error, according to this issue (github.com/rbarrois/factory_boy/issues/173), you need to create a cutoff target depth.Specialty
A
0

I failed to get it fixed with Factory Boy, but the old fashioned setup did work.

Then I'll (reluctantly) use the standard.

    class ModelHalteSelfTests(TestCase):
    def setUp(self):
        self.lijn1 = Lijn.objects.create(id=1, nummer=1, techniek=GlobalWaardes.TECHNIEK_BUS)
        self.lijn2 = Lijn.objects.create(id=2, nummer=2, techniek=GlobalWaardes.TECHNIEK_TRAM)

        self.halte1 = Halte.objects.create(id=1, nummer=100, aantal_vitrinekasten=2, aantal_hpkasten=0)
        self.halte2 = Halte.objects.create(id=2, nummer=200, aantal_vitrinekasten=1, aantal_hpkasten=1)
        self.halte3 = Halte.objects.create(id=3, nummer=300, aantal_vitrinekasten=0, aantal_hpkasten=3)
        self.halte4 = Halte.objects.create(id=4, koppel_halte1=self.halte1, koppel_halte2=self.halte2)

        self.halteregel1 = Halteregel.objects.create(id=1, lijn=self.lijn1, halte=self.halte1, volgorde=10)
        self.halteregel2 = Halteregel.objects.create(id=2, lijn=self.lijn1, halte=self.halte2, volgorde=20)
        self.halteregel3 = Halteregel.objects.create(id=3, lijn=self.lijn2, halte=self.halte2, volgorde=20)
        self.halteregel4 = Halteregel.objects.create(id=4, lijn=self.lijn2, halte=self.halte3, volgorde=10)

    def test_lijst_halteregels(self):
        self.assertEqual(self.halte1.lijst_halteregels(), [self.halteregel1])
        self.assertEqual(self.halte2.lijst_halteregels(), [self.halteregel2, self.halteregel3])
        self.assertEqual(self.halte3.lijst_halteregels(), [self.halteregel4])
        self.assertEqual(self.halte4.lijst_halteregels(), [self.halteregel1, self.halteregel2, self.halteregel3])
Ankle answered 11/2, 2016 at 10:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.