Project

General

Profile

Actions

AQuickTutorial » History » Revision 9

« Previous | Revision 9/10 (diff) | Next »
Aurynn Shaw, 07/20/2010 10:31 AM
Updating docs for 0.3.1.


Simpycity for People in a Hurry

And what to expect when you use it.

Beginning

The first step, once you've installed Simpycity, is to instance a Context. Everything in Simpycity derives from the base Context types, which keeps the basic connection handle consistent across all derived objects.

To initialize a context:

>>> from simpycity.context import Context
>>> ctx = Context("dbname=your_db user=aurynn password=12345 host=localhost port=5432")

As you can see, the startup semantics are identical to the underlying psycopg2 DSN string.

Some Functions

The power of Simpycity comes from being able to treat database objects and queries as callables - cleanly mapping Python semantics to PostgreSQL query semantics, without all the database interaction getting in the way.

To initialize functions, simply:

>>> f = ctx.Function("get_row",['id'])
>>> f_all = ctx.Function("get_rows")

In this, f_all maps to the stored procedure "get_rows", which takes no arguments.
f, on the other hand, maps to the stored procedure "get_row", which takes a single argument, which we've named id.

Argument names are completely arbitrary - they don't need to be named based on the underlying function arguments.

To call f_all, it's a standard python function:

>>> all_results = f_all()

For get_row, it's also a standard Python call, supporting both positional and keyword arguments:

>>> result = f(1) # get id 1
>>> result = f(id=1) # also get id 1

Result sets are handled as a List, which raises the side effect of being possible to exhaust memory by pulling the entire result set into memory. This needs to be approached with caution.

This does allow for normal iteration to occur:

>>> for row in f_all():
...  # do row stuff

In the case of a single result, by default, you'll end up with that one record, ONLY. This can cause confusion if you're not expecting it:

>>> row = f(id=1) # row is a single row, not a list.
 

If you always expect a list, you should do instead:

>>> f_all = ctx.Function("get_rows", returns_a="list")

"returns_a" coercing Simpycity into always returning a list of objects, similarly to if you'd natively gotten a list of objects.

Queries

Queries behave exactly the same as Functions, and have the same arguments:

>>> q = ctx.Raw("SELECT * FROM your_table WHERE id = %s", ['id'])

In all cases, %s mapping is handled by psycopg2, which performs correct parameterization.

Models

Models in Simpycity are a minimalistic construct to facilitate easy generation of complex business objects without compromising the underlying database representation.

We generally advise using a single base Model class that all other models derive from, such as:

>>> base = ctx.Model()
>>> class myModel(base):
...  pass

This allows for model modifications to be made globally in your application.

table

The table declaration in the model describes how the model is represented, and what items can be persisted to the database:

>>> class myModel(base):
...   table = ['id','description']

The declaration is intentionally simple, and (currently) we rely on psycopg2 and PostgreSQL doing the Right Thing when it comes to raising type coercion errors.

load

load is used by the Model object to handle loading from the database when the model is instanced with arguments, such as:

>>> class myModel(base):
...   table = ['id','description']
...   __load__ = ctx.Raw("SELECT id, description FROM your_table WHERE id = %s", ['id'])
...
>>> m = myModel(1)
>>> n = myModel(id=1)

load can use any standard Simpycity callable.

save

save allows for the ActiveRecord pattern of "m.save()" to work on Simpycity Models, allowing for utterly arbitrary control of the underlying save mechanism.

>>> class myModel(base):
...   table = ['id','description']
...   __load__ = ctx.Raw("SELECT id, description FROM your_table WHERE id = %s", ['id'])
...   __save__ = ctx.Function("update_my_table",['id','description'])
>>> m = myModel(1)
>>> m.description = "cheese" 
>>> m.save()
>>> ctx.commit()

Yes, you can change the id value on the model, however, this would merely break the model and prevent it from saving as expected.
If your underlying sproc was sufficiently intelligent, it could be coerced into doing an UPSERT, thus preventing any issues from occurring.

new

The final component of sensible models is the "new" structure. Simpycity does not directly support this, and requires either intelligent sprocs, or a simple pattern:

>>> class myModel(base):
...   table = ['id','description']
...   __load__ = ctx.Raw("SELECT id, description FROM your_table WHERE id = %s", ['id'])
...   __save__ = ctx.Function("update_my_table",['id','description'])
...
>>> new = ctx.Function("new_table_row",['description'], return_type=myModel)

return_type allows a Simpycity callable to declare that it returns the provided Model definition. This allows for the pattern of:

>>> from models import mymodel
>>> m = mymodel.myModel
>>> m = mymodel.new("a test")
>>> m.description
'a test'
>>> m.description = "Test successful" 
>>> m.save()
>>> ctx.commit()

Updated by Aurynn Shaw almost 14 years ago · 9 revisions