Dan Schien
Dan Schien

Reputation: 1412

Factor boy same model property instance on parent and related factory

I am sharing my solution to my own problem to which I realised the solution while typing the question... it may be useful to others.

In my Django models I have a Gateway that has a set of Nodes with and they both should have the same Vendor.

class Vendor(models.Model):
    name = models.CharField(max_length=80, null=False)

class Gateway(models.Model):
    vendor = models.ForeignKey(Vendor, related_name='gateways')

class Node(models.Model):
    gateway = models.ForeignKey(Gateway, related_name='nodes')
    vendor = models.ForeignKey(Vendor, related_name='nodes')

Now I would like to generate fake data with factory boy. Here the VendorFactory and NodeFactory.

class VendorFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Vendor

    name = 'test_vendor'


class NodeFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Node

    # is created after this node instance
    vendor = factory.SubFactory(VendorFactory)

That works fine when creating the node = NodeFactory().

The problem is with the GatewayFactory. What I would like is that gateway = GatewayFactory() creates a Gateway that is referenced by a Node instance and both the Gateway and the Node using the same Vendor.

A tried a few things but failed. Here one shot:

class GatewayFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Gateway

    node = factory.RelatedFactory(NodeFactory, 'gateway', vendor=factory.LazyAttribute(lambda o: o.vendor))
    vendor = factory.SubFactory(VendorFactory)

The problem is that the RelatedFactory is evaluated after the GatewayFactory and a LazyAttribute or a SelfAttribute are evaluated in the context of the NodeFactory.

Upvotes: 4

Views: 810

Answers (1)

Dan Schien
Dan Schien

Reputation: 1412

The solution is in the fact that "the RelatedFactory is evaluated after the GatewayFactory and a LazyAttribute or a SelfAttribute are evaluated in the context of the NodeFactory"

Thus, we can reference the parent to get the vendor attribute:

class GatewayFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Gateway

    node = factory.RelatedFactory(NodeFactory, 'gateway', vendor=factory.SelfAttribute('gateway.vendor'))
    vendor = factory.SubFactory(VendorFactory)

This works for the LazyAttribute as well:

class GatewayFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Gateway

    node = factory.RelatedFactory(NodeFactory, 'gateway', vendor=factory.LazyAttribute(lambda o: o.gateway.vendor))
    vendor = factory.SubFactory(VendorFactory)

Upvotes: 2

Related Questions