Hi Gustavo, Am Dienstag, 11. Dezember 2007 23:46:30 schrieb Gustavo Niemeyer: > Hello Stephan, > > > I'm new to storm and really like it so far. Until now I've used my own > > homegrown database wrapper (so I can't compare to SQLAlchemy or > > SQLObject). > > I'm glad that you enjoy it. > > > I've a question about ReferenceSet. The name implies some relation to, > > well, sets. Is it planned that the set interface will be supported > > sometimes in the future? > > It may be, but notice that the "set" naming isn't about being able to do > Python-style set operations in this case, but rather about the fact that > this type offers access to "a set of referenced objects". > > > For example, if I have some classes 'User' and 'Group', I'd like to be > > able to do something like this: > > accessGroups = set([grp1, grp2, grp3]) > > if usr.groups & accessGroups: > > doSomething > > This does look like an interesting feature, and at first sight I can't > think of any incoveniences about it. It's been added it to the TODO > file for future consideration. > > Thanks for the suggestion.
I thought a bit more about my idea and had a look at storms code. I've now implemented something a bit different to my first suggestion. With the patch, the following is now possible (staying with the above example): usr.groups = (grp1, grp2) # setting an iterable to a ReferenceSet attribute usr.groups |= (grp3,) # updating the attribute ... and all the other set related operation. I believe that this allows quite compact and obvious code. The patch also includes some doctests and documentation in "tests/set_feature.txt" I hope you find this as usefull as I do :-) Cheers Stephan
# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: [EMAIL PROTECTED] # target_branch: http://bazaar.launchpad.net/~storm/storm/trunk # testament_sha1: 62bc2d1211d315ae03389493279d0de61654e5d5 # timestamp: 2007-12-13 19:11:54 +0100 # source_branch: . # base_revision_id: [EMAIL PROTECTED] # 7mdqxusap4ttpk1b # # Begin patch === added file 'tests/set_feature.txt' --- tests/set_feature.txt 1970-01-01 00:00:00 +0000 +++ tests/set_feature.txt 2007-12-13 17:19:52 +0000 @@ -0,0 +1,149 @@ + +[[TableOfContents]] + +==== Storm patch ==== +This document describes a little patch to storm to allow some +set like operation when using 'ReferenceSet' attributes. + +The added interface allows very concise and natural code if one is +used to the set operation. + +==== First Change ==== +add a '__set__' method to storm.references.ReferenceSet. +This allows to set any iterable to a ReferenceSet attribute +directly. Any element of that iterable has to be compatible, of course. + +Furthermore, this allows to use the augmented set operation on +such an attribute. + +==== Second Change ==== +add the set interface to storm.references.BoundReferenceSetBase. +They take any iterable as an argument. + +Added methods: +{{{ +update +__ior__ +intersection_update +__iand__ +difference_update +__isub__ +symmetric_difference_update +__ixor__ +}}} + +==== Example ==== +First, let's define basic 'User' and 'Group' classes which have a n:m +relationship: +{{{#!python +>>> from storm.locals import * +>>> class Group(object): +... __storm_table__ = "group_table" +... id = Int(primary=True) +... name = Unicode() +>>> +>>> class UserGroup(object): +... __storm_table__ = "user_group_table" +... __storm_primary__ = 'user_id', 'group_id' +... user_id = Int() +... group_id = Int() +>>> +>>> class User(object): +... __storm_table__ = "user_table" +... id = Int(primary=True) +... name = Unicode() +... groups = ReferenceSet(id, UserGroup.user_id, +... UserGroup.group_id, Group.id) +>>> +>>> database = create_database("sqlite:") +>>> store = Store(database) +>>> store.execute("CREATE TABLE group_table " +... "(id INTEGER PRIMARY KEY, name VARCHAR)") +<storm.databases.sqlite.SQLiteResult object at 0x...> +>>> store.execute("CREATE TABLE user_table " +... "(id INTEGER PRIMARY KEY, name VARCHAR)") +<storm.databases.sqlite.SQLiteResult object at 0x...> +>>> store.execute("CREATE TABLE user_group_table " +... "(user_id INTEGER, group_id INTEGER)") +<storm.databases.sqlite.SQLiteResult object at 0x...> +>>> +}}} + +First, let's define some groups and a user: + +{{{#!python +>>> group_1 = Group() +>>> group_1.name = u"group_1" +>>> group_2 = Group() +>>> group_2.name = u"group_2" +>>> group_3 = Group() +>>> group_3.name = u"group_3" +>>> store.add(group_1) +<Group object at 0x...> +>>> store.add(group_2) +<Group object at 0x...> +>>> store.add(group_3) +<Group object at 0x...> +>>> user = User() +>>> user.name = u"user" +>>> store.add(user) +<User object at 0x...> +>>> store.commit() +>>> +}}} + +Then, instead running + +{{{ +user.groups.add(group_1) +user.groups.add(group_2) +}}} + +we can just do + +{{{#!python +>>> user.groups = (group_1, group_2) +>>> +}}} + +let's check if everything worked as expected: +{{{#!python +>>> assert group_1 in user.groups +>>> assert group_2 in user.groups +>>> assert len(list(user.groups)) == 2 +>>> +}}} + +Next, make sure that group_3 is contained in users groups +{{{#!python +>>> user.groups |= (group_3,) +>>> assert group_3 in user.groups +>>> assert len(list(user.groups)) == 3 +>>> assert set(user.groups) & set((group_1,)) +>>> +}}} + +We don't like group_3 after all. Let's remove it again. +{{{#!python +>>> user.groups -= (group_3,) +>>> assert group_3 not in user.groups +>>> assert len(list(user.groups)) == 2 +>>> +}}} + +And here are the remaining operations: + +{{{#!python +>>> user.groups &= (group_2, group_3) +>>> assert group_2 in user.groups +>>> assert len(list(user.groups)) == 1 +>>> user.groups |= (group_1,) +>>> assert group_1 in user.groups +>>> assert group_2 in user.groups +>>> assert len(list(user.groups)) == 2 +>>> user.groups ^= (group_2, group_3) +>>> assert len(list(user.groups)) == 2 +>>> assert group_1 in user.groups +>>> assert group_3 in user.groups +>>> +}}} === modified file 'storm/references.py' --- storm/references.py 2007-10-24 21:27:46 +0000 +++ storm/references.py 2007-12-13 17:19:52 +0000 @@ -195,6 +195,20 @@ self._relation2, local, self._order_by) + + def __set__(self, local, value): + # value must be an iterable + rs = self.__get__(local) + if hasattr(rs, '_in_iop') and rs._in_iop: + # we are inside one of the __ixx__ operations + # the actual action already happened + rs._inop = False + else: + v = set(value) + r = set(rs) + rs._add_iter(v - r) + rs._rem_iter(r - v) + def _build_relations(self, used_cls): resolver = PropertyResolver(self, used_cls) @@ -236,6 +250,48 @@ def count(self): return self.find().count() + def _add_iter(self, to_add): + for item in to_add: + self.add(item) + return self + + def _rem_iter(self, to_remove): + for item in to_remove: + self.remove(item) + return self + + def update(self, other): + return self._add_iter(set(other) - set(self)) + + def __ior__(self, other): + self._in_iop = True + return self.update(other) + + def intersection_update(self, other): + me = set(self) + return self._rem_iter(me - (me & set(other))) + + def __iand__(self, other): + self._in_iop = True + return self.intersection_update(other) + + def difference_update(self, other): + return self._rem_iter(set(other)&set(self)) + + def __isub__(self, other): + self._in_iop = True + return self.difference_update(other) + + def symmetric_difference_update(self, other): + o = set(other) + to_remove = o & set(self) + self._rem_iter(to_remove) + return self._add_iter(o - to_remove) + + def __ixor__(self, other): + self._in_iop = True + return self.symmetric_difference_update(other) + class BoundReferenceSet(BoundReferenceSetBase): # Begin bundle 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 HIP+LuSKcKEhkd0ISg==
-- storm mailing list [email protected] Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/storm
