3.0 KiB
Libkosokoso -- simple tagging with sqlalchemy.
Libkosokoso provides compact and table-stingy tags for sqlalchemy-backed objects.
Usage
import libkosokoso as kk
import sqlalchemy as sa
base = sa.ext.declarative.declarative_base()
kk.__dict__.update(kk.init_base(base))
class Foo(kk.Taggable, base):
__tablename__ = 'foos'
a = Foo()
a.tags.append('tag1')
a.tags.extend(['tag2', 'tag3'])
and so forth. (Note that certain key steps, like connecting to a database, are omitted.)
The code above will generate tables like this:
foos:
db_id
1
kk_tag_associations:
db_id tag_id target_table target_id
1 1 foos 1
2 2 foos 1
3 3 foos 1
kk_tags:
db_id text
1 tag1
2 tag2
3 tag3
To access the tags:
for i in a.tags:
print(i)
tag1
tag2
tag3
The tags member gives you a list of tag strings. To access the tag objects, iterate the kk_tag_associations member:
return [ta.tag_obj for ta in a.kk_tag_associations]
This will bypass the association proxy and return a list of Tag objects. The tag object has a useful member, "collection", which provides direct access to the objects with this tag.
It's also possible to add kk.Tag objects directly to the tags property on a taggable object, and they'll be handled correctly. Similarly, it's possible to add objects to the collections member of a Tag object.
Design considerations
My basic reasoning was that at some point I'd have to go and muck about in the database by hand, and I wanted a structure that was easy to perceive and modify.
I also wanted to avoid modifying preexisting tables. This structure can be dropped into a live system without interfering with any current entities. (Likewise the reverse.)
At all times I aimed to have an interface that matches my intuitions. Hence things like being able to both add tags to an object and add objects to a tag, despite the massive increase in complexity for arguably minimal gain.
I explicitly did not aim for efficiency. The underlying code is object-happy and probably very inefficient.
Implementation notes
The init_base() call required to set up the library is an absolute mess. I'm pleased that I got it working, but kind of surprised.
The authors of SQLAlchemy would most likely be appalled. This model breaks the "relational" element of the database quite badly. One solution that might make everyone happy would be to add a tags table for every current type, and use a view to consolidate them if desired.
Why is it called that?
"kosokoso" is Japanese onomatopoeia for sneaking or whispering secretly. I picked the name because tags always feel like a kind of buzzing side channel to me.
(I also sometimes call it "guzuguzu", which is, like, slow. This took me way longer to write than I was expecting.)