Skip to content
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

FK to existing object is ignored, SubFactory is used instead #571

Closed
grokcode opened this issue Mar 18, 2019 · 2 comments
Closed

FK to existing object is ignored, SubFactory is used instead #571

grokcode opened this issue Mar 18, 2019 · 2 comments

Comments

@grokcode
Copy link

Description

When using SQLAlchemyModelFactory, if a foreign key to an existing database record is provided, the FK is ignored, a new record is created via the SubFactory, and the provided FK is overwritten with the FK of the record created via the SubFactory.

To Reproduce

Using the below factories and models, AddressFactory() works as expected, creating a new Address and a Person via the SubFactory and committing them to the db.

However, AddressFactory(person_id=1), where 1 is the id of a person that already exists in the db, does not work as I was expecting it to. I'm thinking that in this case, an Address should be created with a foreign key to the existing Person. Instead, a new Address is created, a new Person is created via the SubFactory, and the Address is set to use the new Person.

Model / Factory code
## factories.py
from factory.alchemy import SQLAlchemyModelFactory
from factory import Faker, SubFactory


class PersonFactory(SQLAlchemyModelFactory):
    class Meta:
        sqlalchemy_session = db.session
        sqlalchemy_session_persistence = 'commit'
        model = models.Person

    first_name = Faker('first_name')


class AddressFactory(SQLAlchemyModelFactory):
    class Meta:
        sqlalchemy_session = db.session
        sqlalchemy_session_persistence = 'commit'
        model = models.Address

    person = SubFactory(PersonFactory)


## models.py
from sqlalchemy import Column, String, Integer, ForeignKey
from sqlalchemy.orm import relationship


class Person:
    id = Column(Integer, primary_key=True)
    first_name = Column(String(25))

    addresses = relationship('Address', backref="person")

class Address:
    person_id = Column(Integer, ForeignKey('person.id', ondelete='CASCADE'))

Notes

Possibly related to #463?

@rbarrois
Copy link
Member

It is kinda related to #463 and #69; however, in your declaration, there is no way for factory_boy to know that a relation exists between person and person_id.
When you call AddressFactory(person_id=1), you end up with:

person = Person.create(**params)
Address.create(
    person=person,
    person_id=1,
)

If you want some kind of get_or_create behaviour, we'd need to add it to SQLAlchemyFactory; see [the code in DjangoModelFactoryfor inspiration](https://github.com/FactoryBoy/factory_boy/blob/master/factory/django.py#L135-L166). If you did add this toSQLAlchemyFactory(or a custom subclass of it) and declared theidfield to be part of saidget_or_create``, you would get the expected behaviour :-)

@francoisfreitag
Copy link
Member

Closing due to inactivity.

sqlalchemy_get_or_create was implemented in 59ee658 and is now being superseded by #794. Feel free to reopen an issue if you think the original concern was not resolved.

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

No branches or pull requests

3 participants