Project

General

Profile

AQuickTutorial » History » Revision 9

Revision 8 (Aurynn Shaw, 01/22/2009 09:10 AM) → Revision 9/10 (Aurynn Shaw, 07/20/2010 10:31 AM)


 h1. Simpycity for People in a Hurry 

 

 And what to expect when you use it. 


 


 h2. 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. Setup 


 First, set up Simpycity: 

 To initialize a context: 
 <pre> 
 >>> from simpycity.core import Function 
 >>> from simpycity.context simpycity import Context config 

 >>> config.host = 'localhost' 
 >>> ctx config.port = Context("dbname=your_db user=aurynn password=12345 host=localhost port=5432") 5432 
 >>> config.user = 'user' 
 >>> config.password = 'password' 
 >>> config.database = 'dbname' 

 </pre> 

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


 h2. 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: 

 Then, create some basic functions. 
 <pre> 
 
 >>> f = ctx.Function("get_row",['id']) Function("get_row",['id']) 
 >>> f_all = ctx.Function("get_rows") Function("get_rows") 
 </pre> 

 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: 
 <pre> 
 
 >>> all_results = f_all() 
 </pre> 

 

 For get_row, it's also a standard the same Python call, supporting call semantics, both positional and keyword arguments: arguments being supported 
 <pre> 
 
 >>> result = f(1) # get id 1 
 >>> result = f(id=1) # also get id 1 
 </pre> 

 Result sets are handled as 

 The results (look kind of like) a List, which raises the side effect of being possible to exhaust memory by pulling the entire result set into memory. This needs to from psycopg2, and can be approached with caution. 

 This does allow for normal iteration to occur: iterated over as per normal: 
 <pre> 
 
 >>> for row in f_all(): all_result: 
 ...    # do row stuff 
 </pre> 

 In 

 and 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: 
 <pre> 
 >>> row = f(id=1) # row is a single row, not a list. 
 </pre>  

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

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

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

 h2. Queries 

 Queries behave exactly the same as Functions, and have the same arguments: 
 <pre> 
 >>> q = ctx.Raw("SELECT * FROM your_table WHERE id = %s", ['id']) 
 </pre> 

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

 h2. 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: 

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

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

 h3. table 

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

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

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

 h3. __load__ 

 __load__ is used by the Model object to handle loading from the database when the model is instanced with arguments, such as: row object. 
 <pre> 
 
 >>> class myModel(base): 
 ...     table row = ['id','description'] result.next() 
 ...     __load__ = ctx.Raw("SELECT id, description FROM your_table WHERE id = %s", ['id']) 
 ... 
 >>> m = myModel(1) 
 >>> n = myModel(id=1) 
 </pre> 

 __load__ can use any standard Simpycity callable. 

 h3. __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. 

 <pre> 
 >>> 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() 
 </pre> 

 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. 

 h3. 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: 

 <pre> 
 >>> class myModel(base): 
 ...     table row = ['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) 
 </pre> 

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

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