Contact: fumanchu@aminus.org

Log in as guest/dejavu to create tickets

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

root/trunk/dejavu/schemas.py

Revision 525 (checked in by fumanchu, 5 years ago)

Wow. Long-standing bug in Schema.guid.

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