Hello Michael,

On Sunday, March 16, 2014 10:19:16 PM UTC+1, Michael Bayer wrote:
>
>
>
> are you sure it’s actually saying “SET NULL” in the SQL?   if the value 
> isn’t present in the params, that just means it’s leaving it unaffected. 
>  this is normal behavior when the value hasn’t changed. 
>
> I can’t say much else because these are only excerpts of code without 
> context. 
>
> if you wanted to show, “stores a NULL”, send a full script that does that 
> and asserts it’s the case. 
>

Yes, I'm positive, SA explicitly SETs editor_id = NULL. Please find 
attached a stand-alone script.

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.
#!/usr/bin/env python
import logging

import sqlalchemy as sa
import sqlalchemy.orm
import transaction
import zope.sqlalchemy
from sqlalchemy.ext.declarative import (
    declared_attr,
    declarative_base
)

logging.basicConfig(level=logging.INFO)
lgg = logging.getLogger(__name__)

salgg = logging.getLogger('sqlalchemy.engine')
salgg.setLevel(logging.INFO)

DbEngine = sa.create_engine("postgresql+psycopg2://test:test@localhost/test")
DbSession = sa.orm.scoped_session(
    sa.orm.sessionmaker(
        extension=zope.sqlalchemy.ZopeTransactionExtension()
    )
)
DbSession.configure(bind=DbEngine)
DbBase = declarative_base(bind=DbEngine)


def _validate_editor(context):
    pass
    # if not context.current_parameters['editor_id']:
    #     raise ValueError('Editor must be set on update.')


class DefaultMixin(object):
    """Mixin to add Parenchym's standard fields to a model class.

    These are: id, ctime, owner, mtime, editor.
    """

    id = sa.Column(sa.Integer(), primary_key=True, nullable=False)
    """Primary key of table."""

    ctime = sa.Column(sa.DateTime(), server_default=sa.func.current_timestamp(),
        nullable=False)
    """Timestamp, creation time."""

    # noinspection PyMethodParameters
    @declared_attr
    def owner_id(cls):
        """ID of user who created this record."""
        return sa.Column(
            sa.Integer(),
            sa.ForeignKey(
                "pym.user.id",
                onupdate="CASCADE",
                ondelete="RESTRICT"
            ),
            nullable=False
        )

    mtime = sa.Column(sa.DateTime(), onupdate=sa.func.current_timestamp(), nullable=True)
    """Timestamp, last edit time."""

    # noinspection PyMethodParameters
    @declared_attr
    def editor_id(cls):
        """ID of user who was last editor."""
        return sa.Column(
            sa.Integer(),
            sa.ForeignKey(
                "pym.user.id",
                onupdate="CASCADE",
                ondelete="RESTRICT"
            ),
            nullable=True,
            onupdate=_validate_editor
        )


class User(DbBase, DefaultMixin):
    __tablename__ = "user"
    __table_args__ = (
        {'schema': 'pym'}
    )

    principal = sa.Column(sa.Unicode(255), nullable=False)


class Tenant(DbBase, DefaultMixin):
    """
    A tenant.
    """
    __tablename__ = "tenant"
    __table_args__ = (
        sa.UniqueConstraint('name', name='tenant_ux'),
        {'schema': 'pym'}
    )

    name = sa.Column(sa.Unicode(255), nullable=False)
    # Load description only if needed
    descr = sa.orm.deferred(sa.Column(sa.UnicodeText, nullable=True))
    """Optional description."""

    def __repr__(self):
        return "<{name}(id={id}, name='{n}'>".format(
            id=self.id, n=self.name, name=self.__class__.__name__)


def setup(sess):
    with transaction.manager:
        sess.execute("create schema pym")
        zope.sqlalchemy.mark_changed(sess)

    with transaction.manager:
        DbBase.metadata.create_all(DbEngine)
        u1 = User()
        u1.owner_id = 1
        u1.principal = 'salomon'
        u2 = User()
        u2.owner_id = 1
        u2.principal = 'sibylle'
        sess.add(u1)
        sess.add(u2)


def dance(sess):
    lgg.info('=' * 78)
    with transaction.manager:
        try:
            tn = sess.query(Tenant).filter(Tenant.name == 'foo').one()
            cnt = tn.descr.count('o')
        except sa.orm.exc.NoResultFound:
            tn = Tenant()
            tn.owner_id = 2
            tn.name = 'foo'
            tn.descr = 'o'
            sess.add(tn)
            cnt = 0
            lgg.info('Loop {} (added): {} {} {}'.format(cnt, tn.editor_id,
                tn.descr, tn.descr.count('o')))
        else:
            lgg.info('Loop {} (stored before change): {} {} {}'.format(cnt, tn.editor_id,
                tn.descr, tn.descr.count('o')))
            tn.descr += 'o'
            tn.editor_id = 2
        lgg.info('-' * 78)


def main():
    sess = DbSession()
    setup(sess)

    dance(sess)
    dance(sess)
    dance(sess)
    dance(sess)
    dance(sess)

if __name__ == '__main__':
    main()

Reply via email to