Recipes/RecordOwner: record_owner.py

File record_owner.py, 4.0 kB (added by ged, 5 years ago)
Line 
1"""
2A plugin for Elixir, which adds and maintains columns for the user that creates,
3and respectivelly updates, a record.
4
5The user may be supplied directly, or a function getting an argument to fetch
6the user may be supplied. the argument (getter_param) is optional, defaulting
7to None.
8
9The field names may be optionally supplied in the column_field_names parameter.
10If column_field_names is None (by default), ['created_by', 'updated_by'],
11will be assumed. The field names may be already defined, in which case they will
12be used as they are. This feature will allow those fields belonging to entity
13relationships. If only one field will be supplied, the same field name will be
14set for inserter and updater, the effect will be to keep the last toucher. In
15combination with the versioned extension, the inserter will be the user who
16touched version 1.
17
18Example:
19
20def get_current_user(param):
21    return current_user
22
23class Movie(Entity):
24    title = Field(Unicode(30))
25    year = Field(Integer)
26    acts_as_owned(Unicode(17), get_current_user, 12, ['toucher'])
27
28
29This extension may well be complemented by the excellent 'versioned' extension.
30"""
31
32from sqlalchemy            import Column, and_
33from sqlalchemy.orm        import mapper, MapperExtension, EXT_CONTINUE
34
35from elixir.statements     import Statement
36from elixir.properties     import EntityBuilder
37
38__all__ = ['acts_as_owned']
39__doc_all__ = []
40
41#
42# utility functions
43#
44
45def get_entity_where(instance):
46    clauses = []
47    for column in instance.table.primary_key.columns:
48        instance_value = getattr(instance, column.name)
49        clauses.append(column==instance_value)
50    return and_(*clauses)
51
52def get_current_user(instance):
53    gcu = instance.__class__.__get_current_user__
54    if callable(gcu):
55        return gcu(instance.__getter_param__)
56    else:
57        return gcu
58
59#
60# a mapper extension to track the user that insert, or update a record
61#
62
63class OwnedMapperExtension(MapperExtension):
64   
65    def before_insert(self, mapper, connection, instance):
66        owned_columns = instance.__class__.__owned_column_field_names__
67        setattr(instance, owned_columns[1], None)
68        setattr(instance, owned_columns[0], get_current_user(instance))
69        return EXT_CONTINUE
70       
71    def before_update(self, mapper, connection, instance):
72        owned_columns = instance.__class__.__owned_column_field_names__
73        setattr(instance, owned_columns[1], get_current_user(instance))
74        return EXT_CONTINUE
75
76owned_mapper_extension = OwnedMapperExtension()
77
78
79#
80# the acts_as_owned statement
81#
82
83class OwnedEntityBuilder(EntityBuilder):
84   
85    def __init__(self, entity, column_type, get_current_user, getter_param=None, 
86                 column_field_names=None):
87        entity._descriptor.add_mapper_extension(owned_mapper_extension)
88        self.entity = entity
89        self.add_mapper_extension(owned_mapper_extension)
90
91        entity.__current_user_column_type__ = column_type
92        if column_field_names in [None, []]:
93            column_field_names = ['created_by', 'updated_by']
94        entity.__owned_column_field_names__ = column_field_names
95        if len(column_field_names) < 2:
96            entity.__owned_column_field_names__.append(column_field_names[0])
97        if callable(get_current_user):
98            entity.__get_current_user__ = staticmethod(get_current_user)
99            entity.__getter_param__ = getter_param
100        else: entity.__get_current_user__ = get_current_user
101       
102    def create_non_pk_cols(self):
103        # add columns for the user who inserted or updated the record, if fields
104        # don't exist yet. this to allow their definition as relationships etc.
105        for fieldname in self.entity.__owned_column_field_names__:
106            if fieldname not in \
107                    [col.name for col in self.entity._descriptor.columns]:
108                self.entity._descriptor.add_column(Column(fieldname, 
109                                    self.entity.__current_user_column_type__))
110
111acts_as_owned = Statement(OwnedEntityBuilder)