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

Versioning of one-to-many relationships #217

Open
malikolivier opened this issue Mar 11, 2019 · 3 comments
Open

Versioning of one-to-many relationships #217

malikolivier opened this issue Mar 11, 2019 · 3 comments

Comments

@malikolivier
Copy link

malikolivier commented Mar 11, 2019

Hello, I am trying to version a table with several one-to-many relationships. I am wondering if sqlalchemy-continuum is appropriate for this use case. Please let me know if I am wrong.

Checking the doc

I started reading the documentation there: https://sqlalchemy-continuum.readthedocs.io/en/latest/version_objects.html#version-relationships

Here are the models used as examples in the doc:

class Article(Base):
    __tablename__ = 'article'
    __versioned__ = {}

    id = sa.Column(sa.Integer, autoincrement=True, primary_key=True)
    name = sa.Column(sa.Unicode(255), nullable=False)


class Category(Base):
    __tablename__ = 'tag'

    id = sa.Column(sa.Integer, autoincrement=True, primary_key=True)
    name = sa.Column(sa.Unicode(255))
    article_id = sa.Column(sa.Integer, sa.ForeignKey(Article.id))
    article = sa.orm.relationship(
        Article,
        backref=sa.orm.backref('categories')
    )

An instance of Article has a one-to-many relationship to several instances of Category. That seems to be a simple example of my use case.
However, the doc uses the category keyword, so the example code crashes with TypeError: 'category' is an invalid keyword argument for Article. The backref is a plural categories.

category = Category(name=u'Some category')
article = Article(
    name=u'Some article',
    category=category # <- Crashes here
)

Writing a test program

In any case, I assumed this was a typo in the doc, and wrote the test program below. You can run the test program as long as you fire up a postgres server (e.g. with docker: docker run -p 5432:5432 postgres:9.3-alpine).

import sqlalchemy as sa
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm.session import sessionmaker
from sqlalchemy_continuum import make_versioned

engine = sa.create_engine('postgres://postgres:postgres@localhost:5432/postgres', echo=True)
Session = sessionmaker()

make_versioned(user_cls=None)

Base = declarative_base()

class Article(Base):
    __versioned__ = {}
    __tablename__ = 'article'

    id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
    name = sa.Column(sa.Unicode(255))
    content = sa.Column(sa.UnicodeText)

class Category(Base):
    __versioned__ = {}
    __tablename__ = 'category'

    id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
    name = sa.Column(sa.Unicode(255))
    article_id = sa.Column(sa.Integer, sa.ForeignKey(Article.id))
    article = sa.orm.relationship(
        Article,
        backref=sa.orm.backref('categories')
    )

# after you have defined all your models, call configure_mappers:
sa.orm.configure_mappers()


Base.metadata.create_all(engine)

session = Session(bind=engine)

# Commit first version
article = Article()
session.add(article)
session.commit()

# Add a tag and commit second version
article.categories.append(Category(name="A tag"))
session.commit()

# Remove tags and commit third version
article.categories = []
session.commit()

assert article.versions.count() == 3, "Expect to have 3 versions!"

I assume the last assertion to be verified.
However, article.versions.count() evaluates to 1. It seems that the relationship of the parent to the children is not versioned.
Is the failure of this assertion expected behaviour?

Thank you very much for the feedback. I can contribute patches (at least to the doc once we confirmed what is expected behaviour) if necessary.

@sebr74
Copy link

sebr74 commented Aug 2, 2019

I have a similar behavior with a many to many relationship. The documentation seems to imply that relationships are supported and will produce a version of the parent object if you modify the list of associated objects, but I get the same behavior as @malikolivier. My parent object does not get a new version if I change the list of associated objects. However, a _version table is created for my secondary table that is used to associate both models of the many to many relationship. I am still trying to figure out if and how I can use this. @kvesteri, I think a bit of insight from you would help us a lot.

@davidchenfn
Copy link

davidchenfn commented Aug 19, 2019

Someone wrote RelatedVersioningPlugin that works pretty well, and creates new parent versions if child objects are updated. Maybe this helps for your case:
https://gist.github.com/grakic/66006440ed871c8b9d73a2206dff8a5e

The issue where I originally found this solution is /#35

@njoeres
Copy link

njoeres commented Oct 18, 2023

I recently ran into this problem as well. For the simple case with Article and Category the plugin from @grakic did work quite well with some small modifications. So thanks for sharing that!
However, in our case with more complex models, the plugin does not work anymore and I usually run into the error AttributeError('Unknown model for table %s' % table_name) in line 22. So far I could not make it work unfortunately.
I know this is issue is quite old, but did someone found a better solution, e.g., fixing the bug instead of using a plugin? @marksteward

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

No branches or pull requests

4 participants