📄 tutorial.txt
字号:
[[TableOfContents]]==== It runs! ====This Storm tutorial is included in the source code attests/tutorial.txt, so that it may be tested and stayup to date.==== Importing ====Let's start by importing some names into the namespace.{{{#!python>>> from storm.locals import *>>>}}}==== Basic definition ====Now we define a type with some properties describing the informationwe're about to map.{{{#!python>>> class Person(object):... __storm_table__ = "person"... id = Int(primary=True)... name = Unicode()}}}Notice that this has no Storm-defined base class or constructor.==== Creating a database and the store ====We still don't have anyone to talk to, so let's define an in-memorySQLite database to play with, and a store using that database.{{{#!python>>> database = create_database("sqlite:")>>> store = Store(database)>>>}}}Three databases are supported at the moment: SQLite, MySQL and PostgreSQL.The parameter passed to `create_database()` is an URI, as follows:{{{database = create_database("scheme://username:password@hostname:port/database_name")}}}The scheme may be "sqlite", "postgres", or "mysql".Now we have to create the table that will actually hold the datafor our class.{{{#!python>>> store.execute("CREATE TABLE person "... "(id INTEGER PRIMARY KEY, name VARCHAR)")<storm.databases.sqlite.SQLiteResult object at 0x...>}}}We got a result back, but we don't care about it for now. We could alsouse `noresult=True` to avoid the result entirely.==== Creating an object ====Let's create an object of the defined class.{{{#!python>>> joe = Person()>>> joe.name = u"Joe Johnes">>> print "%r, %r" % (joe.id, joe.name)None, u'Joe Johnes'}}}So far this object has no connection to a database. Let's add it to thestore we've created above.{{{#!python>>> store.add(joe)<Person object at 0x...>>>> print "%r, %r" % (joe.id, joe.name)None, u'Joe Johnes'}}}Notice that the object wasn't changed, even after being added to thestore. That's because it wasn't flushed yet.==== The store of an object ====Once an object is added to a store, or retrieved from a store, it'srelation to that store is known. We can easily verify which storean object is bound.{{{>>> Store.of(joe) is storeTrue>>> Store.of(Person()) is NoneTrue}}}==== Finding an object ====Now, what would happen if we actually asked the store to give usthe person named ''Joe Johnes''?{{{#!python>>> person = store.find(Person, Person.name == u"Joe Johnes").one()>>> print "%r, %r" % (person.id, person.name)1, u'Joe Johnes'>>>}}}The person is there! Yeah, ok, you were expecting it. :-)We can also retrieve the object using its primary key.{{{#!python>>> store.get(Person, 1).nameu'Joe Johnes'}}}==== Caching behavior ====One interesting thing is that this person is actually Joe, right? We'vejust added this object, so there's only one Joe, why would there betwo different objects? There isn't.{{{#!python>>> person is joeTrue}}}What's going on behind the scenes is that each store has an objectcache. When an object is linked to a store, it will be cached bythe store for as long as there's a reference to the object somewhere,or while the object is dirty (has unflushed changes).Let's try to show that this is the case in practice.{{{#!python>>> del person>>> joe.tainted = True>>> joe.taintedTrue>>> joe = store.get(Person, 1)>>> joe.taintedTrue}}}Now, let's get rid of the last reference, and see if it's still tainted.{{{#!python>>> del joe>>> import gc>>> collected = gc.collect()>>> joe = store.get(Person, 1)>>> joe.taintedTraceback (most recent call last):...AttributeError: 'Person' object has no attribute 'tainted'>>> print "%r, %r" % (joe.id, joe.name)1, u'Joe Johnes'}}}So, basically the last reference disapeared, and the object gotdeallocated, so the store has loaded the object again for us.==== Flushing ====When we tried to find Joe in the database for the first time, we'venoticed that the `id` property was magically assigned. This happenedbecause the object was flushed implicitly so that the operation wouldaffect any pending changes as well.Flushes may also happen explicitly.{{{#!python>>> mary = Person()>>> mary.name = u"Mary Margaret">>> store.add(mary)<Person object at 0x...>>>> print "%r, %r" % (mary.id, mary.name)None, u'Mary Margaret'>>> store.flush()>>> print "%r, %r" % (mary.id, mary.name)2, u'Mary Margaret'}}}==== Changing objects with the Store ====Besides changing objects as usual, we can also benefit from the factthat objects are tied to a database to change them using expressions.{{{#!python>>> store.find(Person, Person.name == u"Mary Margaret").set(name=u"Mary Maggie")>>> mary.nameu'Mary Maggie'}}}This operation will touch every matching object in the database, andalso objects that are alive in memory.==== Committing ====Everything we've done so far is inside a transaction. At this point,we can either make these changes and any pending uncommitted changespersistent by committing them, or we can undo everything by rollingthem back.We'll commit them, with something as simple as{{{#!python>>> store.commit()>>>}}}That was straightforward. Everything is still the way it was, but nowchanges are there "for real".==== Rolling back ====Aborting changes is very straightforward as well.{{{#!python>>> joe.name = u"Tom Thomas">>>}}}Let's see if these changes are really being considered by Stormand by the database.{{{#!python>>> person = store.find(Person, Person.name == u"Tom Thomas").one()>>> person is joeTrue}}}Yes, they are. Now, for the magic step (suspense music, please).{{{#!python>>> store.rollback()>>>}}}Erm.. nothing happened?Actually, something happened.. with Joe. He's back!{{{#!python>>> print "%r, %r" % (joe.id, joe.name)1, u'Joe Johnes'}}}==== Constructors ====So, we've been working for too long with people only. Let's introducea new kind of data in our model: companies. For the company, we'lluse a constructor, just for the fun of it. It will be the simplestcompany class you've ever seen:{{{#!python>>> class Company(object):... __storm_table__ = "company"... id = Int(primary=True)... name = Unicode()...... def __init__(self, name):... self.name = name}}}Notice that the constructor parameter isn't optional. It could beoptional, if we wanted, but our companies always have names.Let's add the table for it.{{{#!python>>> store.execute("CREATE TABLE company "... "(id INTEGER PRIMARY KEY, name VARCHAR)", noresult=True)}}}Then, create a new company.{{{>>> circus = Company(u"Circus Inc.")>>> print "%r, %r" % (circus.id, circus.name)None, u'Circus Inc.'}}}The `id` is still undefined because we haven't flushed it. In fact,we haven't even '''added''' the company to the store. We'll dothat soon. Watch out.==== References and subclassing ====Now we want to assign some employees to our company. Rather thanredoing the Person definition, we'll keep it as it is, since it'sgeneral, and will create a new subclass of it for employees, whichinclude one extra field: the company id.{{{#!python>>> class Employee(Person):... __storm_table__ = "employee"... company_id = Int()... company = Reference(company_id, Company.id)...... def __init__(self, name):... self.name = name}}}Pay attention to that definiton for a moment. Notice that it doesn'tdefine what's already in person, and introduces the `company_id`,and a `company` property, which is a reference to another class. Italso has a constructor, but which leaves the company alone.As usual, we need a table. SQLite has no idea of what a foreign key is,so we'll not bother to define it.{{{#!python>>> store.execute("CREATE TABLE employee "... "(id INTEGER PRIMARY KEY, name VARCHAR, company_id INTEGER)",... noresult=True)}}}Let's give life to Ben now.{{{#!python>>> ben = store.add(Employee(u"Ben Bill"))>>> print "%r, %r, %r" % (ben.id, ben.name, ben.company_id)None, u'Ben Bill', None}}}We can see that they were not flushed yet. Even then, we can saythat Bill works on Circus.{{{#!python>>> ben.company = circus>>> print "%r, %r" % (ben.company_id, ben.company.name)None, u'Circus Inc.'}}}Of course, we still don't know the company id since it was notflushed to the database yet, and we didn't assign an id explicitly.Storm is keeping the relationship even then.If whatever is pending is flushed to the database (implicitly orexplicitly), objects will get their ids, and any references areupdated as well (before being flushed!).{{{#!python>>> store.flush()>>> print "%r, %r" % (ben.company_id, ben.company.name)1, u'Circus Inc.'}}}They're both flushed to the database. Now, notice that the Circuscompany wasn't added to the store explicitly in any moment. Stormwill do that automatically for referenced objects, for both objects(the referenced and the referencing one).
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -