6
6
import click
7
7
from django .core .serializers import serialize
8
8
from django .core .serializers .json import DjangoJSONEncoder
9
- from django .db .models .fields .related import ManyToManyField
10
9
11
- from sentry .backup .helpers import EXCLUDED_APPS
10
+ from sentry .backup .dependencies import sort_dependencies
12
11
13
12
UTC_0 = timezone (timedelta (hours = 0 ))
14
13
@@ -24,103 +23,6 @@ def default(self, obj):
24
23
return super ().default (obj )
25
24
26
25
27
- def sort_dependencies ():
28
- """
29
- Similar to Django's except that we discard the important of natural keys
30
- when sorting dependencies (i.e. it works without them).
31
- """
32
- from django .apps import apps
33
-
34
- from sentry .models .actor import Actor
35
- from sentry .models .team import Team
36
- from sentry .models .user import User
37
-
38
- # Process the list of models, and get the list of dependencies
39
- model_dependencies = []
40
- models = set ()
41
- for app_config in apps .get_app_configs ():
42
- if app_config .label in EXCLUDED_APPS :
43
- continue
44
-
45
- model_iterator = app_config .get_models ()
46
-
47
- for model in model_iterator :
48
- models .add (model )
49
- # Add any explicitly defined dependencies
50
- if hasattr (model , "natural_key" ):
51
- deps = getattr (model .natural_key , "dependencies" , [])
52
- if deps :
53
- deps = [apps .get_model (* d .split ("." )) for d in deps ]
54
- else :
55
- deps = []
56
-
57
- # Now add a dependency for any FK relation with a model that
58
- # defines a natural key
59
- for field in model ._meta .fields :
60
- rel_model = getattr (field .remote_field , "model" , None )
61
- if rel_model is not None and rel_model != model :
62
- # TODO(hybrid-cloud): actor refactor.
63
- # Add cludgy conditional preventing walking actor.team_id, actor.user_id
64
- # Which avoids circular imports
65
- if model == Actor and (rel_model == Team or rel_model == User ):
66
- continue
67
-
68
- deps .append (rel_model )
69
-
70
- # Also add a dependency for any simple M2M relation with a model
71
- # that defines a natural key. M2M relations with explicit through
72
- # models don't count as dependencies.
73
- many_to_many_fields = [
74
- field for field in model ._meta .get_fields () if isinstance (field , ManyToManyField )
75
- ]
76
- for field in many_to_many_fields :
77
- rel_model = getattr (field .remote_field , "model" , None )
78
- if rel_model is not None and rel_model != model :
79
- deps .append (rel_model )
80
-
81
- model_dependencies .append ((model , deps ))
82
-
83
- model_dependencies .reverse ()
84
- # Now sort the models to ensure that dependencies are met. This
85
- # is done by repeatedly iterating over the input list of models.
86
- # If all the dependencies of a given model are in the final list,
87
- # that model is promoted to the end of the final list. This process
88
- # continues until the input list is empty, or we do a full iteration
89
- # over the input models without promoting a model to the final list.
90
- # If we do a full iteration without a promotion, that means there are
91
- # circular dependencies in the list.
92
- model_list = []
93
- while model_dependencies :
94
- skipped = []
95
- changed = False
96
- while model_dependencies :
97
- model , deps = model_dependencies .pop ()
98
-
99
- # If all of the models in the dependency list are either already
100
- # on the final model list, or not on the original serialization list,
101
- # then we've found another model with all it's dependencies satisfied.
102
- found = True
103
- for candidate in ((d not in models or d in model_list ) for d in deps ):
104
- if not candidate :
105
- found = False
106
- if found :
107
- model_list .append (model )
108
- changed = True
109
- else :
110
- skipped .append ((model , deps ))
111
- if not changed :
112
- raise RuntimeError (
113
- "Can't resolve dependencies for %s in serialized app list."
114
- % ", " .join (
115
- f"{ model ._meta .app_label } .{ model ._meta .object_name } "
116
- for model , deps in sorted (skipped , key = lambda obj : obj [0 ].__name__ )
117
- )
118
- )
119
- model_dependencies = skipped
120
-
121
- return model_list
122
-
123
-
124
26
class OldExportConfig (NamedTuple ):
125
27
"""While we are migrating to the new backup system, we need to take care not to break the old
126
28
and relatively untested workflows. This model allows us to stub in the old configs."""
0 commit comments