GithubHelp home page GithubHelp logo

sqlalchemy-continuum's People

Contributors

andrew-dickinson avatar debonzi avatar dtheodor avatar edhaz avatar edwardbetts avatar fuhrysteve avatar grakic avatar indivar0508 avatar jirikuncar avatar josecsotomorales avatar killthekitten avatar kvesteri avatar kyheo avatar lyndsysimon avatar marksteward avatar mauler avatar milonline-eu avatar nanvel avatar nkocic avatar oinopion avatar quantus avatar rooterkyberian avatar rubencho avatar stepsame avatar tomgobravo avatar tsantanadh avatar tvuotila avatar vault avatar vhermecz avatar yetem avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sqlalchemy-continuum's Issues

history_class function nowhere

Hi,

In the docs (http://sqlalchemy-continuum.readthedocs.org/en/latest/intro.html) there is the statement:

from sqlalchemy_continuum import history_class, parent_class

But this gives an import error. Furthermore, doing git grep history_class on the current master branch gives no results outside comments and documentation. Surely this is not the way it's supposed to be. Furthermore, I can't seem to realize how to get the history class for direct querying.

Thanks,
Kári

undo make_versioned

Hi, trying to introduce continuum to my unittests, but the make_versioned is giving excessive trouble. I want to be able to call make_versioned in the test setup as well as undo calling it in the teardown. It looks like make_versioned is not meant for that, I see that you are also calling it once globally in your own tests.

Calling it twice in a row actually throws SQL alchemy errrors related to already registered events. If I were to unregister all events that are introduced by continuum, would that suffice as an undo_make_versioned function?

version.previous is not None after first change

My testcase is this:

def test_version_order(session, order_factory):
    order = order_factory.build(store_id=1)
    session.add(order)
    session.commit()

    OrderHistory = Order.__versioned__['class']

    assert session.query(OrderHistory).count() == 1

    version = order.versions[0]
    assert version.index == 0
    assert version.operation_type == Operation.INSERT

    print version.transaction_id
    print version.previous.transaction_id
    print version.previous.previous.previous.previous

version.previous() is never None, as stated in https://github.com/kvesteri/sqlalchemy-continuum/blob/master/sqlalchemy_continuum/version.py#L11

I'm using python 2.6.6, sqlite, sqlalchemy, flask-sqlalchemy and pytest.

Use versioning for controlling workflow changes?

So I have a site that would benefit from your amazing plugin!

Is it possible to do the following:

  1. Have a non versioned current_version field on models that selects a version in the past for the live site. This current_version does not change or cause model version increments when it is changed by the application.

  2. When a change is made to the database (in my case a list of residents) these changes are added to a list for the site's administrators to look over. Changes are per user, but this should be fairly self explanatory - there are only two tables a user can change as things stand and they are both changed from one form.

  3. If a version is rejected we could add the version number and the reason to another table.

  4. If the version is approved we increment current version to that one and the live application sees the new information.

  5. This obviously allows us to easily do previewing of changes by simply passing the version number we want to see to our view.

Additionally, are there any problems associated with having master/slave replication and possibly in the far far distant future multiple sharded databases. Data for each Resident would never span shards.

Hope this is the place to ask this sort of question; if not please close and suggest where to ask it! Thanks.

Alembic autogenerate fails if the models contains a many-to-many relationsship and a non-default database schema is used

Hi,

i try to autogenerate an Alembic revision with models that contain a many-to-many relationship. Without sqlalchemy-continuum everything works fine. However if i try to include sqlalchemy-continuum Alembic fails with the following error:

Traceback (most recent call last):
File "/home/khoffrath/projects/testprojects/sa-alembic-continuum-m2m/venv/bin/alembic", line 9, in
load_entry_point('alembic==0.6.0', 'console_scripts', 'alembic')()
File "/home/khoffrath/projects/testprojects/sa-alembic-continuum-m2m/venv/local/lib/python2.7/site-packages/alembic/config.py", line 294, in main
CommandLine(prog=prog).main(argv=argv)
File "/home/khoffrath/projects/testprojects/sa-alembic-continuum-m2m/venv/local/lib/python2.7/site-packages/alembic/config.py", line 289, in main
self.run_cmd(cfg, options)
File "/home/khoffrath/projects/testprojects/sa-alembic-continuum-m2m/venv/local/lib/python2.7/site-packages/alembic/config.py", line 275, in run_cmd
*_dict((k, getattr(options, k)) for k in kwarg)
File "/home/khoffrath/projects/testprojects/sa-alembic-continuum-m2m/venv/local/lib/python2.7/site-packages/alembic/command.py", line 97, in revision
script.run_env()
File "/home/khoffrath/projects/testprojects/sa-alembic-continuum-m2m/venv/local/lib/python2.7/site-packages/alembic/script.py", line 193, in run_env
util.load_python_file(self.dir, 'env.py')
File "/home/khoffrath/projects/testprojects/sa-alembic-continuum-m2m/venv/local/lib/python2.7/site-packages/alembic/util.py", line 177, in load_python_file
module = load_module(module_id, path)
File "/home/khoffrath/projects/testprojects/sa-alembic-continuum-m2m/venv/local/lib/python2.7/site-packages/alembic/compat.py", line 39, in load_module
return imp.load_source(module_id, path, fp)
File "migrations/env.py", line 25, in
import models
File "/home/khoffrath/projects/testprojects/sa-alembic-continuum-m2m/models.py", line 60, in
sa.orm.configure_mappers()
File "/home/khoffrath/projects/testprojects/sa-alembic-continuum-m2m/venv/local/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py", line 2170, in configure_mappers
_call_configured.dispatch.after_configured()
File "/home/khoffrath/projects/testprojects/sa-alembic-continuum-m2m/venv/local/lib/python2.7/site-packages/sqlalchemy/event.py", line 372, in call
fn(_args, *_kw)
File "/home/khoffrath/projects/testprojects/sa-alembic-continuum-m2m/venv/local/lib/python2.7/site-packages/sqlalchemy/orm/events.py", line 489, in wrap
wrapped_fn(_arg, **kw)
File "/home/khoffrath/projects/testprojects/sa-alembic-continuum-m2m/venv/local/lib/python2.7/site-packages/sqlalchemy_continuum/manager.py", line 373, in configure_versioned_classes
self.build_relationships(pending_copy)
File "/home/khoffrath/projects/testprojects/sa-alembic-continuum-m2m/venv/local/lib/python2.7/site-packages/sqlalchemy_continuum/manager.py", line 275, in build_relationships
builder()
File "/home/khoffrath/projects/testprojects/sa-alembic-continuum-m2m/venv/local/lib/python2.7/site-packages/sqlalchemy_continuum/relationship_builder.py", line 152, in call
self.remote_column.table.name + '_history'
KeyError: u'customer_roles_history'

I've created a small project to demonstrate the problem: https://github.com/khoffrath/sa-alembic-continuum-m2m.

Can you please take a look?

Thanks in advance
Karsten

Transaction.user column causing problems

I just upgraded to the most recent version (this repos HEAD) and now I'm getting an error which seemingly has to do with the user transaction table "automagically" having a user column that has a broken relationship with my User table/model (probably due to the fact that it's not called "users"). I'm wondering if this is expected behaviour and I need to explicitly disable this automatic column generation, or if there is some error somewhere. To avoid this column being generated, I specified:

class Transaction(Base, TransactionBase): 
    __tablename__ = 'transaction'
    __manager__ = VersioningManager

make_versioned(manager=VersioningManager(transaction_cls=Transaction))

instead of the simple make_versioned() clause I had before.

Here is the original stack trace:

NoForeignKeysError                        Traceback (most recent call last)
/home/kari/repos/SITE/createdb.py in <module>()
----> 1 from APP import db_create_all
      2 db_create_all()

/home/kari/repos/SITE/APP/__init__.pyc in <module>()
    120 app.register_module(auth)
    121 
--> 122 sa.orm.configure_mappers()

/home/kari/.virtualenvs/ENV/lib/python2.7/site-packages/sqlalchemy/orm/mapper.pyc in configure_mappers()
   2573     finally:
   2574         _CONFIGURE_MUTEX.release()
-> 2575     Mapper.dispatch(Mapper).after_configured()
   2576 
   2577 

/home/kari/.virtualenvs/ENV/lib/python2.7/site-packages/sqlalchemy/event/attr.pyc in __call__(self, *args, **kw)
    216 
    217         for fn in self.parent_listeners:
--> 218             fn(*args, **kw)
    219 
    220     def __len__(self):

/home/kari/.virtualenvs/ENV/lib/python2.7/site-packages/sqlalchemy/orm/events.pyc in wrap(*arg, **kw)
    529                     arg[target_index] = arg[target_index].obj()
    530                 if not retval:
--> 531                     fn(*arg, **kw)
    532                     return interfaces.EXT_CONTINUE
    533                 else:

/home/kari/.virtualenvs/ENV/src/sqlalchemy-continuum/sqlalchemy_continuum/builder.pyc in configure_versioned_classes(self)
    135         pending_copy = copy(self.manager.pending_classes)
    136         self.manager.pending_classes = []
--> 137         self.build_relationships(pending_copy)
    138 
    139         for cls in pending_copy:

/home/kari/.virtualenvs/ENV/src/sqlalchemy-continuum/sqlalchemy_continuum/builder.pyc in build_relationships(self, version_classes)
     91                 continue
     92 
---> 93             for prop in sa.inspect(cls).iterate_properties:
     94                 if prop.key == 'versions':
     95                     continue

/home/kari/.virtualenvs/ENV/lib/python2.7/site-packages/sqlalchemy/orm/mapper.pyc in iterate_properties(self)
   1780         """return an iterator of all MapperProperty objects."""
   1781         if Mapper._new_mappers:
-> 1782             configure_mappers()
   1783         return iter(self._props.values())
   1784 

/home/kari/.virtualenvs/ENV/lib/python2.7/site-packages/sqlalchemy/orm/mapper.pyc in configure_mappers()
   2558                 if not mapper.configured:
   2559                     try:
-> 2560                         mapper._post_configure_properties()
   2561                         mapper._expire_memoizations()
   2562                         mapper.dispatch.mapper_configured(

/home/kari/.virtualenvs/ENV/lib/python2.7/site-packages/sqlalchemy/orm/mapper.pyc in _post_configure_properties(self)
   1671 
   1672             if prop.parent is self and not prop._configure_started:
-> 1673                 prop.init()
   1674 
   1675             if prop._configure_finished:

/home/kari/.virtualenvs/ENV/lib/python2.7/site-packages/sqlalchemy/orm/interfaces.pyc in init(self)
    141         """
    142         self._configure_started = True
--> 143         self.do_init()
    144         self._configure_finished = True
    145 

/home/kari/.virtualenvs/ENV/lib/python2.7/site-packages/sqlalchemy/orm/relationships.pyc in do_init(self)
   1508         self._check_conflicts()
   1509         self._process_dependent_arguments()
-> 1510         self._setup_join_conditions()
   1511         self._check_cascade_settings(self._cascade)
   1512         self._post_init()

/home/kari/.virtualenvs/ENV/lib/python2.7/site-packages/sqlalchemy/orm/relationships.pyc in _setup_join_conditions(self)
   1584                     prop=self,
   1585                     support_sync=not self.viewonly,
-> 1586                     can_be_synced_fn=self._columns_are_mapped
   1587         )
   1588         self.primaryjoin = jc.deannotated_primaryjoin

/home/kari/.virtualenvs/ENV/lib/python2.7/site-packages/sqlalchemy/orm/relationships.pyc in __init__(self, parent_selectable, child_selectable, parent_local_selectable, child_local_selectable, primaryjoin, secondary, secondaryjoin, parent_equivalents, child_equivalents, consider_as_foreign_keys, local_remote_pairs, remote_side, self_referential, prop, support_sync, can_be_synced_fn)
   1847         self.support_sync = support_sync
   1848         self.can_be_synced_fn = can_be_synced_fn
-> 1849         self._determine_joins()
   1850         self._annotate_fks()
   1851         self._annotate_remote()

/home/kari/.virtualenvs/ENV/lib/python2.7/site-packages/sqlalchemy/orm/relationships.pyc in _determine_joins(self)
   1951                         "with a ForeignKey or ForeignKeyConstraint, or "
   1952                         "specify a 'primaryjoin' expression."
-> 1953                         % self.prop)
   1954         except sa_exc.AmbiguousForeignKeysError:
   1955             if self.secondary is not None:

NoForeignKeysError: Could not determine join condition between parent/child tables on relationship Transaction.user - there are no foreign keys linking these tables.  Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression.

Thank you.

TypeError raised with 1.0-b1

I seem to get a TypeError when using the latest version:

Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1836, in __call__
    return self.wsgi_app(environ, start_response)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1820, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1403, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1817, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1477, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1381, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1475, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1461, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "./assetregister/api/__init__.py", line 39, in decorated
    return f(*args, **kwargs)
  File "./assetregister/api/servers.py", line 114, in server_edit
    g.session.commit()
  File "/home/paul/dev/assetregister/env/local/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 765, in commit
    self.transaction.commit()
  File "/home/paul/dev/assetregister/env/local/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 370, in commit
    self._prepare_impl()
  File "/home/paul/dev/assetregister/env/local/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 350, in _prepare_impl
    self.session.flush()
  File "/home/paul/dev/assetregister/env/local/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 1879, in flush
    self._flush(objects)
  File "/home/paul/dev/assetregister/env/local/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 1906, in _flush
    self.dispatch.before_flush(self, flush_context, objects)
  File "/home/paul/dev/assetregister/env/local/lib/python2.7/site-packages/sqlalchemy/event/attr.py", line 211, in __call__
    fn(*args, **kw)
  File "/home/paul/dev/assetregister/env/local/lib/python2.7/site-packages/sqlalchemy_continuum/manager.py", line 274, in before_flush
    uow = self.unit_of_work(session)
  File "/home/paul/dev/assetregister/env/local/lib/python2.7/site-packages/sqlalchemy_continuum/manager.py", line 261, in unit_of_work
    conn = get_bind(obj)
  File "/home/paul/dev/assetregister/env/local/lib/python2.7/site-packages/sqlalchemy_continuum/utils.py", line 64, in get_bind
    'This method accepts only Session, Connection and declarative '
TypeError: This method accepts only Session, Connection and declarative model objects.```

MySQL/MariaDB OperationalError

First off, I'm not sure if this is an issue with Continuum or with SQLAlchemy's dialect engine.

When attempting to create a new object in a versioned table, the following error is thrown before insertion of the object.

Tested on both MySQL 5.6 GPL Edition and MariaDB 5.5.

Module versions:
SQLAlchemy (0.9.2)
SQLAlchemy-Continuum (0.10.1)
SQLAlchemy-Utils (0.23.3)

OperationalError: (OperationalError) (1093, "You can't specify target table 'entity_history' for update in FROM clause") 'UPDATE entity_history SET end_transaction_id=%s WHERE entity_history.transaction_id = (SELECT max(entity_history_1.transaction_id) AS max_1 FROM entity_history AS entity_history_1 WHERE entity_history_1.transaction_id < %s) AND entity_history.id = %s' (2L, 2L, 2L)

Full traceback:
https://gist.github.com/anonymous/d7907460b77513e5e47b

Bad import of {get_}declarative_base?

I'm sure I'm missing something, but when I try to use sqlalchemy-continuum I get an error message "ImportError: cannot import name 'declarative_base'. Problem seems to be that line 8 tries to import declarative_base from sqlalchemy_utils.functions, but sqlalchemy-utils doesn't have declarative_base (though it does have get_declarative_base). I'm using sqlalchemy-continuum 0.10.3 and sqlalchemy-utils 0.25.4.

Alembic migration support ?

Hi,

I read some reference to alembic migration in changelog but didn't figure out how to get sa-continuum and alembic working together during an alembic migration.

Any hint ?

Object has no attribute 'visit_HSTORE'

I tried a minimalist sample.

With MySQL, I got :
AttributeError: 'MySQLTypeCompiler' object has no attribute 'visit_HSTORE'

With SQLite, I got :
AttributeError: 'SqliteTypeCompiler' object has no attribute 'visit_HSTORE'

Did I miss something ?

Attribute error in flask extension

in your extension for flask you are using @sa.ext.declarative.declared_attr decorator
you imported sqlalchemy as sa
but sa doesn't contain reference to ext inside init.py, so this code will work only if i imported full package before your code execution... (otherwise module object doesn't have 'ext' attribute...)

Have version class inherit from parent

Since ORM models that go beyond example material also define behavior (methods), it would be very convenient if the version models can inherit the behavior of the parent models. This is very useful in accessor methods (@property, to_dict() kind of methods etc.)

This already works partially when someone defines behavior on the Base class, since the version models by default inherit from the same Base.

There are a couple of ways to achieve this

  • Have version class directly subclass the parent class. Since the model class is loaded with sql alchemy magic (__mapper__, instrumented attributes, relations, validators etc.) this cannot be done in a straightforward way. I implemented this on my own using a hack in the ModelBuilder that creates an intermediate model like this:
class Model(Base):
   #...
class AbstractModel(Model):
  __abstract__ = True #at least this does not create a table and mapper, need more work to get rid of other "offensive" attributes
class ModelVersion(AbstractModel, VersionBase):
  #...
  • Split your models in behavior and declarative so that the version class can directly and safely inherit from the behavior one. This requires manual work for every model
class _ModelBehavior():
  def useful_method(self):
    #...
class Model(_ModelBehavior, Base):
  __versioned__ = {"base_classes": (_ModelBehavior, VersionBase)}
  #..
  • Create a VersionBase that uses __getattr__ to access attributes from the parent
class VersionBase():
  def __getattr__(self, name):
    return getattr(self.version_parent, name)

This is the most hacky one with low reliability (this example does not work when version_parent is None for example)

Thoughts?

sqlalchemy.exc.OperationalError: (OperationalError) (1075, 'Incorrect table definition; there can be only one auto column and it must be defined as a key')

With a simple model :

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)

It raises :
sqlalchemy.exc.OperationalError: (OperationalError) (1075, 'Incorrect table definition; there can be only one auto column and it must be defined as a key') '
CREATE TABLE article_history (
id INTEGER NOT NULL,
name VARCHAR(255),
content TEXT,
transaction_id BIGINT NOT NULL AUTO_INCREMENT,
operation_type SMALLINT NOT NULL,
PRIMARY KEY (id, transaction_id)
)' ()

History not saved if session was flushed before commit

Test that currently fails:

diff --git a/tests/test_exotic_operation_combos.py b/tests/test_exotic_operation_combos.py
index 67d0b5f..9e261fa 100644
--- a/tests/test_exotic_operation_combos.py
+++ b/tests/test_exotic_operation_combos.py
@@ -52,3 +52,14 @@ class TestExoticOperationCombos(TestCase):
         assert article2.versions.count() == 2
         assert article2.versions[0].operation_type == 0
         assert article2.versions[1].operation_type == 1
+
+    def test_insert_flushed_object(self):
+        article = self.Article()
+        article.name = u'Some article'
+        article.content = u'Some content'
+        self.session.add(article)
+        self.session.flush()
+        self.session.commit()
+
+        assert article.versions.count() == 1
+        assert article.versions[0].operation_type == 0

SQLAlchemy-Continuum 727b251

Flask==0.10.1
SQLAlchemy==0.9.2
SQLAlchemy-Utils==0.23.5
SQLAlchemy-i18n==0.8.2
flexmock==0.9.7
inflection==0.2.0
pytest==2.5.2
six==1.5.2
toolz==0.5.2

OS: Ubuntu 12.04 64-bit
PostgreSQL version: 9.1.3-2

Migration function when porting continuum to existing projects

When user has an existing project and starts using SA-Continuum the following problems are exposed:

  1. Continuum transaction history is not aware of old object insertions
  2. Since transaction history is not aware of object insertions all relation queries do not return expected results.

For example lets say we have articles and users (each article has one user as owner). The articles and users are already in the database when the developer migrates the whole project to use Continuum. Then user updates article A. When user calls:

article.versions[-1].owner

It returns None, because there is no history yet for this user object.

Solution:

Lets make a migration function which generates history records for all versioned table rows.

Missing inflection dependency ?

Traceback (most recent call last):
File "run.py", line 4, in
from app import app
File "/home/userdev/projects/userproject/app/init.py", line 38, in
from app.users.views import mod as usersModule
File "/home/userdev/projects/userproject/app/users/views.py", line 9, in
from app.users.models import User
File "/home/userdev/projects/userproject/app/users/models.py", line 4, in
from sqlalchemy_continuum import make_versioned
File "/home/userdev/envs/userproject/lib/python2.7/site-packages/sqlalchemy_continuum/init.py", line 2, in
from .manager import VersioningManager
File "/home/userdev/envs/userproject/lib/python2.7/site-packages/sqlalchemy_continuum/manager.py", line 2, in
from inflection import underscore, pluralize
ImportError: No module named inflection

historyobject.changeset always empty on 0.10.2

Tested on postgresql, MariaDB.

History objects are saved to the database but the changeset helper property is always empty. Tested both Insert and Update operations.

inflection (0.2.0)
six (1.5.2)
SQLAlchemy (0.9.2)
SQLAlchemy-Continuum (0.10.2)
SQLAlchemy-Utils (0.23.5)
toolz (0.5.2)

__versioned__ API could be nicer

Accessing a models corresponding history class through ModelClass.versioned['class'] feels very awkward (evokes a feeling of poking around in internals better left alone).

Would you consider making it (and perhaps transaction_changes/_log) accessible via a "nicer" API?

For example: ModelClass.history_class

New configuration option: store_data_at_delete

This configuration option would determine if entity deletion should save the data in history record (currently the history record that marks delete operation only holds NULL values for all columns other than primary keys).

Relation from versioned table to non-versioned one

Say I have two models:

class User(Base):
  __tablename__ = 'users'

  id = sa.Column(sa.Integer, primary_key=True)
  username = sa.Column(sa.Unicode(255))

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

  id = sa.Column(sa.Integer, primary_key=True)
  user_id = sa.Column(sa.Integer, sa.ForeignKey('users.id'), nullable=False)
  user = relationship('User', backref=backref('articles'))

I'm running into the problem that when I do something along the lines of:

  article.versions[0].user # does not work
  article.versions[0].user_id # returns the user id

I see that relationship fields seem to be copied to the history table if the related table is also versioned, and the instructions at http://sqlalchemy-continuum.readthedocs.org/en/latest/version_objects.html#version-relationships work given this assumption. However, I would argue that foreign key relations to non-versioned tables are also useful, in my case for example, it doesn't really make sens to version the users table (it's mostly static and preserving the history of the fields is not useful), but my application doesn't allow for user deletion (only deactivation) so the foreign key would work.

Any tips or ideas? I haven't delved into the codebase so I might be missing something.

Thanks,
Kári

Using model attribute with custom table column name doesn't work

I am trying to add versioning to an existing project. I use column names that differ from the model attribute name. But that doesn't seem to work at all.
Here is an simple example:

import sqlalchemy as sa
from sqlalchemy_continuum import make_versioned

Base = sa.ext.declarative.declarative_base()
make_versioned()

class ProjectBrand(Base):
    __tablename__ = "t_project_brands"
    __versioned__ = {}

    id = sa.Column(sa.Integer, primary_key=True)
    ortho = sa.Column('p_ortho', sa.String, nullable = False, index=True)

sa.orm.configure_mappers()

engine = sa.create_engine('sqlite:///:memory:', echo=False)     
Base.metadata.create_all(engine)
Session = sa.orm.sessionmaker(bind=engine)
session = Session()

brand1 = ProjectBrand()
brand1.ortho = "Brand1"
session.add(brand1)
session.commit()

brand1.ortho = "Brand2"
session.commit()

brand1.versions[0].revert()
session.commit()

When I run it I get the following error:

Traceback (most recent call last):
  File "E:\Projects\Ford\sync3\workspace\POI_linguist\Aptana Studio 3 Workspace\Brand Linguistics Tool\src\continuum_test.py", line 35, in <module>
    brand1.versions[0].revert()
  File "C:\Python27\lib\site-packages\sqlalchemy_continuum\version.py", line 67, in revert
    return Reverter(self, relations=relations)()
  File "C:\Python27\lib\site-packages\sqlalchemy_continuum\reverter.py", line 102, in __call__
    self.revert_properties()
  File "C:\Python27\lib\site-packages\sqlalchemy_continuum\reverter.py", line 49, in revert_properties
    getattr(self.obj, prop.key)
AttributeError: 'ProjectBrandHistory' object has no attribute 'ortho'

It seems it tries to use ortho attribute, but it hasn't mapped this internally to the correct table column name.

Searching for deleted elements

I'm unable to find how to find deleted objects through the versioning when using the flask TransactionLogBase
My problem is due to the fact that when a versioned table entry is deleted, the deleted transaction log is completely empty, with only the operation_type Operations.DELETE
If this could be done easily with the tx_meta, that would work for me.

I'm hoping to implement a full undo style feature to allow my users to revert changes or even deletions of their entities

Make unit of work use context.isinsert and context.isdelete for many-to-many operations

Mike Bayer:

"you can tell if a statement is an INSERT/DELETE usually by looking at context.isinsert/context.isdelete, and also the table name
you can get from context.statement.table (something like that). Similar things can be done where I see you're regexping the DELETE
later on. Digging into the string is fine but once you're targeting the broad spectrum of scenarios, like users that are adding SQL comments and such to their SQL, backends that don't actually use SQL, you want to stick with inspecting the expression trees as much as possible"

Version numbers and relations (Should object version increase when its relations change?)

I have a problem with the following use-case:

My Article has one-to-many to Tag. The end-user application only considers the Article history state (so the fact that Tags have state too is invisible), and is able to revert or see all snapshots of the Article history. The Article state is considered to be changed when the article itself is changed, when tags are added/removed and when one of its existing tags is modified.

How can I go back to all Article history (and by that I want to include snapshots where only the Tags of the article were modified, not necessarily the article column's)? The revert(relations=['Tags'] produces very unexpected results based on the number of the Article versions and the number of each Tag's versions.

If one of my tags has more versions that the article for example, there is no way to use article.revert(relations=['Tags']) to go back to all states of that tag. How can I achieve this?

It would be nice to have an option like increment_version_on_relation_change.

Column onupdate clause erroneously copied to history table

If I have a datetime (or any other column type) column in a table with onupdate= and include this in the history table, the onupdate clause gets erroneously (I'd argue) "copied" to the history table.

Example:

class Article(Base):
    __versioned__ = {'include' : ['last_update', ]}

    id = sa.Column(sa.Integer, primary_key=True)
    body = sa.Column(sa.UnicodeText)
    last_update = sa.Column(sa.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow, nullable=False)

The articles table behaves correctly, in that the last_update column is updated each time a record is changed. However, the _history table, due to the update of end_transaction_id (I presume) fails to preserve the value of the column, so the second latest row effectively has the last_update value of the most recent update.

Thanks for an otherwise wonderful extension!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.