OLD | NEW |
(Empty) | |
| 1 from __future__ import unicode_literals |
| 2 |
| 3 |
| 4 class MigrationOptimizer(object): |
| 5 """ |
| 6 Powers the optimization process, where you provide a list of Operations |
| 7 and you are returned a list of equal or shorter length - operations |
| 8 are merged into one if possible. |
| 9 |
| 10 For example, a CreateModel and an AddField can be optimized into a |
| 11 new CreateModel, and CreateModel and DeleteModel can be optimized into |
| 12 nothing. |
| 13 """ |
| 14 |
| 15 def optimize(self, operations, app_label=None): |
| 16 """ |
| 17 Main optimization entry point. Pass in a list of Operation instances, |
| 18 get out a new list of Operation instances. |
| 19 |
| 20 Unfortunately, due to the scope of the optimization (two combinable |
| 21 operations might be separated by several hundred others), this can't be |
| 22 done as a peephole optimization with checks/output implemented on |
| 23 the Operations themselves; instead, the optimizer looks at each |
| 24 individual operation and scans forwards in the list to see if there |
| 25 are any matches, stopping at boundaries - operations which can't |
| 26 be optimized over (RunSQL, operations on the same field/model, etc.) |
| 27 |
| 28 The inner loop is run until the starting list is the same as the result |
| 29 list, and then the result is returned. This means that operation |
| 30 optimization must be stable and always return an equal or shorter list. |
| 31 |
| 32 The app_label argument is optional, but if you pass it you'll get more |
| 33 efficient optimization. |
| 34 """ |
| 35 # Internal tracking variable for test assertions about # of loops |
| 36 self._iterations = 0 |
| 37 while True: |
| 38 result = self.optimize_inner(operations, app_label) |
| 39 self._iterations += 1 |
| 40 if result == operations: |
| 41 return result |
| 42 operations = result |
| 43 |
| 44 def optimize_inner(self, operations, app_label=None): |
| 45 """ |
| 46 Inner optimization loop. |
| 47 """ |
| 48 new_operations = [] |
| 49 for i, operation in enumerate(operations): |
| 50 # Compare it to each operation after it |
| 51 for j, other in enumerate(operations[i + 1:]): |
| 52 in_between = operations[i + 1:i + j + 1] |
| 53 result = operation.reduce(other, in_between, app_label) |
| 54 if isinstance(result, list): |
| 55 # Optimize! Add result, then remaining others, then return |
| 56 new_operations.extend(result) |
| 57 new_operations.extend(in_between) |
| 58 new_operations.extend(operations[i + j + 2:]) |
| 59 return new_operations |
| 60 if not result: |
| 61 # We can't optimize across `other`. |
| 62 new_operations.append(operation) |
| 63 break |
| 64 else: |
| 65 new_operations.append(operation) |
| 66 return new_operations |
OLD | NEW |