Contact: fumanchu@aminus.org

Log in as guest/dejavu to create tickets

I think I've seen this ORM somewhere before...

root/trunk/schemas.py

Revision 303 (checked in by fumanchu, 7 years ago)

Initial fix for #4 (transaction support). Tests pass, but this is not to be used in production yet!

  • Property svn:eol-style set to native
Line 
1
2 from dejavu import errors, units
3
4
5 __all__ = ['DeployedVersion', 'Schema',
6            ]
7
8
9 class DeployedVersion(units.Unit):
10     """Which schema version the current deployment has reached."""
11     ID = units.UnitProperty(unicode)
12     Version = units.UnitProperty(int)
13     sequencer = units.UnitSequencerNull()
14
15
16 class Schema(object):
17     """Schema versioning tool for Dejavu Units.
18     
19     Make a subclass of this base class to manage changes to your model layer
20     (Units and UnitProperties) as your application grows. Each time you make
21     a change to your model, add a new upgrade_to_X method to your subclass,
22     and increment the "latest" attribute to match:
23     
24     class MySchema(dejavu.Schema):
25         
26         latest = 1
27         
28         def upgrade_to_1(self):
29             # Remove Employee.ZipCode and add .StartDate
30             self.arena.drop_property(Employee, "ZipCode")
31             self.arena.add_property(Employee, "StartDate")
32     
33     Then, in your deployment scripts, you can choose how much you want to
34     automate control of the schema.
35     
36         schema = MySchema(myarena)
37         
38         if cmd == 'upgrade':
39             schema.upgrade()
40         elif cmd == 'install':
41             schema.assert_storage()
42         else:
43             schema.assert_version()
44     """
45    
46     guid = ""
47     latest = 0
48     stage = None
49    
50     def __init__(self, arena):
51         # Since schema upgrades may take some time, we keep track of our
52         # own processing state. Legal values are:
53         #   None              = not working on an upgrade
54         #   0 to self.latest  = working on an upgrade to this version
55         self.stage = None
56        
57         self.arena = arena
58         arena.register(DeployedVersion)
59         if not arena.has_storage(DeployedVersion):
60             arena.create_storage(DeployedVersion)
61         self._deployed_unit = None
62    
63     def deployed_unit(self, default=None):
64         """Retrieve our DeployedVersion unit, optionally creating it."""
65         if self._deployed_unit is None:
66             box = self.arena.new_sandbox()
67             try:
68                 du = box.unit(DeployedVersion, ID=self.guid)
69                 if du is None:
70                     if default is None:
71                         raise errors.DejavuError("Missing DeployedVersion unit.")
72                     else:
73                         du = DeployedVersion(ID=self.guid, Version=default)
74                         box.memorize(du)
75                 self._deployed_unit = du
76             finally:
77                 box.flush_all()
78         return self._deployed_unit
79    
80     def versioned(self):
81         """Return True if this installation has a DeployedVersion, false otherwise."""
82         box = self.arena.new_sandbox()
83         try:
84             return bool(box.unit(DeployedVersion, ID=self.guid))
85         finally:
86             box.flush_all()
87    
88     def _get_dep(self):
89         # Note that we default the Version to latest.
90         # This allows new installs to skip all the upgrade steps,
91         # and just use the latest class definitions when they call
92         # assert_storage. However, it means that if you deploy your
93         # apps for a while without a Schema, and then introduce one
94         # later, you must manually decrement DeployedVersion from
95         # "latest" to the actual deployed version *before* running
96         # your app for the first time (or things will break due to
97         # the difference between the latest and deployed schema).
98         return self.deployed_unit(self.latest).Version
99    
100     def _set_dep(self, newvalue):
101         depunit = self.deployed_unit(newvalue)
102         depunit.Version = newvalue
103         # Skip the sandbox, so we can save without repress
104         self.arena.storage(DeployedVersion).save(depunit, forceSave=True)
105    
106     deployed = property(_get_dep, _set_dep, doc="Deployed version")
107    
108     def upgrade(self, version=None):
109         """Upgrade deployment to version arg [latest]. Idempotent."""
110         if version is None:
111             version = self.latest
112        
113         # Run upgrade_to_X methods.
114         if self.deployed > version:
115             raise errors.DejavuError("Deployed version is greater than latest version.")
116         for step in range(self.deployed, version):
117             self.stage = step
118             procedure = getattr(self, "upgrade_to_%s" % (step + 1), None)
119             if procedure:
120                 procedure()
121             self.deployed = step + 1
122        
123         self.stage = None
124    
125     def upgrade_to_0(self):
126         pass
127    
128     def assert_storage(self):
129         """Assert that each class has storage reserved for it.
130         
131         You may choose to call this method in an admin script (at the
132         discretion of the deployer), or on application startup. If you
133         call this method on every application start, you should do it
134         after calling upgrade(). Once all of the upgrade methods are done,
135         you can then safely use this method to assert the _final_ schemas
136         for all classes. Note that, if the DeployedVersion unit was just
137         created (our actual deployed version was unknown), then some of
138         the upgrade methods may fail. However, they could fail just as
139         easily if you run assert_storage before the upgrade methods.
140         """
141        
142         classes = self.arena._registered_classes.keys()
143         if DeployedVersion in classes:
144             classes.remove(DeployedVersion)
145        
146         for cls in classes:
147             if not self.arena.has_storage(cls):
148                 self.arena.create_storage(cls)
149    
150     def assert_version(self):
151         """Die if schema version is missing or out of sync."""
152         msg = None
153         if not self.versioned:
154             msg = ("This application now uses a versioned schema, but the "
155                    "installed version could not be determined. You must "
156                    "either install the application or manually set the "
157                    "version number.")
158         if self.deployed > self.latest:
159             msg = "Deployed version is greater than the latest version!?!"
160         if self.deployed < self.latest:
161             msg = ("Deployed version is less than the latest version. "
162                    "You must upgrade before proceeding.")
163         if msg:
164             raise errors.DejavuError(msg)
165
Note: See TracBrowser for help on using the browser.