Model » History » Version 6
Elmer de Looff, 2011-09-21 14:08
1 | 1 | Elmer de Looff | h1. Database abstraction model |
---|---|---|---|
2 | 1 | Elmer de Looff | |
3 | 1 | Elmer de Looff | h2. Goal of this component |
4 | 1 | Elmer de Looff | |
5 | 1 | Elmer de Looff | The µWeb framework provides a @model@ module with the intention of simplifying database access. The design goal is to provide a rich abstraction that |
6 | 1 | Elmer de Looff | * takes away the tedious work of retrieving, creating and deleting records |
7 | 1 | Elmer de Looff | * can load its parent objects automatically if so required |
8 | 1 | Elmer de Looff | * _does *not* get in the way of the developer_ |
9 | 1 | Elmer de Looff | |
10 | 1 | Elmer de Looff | Making database interaction easier without restricting the abilities of the developer is our main goal. Some default mechanisms make assumptions on the way the database is organised, but these are well-documented, and it's entirely possible to change the behavior of these mechanisms. |
11 | 1 | Elmer de Looff | |
12 | 2 | Elmer de Looff | h2. Using the Record |
13 | 1 | Elmer de Looff | |
14 | 2 | Elmer de Looff | The basic idea of the @Record@ class is that it is a container for your database records, with related records automatically loaded as needed, and custom methods that provide more info, child objects, etc. Outlined below are the default features available, with minimal configuration requirements. |
15 | 1 | Elmer de Looff | |
16 | 2 | Elmer de Looff | h3. Basic Record usage |
17 | 1 | Elmer de Looff | |
18 | 2 | Elmer de Looff | There are a few ways to use the @Record@ class. The direct way to create a @Record@ is to initiate it with a connection, and a dictionary of @field -> value@ information. The @Record@ is a dictionary subclass that largely copies all the functionality of a dictionary. Retrieving values for keys works exactly as you'd expect. |
19 | 1 | Elmer de Looff | |
20 | 2 | Elmer de Looff | h3. Creating your own @Record@ |
21 | 1 | Elmer de Looff | |
22 | 2 | Elmer de Looff | To create your own @Record@ subclass, nothing is required beyond the class name. The following example substitutes a complete working example: |
23 | 2 | Elmer de Looff | <pre><code class="python"> |
24 | 2 | Elmer de Looff | from underdark.uweb import model |
25 | 2 | Elmer de Looff | class Message(model.Record): |
26 | 2 | Elmer de Looff | """Abstraction class for messages stored in the database.""" |
27 | 2 | Elmer de Looff | </code></pre> |
28 | 1 | Elmer de Looff | |
29 | 2 | Elmer de Looff | h3. Primary field definition |
30 | 1 | Elmer de Looff | |
31 | 2 | Elmer de Looff | The @Record@ requires that a table has a single-field unique column. It's advisable for this to be a PRIMARY index in the database, though this is not required. This field is used to automatically look up a record if it is referenced and requested elsewhere. |
32 | 1 | Elmer de Looff | |
33 | 2 | Elmer de Looff | By default, this primary key field is assumed to be @ID@. If this is not the case for your table, you can easily change this by defining the @_PRIMARY_KEY@ class constant: |
34 | 1 | Elmer de Looff | |
35 | 2 | Elmer de Looff | <pre><code class="python"> |
36 | 2 | Elmer de Looff | from underdark.uweb import model |
37 | 2 | Elmer de Looff | class Country(model.Record): |
38 | 2 | Elmer de Looff | """Abstraction class for a country table. |
39 | 1 | Elmer de Looff | |
40 | 2 | Elmer de Looff | This class uses the ISO-3166-1 alpha2 country code as primary key. |
41 | 2 | Elmer de Looff | """ |
42 | 2 | Elmer de Looff | _PRIMARY_KEY = 'alpha2' |
43 | 2 | Elmer de Looff | </code></pre> |
44 | 1 | Elmer de Looff | |
45 | 2 | Elmer de Looff | h3. Class and table relation |
46 | 2 | Elmer de Looff | |
47 | 2 | Elmer de Looff | By default, the assumption is made that the table name is the same as the class name, with the first letter lowercase. *The table related to the class @Message@ would be @message@.* To change this behavior, assign the correct table name to the @_TABLE@ class constant. This new table name will then be used in all built-in Record methods: |
48 | 2 | Elmer de Looff | |
49 | 2 | Elmer de Looff | <pre><code class="python"> |
50 | 2 | Elmer de Looff | from underdark.uweb import model |
51 | 2 | Elmer de Looff | class Message(model.Record): |
52 | 2 | Elmer de Looff | """Abstraction class for messages stored in the database.""" |
53 | 2 | Elmer de Looff | _TABLE = 'MyMessage' |
54 | 2 | Elmer de Looff | </code></pre> |
55 | 2 | Elmer de Looff | |
56 | 6 | Elmer de Looff | h3. Record initialization |
57 | 1 | Elmer de Looff | |
58 | 6 | Elmer de Looff | Initializing a Record object requires a database connection as first argument, and a dictionary with the record's data as second argument. This second argument can, alternatively, be an iterator of key+value tuples. |
59 | 1 | Elmer de Looff | |
60 | 6 | Elmer de Looff | <pre><code class="python"> |
61 | 6 | Elmer de Looff | from underdark.uweb import model |
62 | 6 | Elmer de Looff | class Message(model.Record): |
63 | 6 | Elmer de Looff | """Abstraction class for messages stored in the database.""" |
64 | 1 | Elmer de Looff | |
65 | 6 | Elmer de Looff | # Caller side: |
66 | 6 | Elmer de Looff | >>> record = {'ID': 1, 'message': 'First message!', 'author': 'Elmer'} |
67 | 6 | Elmer de Looff | >>> message = model.Message(db_conn, record) |
68 | 6 | Elmer de Looff | >>> print message |
69 | 6 | Elmer de Looff | Message({'message': 'First message!', 'ID': 1, 'author': 'Elmer'}) |
70 | 6 | Elmer de Looff | </code></pre> |
71 | 1 | Elmer de Looff | |
72 | 6 | Elmer de Looff | This basic construction is rarely needed in code using the Record objects, but is important for alternative initializers, of which one is provided by default: |
73 | 6 | Elmer de Looff | |
74 | 6 | Elmer de Looff | h3. Alternative initializer: create Record from primary key |
75 | 6 | Elmer de Looff | |
76 | 6 | Elmer de Looff | On the caller side, it's impractical to first query the database, and then instantiate a Record subclass from that. Alternative initializers provide a solution without requiring module-level functions that have poor cohesion to the relevant class. Alternative initializers are @classmethods@, working not on instance, but aiming to create and return one. |
77 | 6 | Elmer de Looff | |
78 | 6 | Elmer de Looff | There is one such alternative initializer provided: @FromKey@, which loads a record from the database based on its primary key. Required for this to function are two arguments: A database connection, and the value for the primary key field: |
79 | 6 | Elmer de Looff | |
80 | 6 | Elmer de Looff | <pre><code class="python"> |
81 | 6 | Elmer de Looff | from underdark.uweb import model |
82 | 6 | Elmer de Looff | class Message(model.Record): |
83 | 6 | Elmer de Looff | """Abstraction class for messages stored in the database.""" |
84 | 6 | Elmer de Looff | |
85 | 6 | Elmer de Looff | # Caller side: |
86 | 6 | Elmer de Looff | >>> message = model.Message.FromKey(db_conn, 1) |
87 | 6 | Elmer de Looff | >>> print message |
88 | 6 | Elmer de Looff | Message({'message': u'First message!', 'ID': 1L, 'author': 'Elmer'}) |
89 | 6 | Elmer de Looff | # Unicode and long integer are side effects from the database read, not the Record class |
90 | 6 | Elmer de Looff | </code></pre> |
91 | 2 | Elmer de Looff | |
92 | 2 | Elmer de Looff | *N.B.* In the default implementation, fields that refer to a record in another table (@n to 1@ or @1 to 1@ relationships) *MUST have the name of that table.* |
93 | 2 | Elmer de Looff | For example: Given two tables `child` and `parent`. Entries in `child` that refer to their parent, must do so using a field called `parent` (not parentID or some such). If the table names are plural, the fields that refer to the relation should also have a pluralized name. |