Skip to content

Allow functions as models#1072

Open
instanceofmel wants to merge 1 commit into
FactoryBoy:masterfrom
instanceofmel:allow-functions-as-models
Open

Allow functions as models#1072
instanceofmel wants to merge 1 commit into
FactoryBoy:masterfrom
instanceofmel:allow-functions-as-models

Conversation

@instanceofmel

Copy link
Copy Markdown

Hi,

in our project, we're using factories for our models:

# domain/factories.py
from . import model, events

def item_factory(sku: str, price: float):
    obj = model.Item(sku, price)
    obj.events.append(events.ItemCreated())
    return obj

Now, in our tests we want factory boy to call those factories instead of the model classes directly, which works perfectly except in this case:

# tests/factories.py
class ItemFactory(factory.Factory):
    class Meta:
        model = factories.item_factory
    sku = ...
    price = ...

class SpecialItemFactory(ItemFactory):  # Inherits from the factory above
    sku = "special"

When a factory inherits from another factory, FactoryOptions._get_counter_reference expects the assigned model to be a class, because issubclass raises:

TypeError: issubclass() arg 1 must be a class

We couldn't find a fix for this case so I've opened this PR. If my solution is wrong or if anything is missing in my commit, please don't hesitate to let me know.

BR

@rbarrois

rbarrois commented Apr 9, 2024

Copy link
Copy Markdown
Member

Hello,

Thanks for the feedback! That's indeed likely to be a bug.

Would it be possible to have a minimal example of code where this breaks, to add to our test suite?

NB: This is one of the reasons why I prefer to see an issue first, before jumping in the code — our issue templates make it easier for us to gather all details we need to move forward ;)

@instanceofmel

instanceofmel commented Apr 24, 2024

Copy link
Copy Markdown
Author

Hey, sorry for the late reply. You mean something like this?

import factory


class Item:
    def __init__(self, sku, price):
        self.sku = sku
        self.price = price


def item_factory(sku, price):
    obj = model.Item(sku, price)
    return obj


class ItemFactory(factory.Factory):
    class Meta:
        model = item_factory

    sku = "foo"
    price = "bar"


class SpecialItemFactory(ItemFactory):
    sku = "special"


item = SpecialItemFactory()  # TypeError: issubclass() arg 1 must be a class

Should I add a test case to the test suite?

BR

@rbarrois

Copy link
Copy Markdown
Member

Yes please! That's exactly what I had in mind ;)

@instanceofmel instanceofmel force-pushed the allow-functions-as-models branch 3 times, most recently from 6a0825f to 4712853 Compare April 26, 2024 08:29
@instanceofmel

Copy link
Copy Markdown
Author

Done @rbarrois

@instanceofmel

Copy link
Copy Markdown
Author

@rbarrois Could you please merge this when you have a chance? Thank you.

Facilitates the use of a factory function, that may create several
related objects at once.
@francoisfreitag francoisfreitag force-pushed the allow-functions-as-models branch from 4712853 to 01a864c Compare July 3, 2024 09:45
@instanceofmel

Copy link
Copy Markdown
Author

Hi @rbarrois, let me know if there's anything left to do for me in order to get this merged 🙏

@instanceofmel

instanceofmel commented May 22, 2025

Copy link
Copy Markdown
Author

Any news? @rbarrois

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants