111 lines
3.0 KiB
ReStructuredText
111 lines
3.0 KiB
ReStructuredText
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.)
|