示例#1
0
def make_audit_table(table, metadata):
    """Create an audit log table for an archetype.
    """
    audit_tbl_name = naming.audit_table_name(table.name)
    audit_columns = get_audit_table_columns(table)
    audit_tbl = sa.Table(audit_tbl_name, metadata, *audit_columns,
        useexisting=False
    )
    return audit_tbl
def downgrade():
    def drop_audit_table(audit_tbl_name):
        print "Dropping audit table:", audit_tbl_name
        op.drop_table(audit_tbl_name)

    drop_audit_table(naming.audit_table_name("group"))
    drop_audit_table(naming.audit_table_name("member"))
    drop_audit_table(naming.audit_table_name("doc_principal"))

    item_vote_tbl_columns = [
        sa.Column("vote_id", sa.Integer, primary_key=True),
        sa.Column(
            "item_id",
            sa.Integer,  # !+RENAME doc_id
            sa.ForeignKey("doc.doc_id"),
            nullable=False),
        sa.Column("date", sa.Date),
        sa.Column("affirmative_vote", sa.Integer),
        sa.Column("negative_vote", sa.Integer),
        sa.Column("remarks", sa.UnicodeText),
        sa.Column("language", sa.String(5), nullable=False),
    ]
    item_member_vote_tbl_columns = [
        sa.Column("vote_id",
                  sa.Integer,
                  sa.ForeignKey("item_vote.vote_id"),
                  primary_key=True,
                  nullable=False),
        sa.Column("member_id",
                  sa.Integer,
                  sa.ForeignKey("user.user_id"),
                  primary_key=True,
                  nullable=False),
        sa.Column("vote", sa.Boolean),
    ]
    print "Re-create table: item_vote"
    op.create_table("item_vote", *item_vote_tbl_columns)
    print "Re-create table: item_member_vote"
    op.create_table("item_member_vote", *item_member_vote_tbl_columns)

    print "Re-adding unique constraint on table column: debate_record_audit.sitting_id"
    op.create_unique_constraint("debate_record_audit_sitting_id_key",
                                "debate_record_audit", ["sitting_id"])
def downgrade():
    
    def drop_audit_table(audit_tbl_name):
        print "Dropping audit table:", audit_tbl_name
        op.drop_table(audit_tbl_name)
    drop_audit_table(naming.audit_table_name("group"))
    drop_audit_table(naming.audit_table_name("member"))
    drop_audit_table(naming.audit_table_name("doc_principal"))
    
    item_vote_tbl_columns = [
        sa.Column("vote_id", sa.Integer, primary_key=True),
        sa.Column("item_id", sa.Integer, # !+RENAME doc_id
            sa.ForeignKey("doc.doc_id"),
            nullable=False
        ),
        sa.Column("date", sa.Date),
        sa.Column("affirmative_vote", sa.Integer),
        sa.Column("negative_vote", sa.Integer),
        sa.Column("remarks", sa.UnicodeText),
        sa.Column("language", sa.String(5), nullable=False),
    ]
    item_member_vote_tbl_columns = [
        sa.Column("vote_id", sa.Integer,
            sa.ForeignKey("item_vote.vote_id"),
            primary_key=True,
            nullable=False
        ),
        sa.Column("member_id", sa.Integer,
            sa.ForeignKey("user.user_id"),
            primary_key=True,
            nullable=False
        ),
        sa.Column("vote", sa.Boolean),
    ]
    print "Re-create table: item_vote"
    op.create_table("item_vote", *item_vote_tbl_columns)
    print "Re-create table: item_member_vote"
    op.create_table("item_member_vote", *item_member_vote_tbl_columns)
    
    print "Re-adding unique constraint on table column: debate_record_audit.sitting_id"
    op.create_unique_constraint("debate_record_audit_sitting_id_key", 
        "debate_record_audit", ["sitting_id"])
示例#4
0
def get_audit_table_columns(table):
    """Derive the columns of the audit table from the table being audited.
    """
    entity_name = table.name
    audit_tbl_name = naming.audit_table_name(entity_name)
    # audit-specific columns -- prefix with "audit_" to avoid potential 
    # clashing of column names from table being audited.
    columns = [
        sa.Column("audit_id", sa.Integer, 
            sa.ForeignKey("audit.audit_id"), 
            primary_key=True),
    ]
    def extend_cols(cols, ext_cols):
        names = [ c.name for c in cols ]
        for c in ext_cols:
            assert c.name not in names, "Duplicate column [%s]." % (c.name)
            names.append(c.name)
            if c.primary_key:
                # PK columns on auditable table become FK columns on audit table
                if len(table.primary_key) == 1:
                    # single-column PK - the id column of the "owning" object for
                    # which the change is being logged; we always retain the same
                    # original column name i.e. doc_id for case of "doc", and have
                    # the audit_head_id property always read and write to this.
                    assert c.name == "%s_id" % (entity_name), \
                        "Inconsistent PK column naming [%s != %s]" % (
                            "%s_id" % (entity_name), c.name)
                else:
                    # composite PK
                    log.debug("Table %r -> skipping pk column %r name "
                            "constraint check for multi-column PK: %s", 
                                audit_tbl_name, c.name, table.primary_key.columns)
                # add the column, corresponding ForeignKeyConstraint added at end
                cols.append(sa.Column(c.name, c.type, nullable=False, index=True))
                # !+FK columns may specify type as None (not c.type), to let 
                # auto detection of the type from that of the FK col
            else:
                # !+ should special ext col constraints NOT be carried over
                # e.g. default value on ext, not/nullable on ext...?
                cols.append(c.copy())
                # auditable "unique" columns may NOT be unique in the audit table!
                if cols[-1].unique:
                    cols[-1].unique = False
    
    extend_cols(columns, table.columns)
    # add ForeignKeyConstraint corresponding to original PK
    pk_col_names = [ c.name for c in table.primary_key.columns ]
    columns.append(
        sa.ForeignKeyConstraint(pk_col_names, 
            [ "%s.%s" % (entity_name, name) for name in pk_col_names ]))
    # !+additional tables...
    return columns
 def make_audit_table(tbl_name):
     tbl = getattr(schema, tbl_name)
     audit_tbl_name = naming.audit_table_name(tbl_name)
     audit_tbl_columns = schema.get_audit_table_columns(tbl)
     print "Creating audit table:", audit_tbl_name
     op.create_table(audit_tbl_name, *audit_tbl_columns)
示例#6
0
    def decorate_model(self, model):
        # Assumption: if a domain class is explicitly pre-defined, then it is
        # assumed that all necessary setup is also taken care of.
        # Typically, only the sub-classes of an archetype (mapped to a same
        # table) need dynamic creation/setup.

        def get_audit_class_for(auditable_class):
            audit_cls_name = "%sAudit" % (auditable_class.__name__)
            return getattr(MODEL_MODULE, audit_cls_name, None)

        def get_base_audit_class(model):
            """Identify what should be the BASE audit class for a 
            {model}Audit class to inherit from, and return it.
            """
            assert interfaces.IFeatureAudit.implementedBy(model), model
            ti = capi.get_type_info(model)
            if ti.archetype is None:
                # !+ should this be allowed to ever happen?
                # i.e. require each type to declare an archetype?
                base_audit_class = domain.Audit
            else:
                base_audit_class = get_audit_class_for(ti.archetype)
                if base_audit_class is None:
                    # fallback to get the audit class for the sys archetype
                    base_audit_class = get_audit_class_for(ti.sys_archetype)
                assert base_audit_class is not None, (model, ti.archetype,
                                                      base_audit_class)
            return base_audit_class

        def new_audit_class(model):
            """Create, set on MODEL_MODULE, and map {model}Audit class.
            """
            base_audit_cls = get_base_audit_class(model)
            audit_cls = base_audit_cls.auditFactory(model)
            # set on MODEL_MODULE
            setattr(MODEL_MODULE, audit_cls.__name__, audit_cls)
            # mapper for newly created audit_cls
            mapper(audit_cls,
                   inherits=base_audit_cls,
                   polymorphic_identity=naming.polymorphic_identity(model))
            log.info("GENERATED new_audit_class %s(%s) for type %s", audit_cls,
                     base_audit_cls, model)
            return audit_cls

        # domain - audit class
        audit_cls = get_audit_class_for(model)
        if audit_cls is None:
            audit_cls = new_audit_class(model)

        # auditor - head cls
        import bungeni.core.audit
        bungeni.core.audit.set_auditor(model)

        # mapper - audit class
        # assumption: audit_cls uses single inheritance only (and not only for
        # those created dynamically in feature_audit())
        base_audit_cls = audit_cls.__bases__[0]
        assert issubclass(base_audit_cls, domain.Audit), \
            "Audit class %s is not a proper subclass of %s" % (
                audit_cls, domain.Audit)

        # extended attributes - propagate any on head cls also to its audit_cls
        for vp_name, vp_type in model.extended_properties:
            mapper_add_relation_vertical_property(audit_cls, vp_name, vp_type)
        # !+NOTE: capi.get_type_info(model).descriptor_model is still None

        # model.changes <-> change.audit.audit_head=doc:
        # doc[@TYPE] <-- TYPE_audit <-> audit <-> change

        # get head table for kls, and its audit table.
        tbl = utils.get_local_table(model)
        # NOT mapped_table, as when cls_mapper.single=False (e.g. for
        # the case of the group type) it gves an sa.sql.expression.Join,
        # and not a table object:
        #   principal JOIN "group" ON principal.principal_id = "group".group_id
        audit_tbl = getattr(schema, naming.audit_table_name(tbl.name))
        cls_mapper = class_mapper(model)
        cls_mapper.add_property(
            "changes",
            relation(
                domain.Change,
                # join condition, may be determined by a composite primary key
                primaryjoin=sa.and_(*[
                    pk_col == audit_tbl.c.get(pk_col.name)
                    for pk_col in tbl.primary_key
                ]),
                secondary=audit_tbl,
                secondaryjoin=sa.and_(
                    audit_tbl.c.audit_id == schema.change.c.audit_id, ),
                lazy=True,
                order_by=schema.change.c.audit_id.desc(),
                cascade="all",
                passive_deletes=False,  # SA default
            ))
    def decorate_model(self, model):
        # Assumption: if a domain class is explicitly pre-defined, then it is 
        # assumed that all necessary setup is also taken care of. 
        # Typically, only the sub-classes of an archetype (mapped to a same 
        # table) need dynamic creation/setup.
        
        def get_audit_class_for(auditable_class):
            audit_cls_name = "%sAudit" % (auditable_class.__name__)
            return getattr(MODEL_MODULE, audit_cls_name, None)
        
        def get_base_audit_class(model):
            """Identify what should be the BASE audit class for a 
            {model}Audit class to inherit from, and return it.
            """
            assert interfaces.IFeatureAudit.implementedBy(model), model
            ti = capi.get_type_info(model)
            if ti.archetype is None:
                # !+ should this be allowed to ever happen? 
                # i.e. require each type to declare an archetype?
                base_audit_class = domain.Audit
            else:
                base_audit_class = get_audit_class_for(ti.archetype)
                if base_audit_class is None:
                    # fallback to get the audit class for the sys archetype
                    base_audit_class = get_audit_class_for(ti.sys_archetype)
                assert base_audit_class is not None, (model, ti.archetype, base_audit_class)
            return base_audit_class
        
        def new_audit_class(model):
            """Create, set on MODEL_MODULE, and map {model}Audit class.
            """
            base_audit_cls = get_base_audit_class(model)
            audit_cls = base_audit_cls.auditFactory(model)
            # set on MODEL_MODULE
            setattr(MODEL_MODULE, audit_cls.__name__, audit_cls)
            # mapper for newly created audit_cls
            mapper(audit_cls,
                inherits=base_audit_cls,
                polymorphic_identity=naming.polymorphic_identity(model)
            )
            log.info("GENERATED new_audit_class %s(%s) for type %s",
                audit_cls, base_audit_cls, model)
            return audit_cls
        
        # domain - audit class
        audit_cls = get_audit_class_for(model)
        if audit_cls is None: 
            audit_cls = new_audit_class(model)
        
        # auditor - head cls
        import bungeni.core.audit
        bungeni.core.audit.set_auditor(model)
        
        # mapper - audit class
        # assumption: audit_cls uses single inheritance only (and not only for 
        # those created dynamically in feature_audit())
        base_audit_cls = audit_cls.__bases__[0]
        assert issubclass(base_audit_cls, domain.Audit), \
            "Audit class %s is not a proper subclass of %s" % (
                audit_cls, domain.Audit)
        
        # extended attributes - propagate any on head cls also to its audit_cls
        for vp_name, vp_type in model.extended_properties:
            mapper_add_relation_vertical_property(
                audit_cls, vp_name, vp_type)
        # !+NOTE: capi.get_type_info(model).descriptor_model is still None

        # model.changes <-> change.audit.audit_head=doc:
        # doc[@TYPE] <-- TYPE_audit <-> audit <-> change
                
        # get head table for kls, and its audit table.
        tbl = utils.get_local_table(model)
        # NOT mapped_table, as when cls_mapper.single=False (e.g. for 
        # the case of the group type) it gves an sa.sql.expression.Join,
        # and not a table object:
        #   principal JOIN "group" ON principal.principal_id = "group".group_id
        audit_tbl = getattr(schema, naming.audit_table_name(tbl.name))
        cls_mapper = class_mapper(model)
        cls_mapper.add_property("changes", relation(domain.Change,
                # join condition, may be determined by a composite primary key
                primaryjoin=sa.and_( *[
                    pk_col == audit_tbl.c.get(pk_col.name)
                    for pk_col in tbl.primary_key ]),
                secondary=audit_tbl,
                secondaryjoin=sa.and_(
                    audit_tbl.c.audit_id == schema.change.c.audit_id,
                ),
                lazy=True,
                order_by=schema.change.c.audit_id.desc(),
                cascade="all",
                passive_deletes=False, # SA default
            ))
 def make_audit_table(tbl_name):
     tbl = getattr(schema, tbl_name)
     audit_tbl_name = naming.audit_table_name(tbl_name)
     audit_tbl_columns = schema.get_audit_table_columns(tbl)
     print "Creating audit table:", audit_tbl_name
     op.create_table(audit_tbl_name, *audit_tbl_columns)