We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Criar stubs para PK igual a zero
Durante a migração, registros filhos podem apontar para um registro pai com PK igual a zero. Atualmente esse valor é mudado para None, porém há campos que não podem ser nuláveis. Nestes casos, um stub de registro pai deve ser criado e o valor do campo do registro filho deve apontar para este novo stub.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `legacy/migration.py`
Content:
```
1 import re
2 
3 import pkg_resources
4 import yaml
5 from django.apps import apps
6 from django.apps.config import AppConfig
7 from django.core.exceptions import ObjectDoesNotExist
8 from django.db import connections, models
9 from django.db.models.base import ModelBase
10 from model_mommy import mommy
11 
12 from comissoes.models import Composicao, Participacao
13 from parlamentares.models import Parlamentar
14 from sessao.models import SessaoPlenaria
15 
16 # BASE ######################################################################
17 
18 #  apps to be migrated, in app dependency order (very important)
19 appconfs = [apps.get_app_config(n) for n in [
20     'parlamentares',
21     'comissoes',
22     'materia',
23     'norma',
24     'sessao',
25     'lexml',
26     'protocoloadm', ]]
27 
28 stubs_list = []
29 
30 name_sets = [set(m.__name__ for m in ac.get_models()) for ac in appconfs]
31 
32 # apps do not overlap
33 for s1 in name_sets:
34     for s2 in name_sets:
35         if s1 is not s2:
36             assert not s1.intersection(s2)
37 
38 # apps include all legacy models
39 legacy_app = apps.get_app_config('legacy')
40 legacy_model_names = set(m.__name__ for m in legacy_app.get_models())
41 
42 model_dict = {m.__name__: m for ac in appconfs for m in ac.get_models()}
43 
44 
45 # RENAMES ###################################################################
46 
47 MODEL_RENAME_PATTERN = re.compile('(.+) \((.+)\)')
48 
49 
50 def get_renames():
51     field_renames = {}
52     model_renames = {}
53     for app in appconfs:
54         app_rename_data = yaml.load(
55             pkg_resources.resource_string(app.module.__name__, 'legacy.yaml'))
56         for model_name, renames in app_rename_data.items():
57             match = MODEL_RENAME_PATTERN.match(model_name)
58             if match:
59                 model_name, old_name = match.groups()
60             else:
61                 old_name = None
62             model = getattr(app.models_module, model_name)
63             if old_name:
64                 model_renames[model] = old_name
65             field_renames[model] = renames
66 
67     # collect renames from parent classes
68     for model, renames in field_renames.items():
69         if any(parent in field_renames for parent in model.__mro__[1:]):
70             renames = {}
71             for parent in reversed(model.__mro__):
72                 if parent in field_renames:
73                     renames.update(field_renames[parent])
74             field_renames[model] = renames
75 
76     # remove abstract classes
77     field_renames = {m: r for m, r in field_renames.items()
78                      if not m._meta.abstract}
79 
80     return field_renames, model_renames
81 
82 
83 # MIGRATION #################################################################
84 
85 def info(msg):
86     print('INFO: ' + msg)
87 
88 
89 def warn(msg):
90     print('WARNING! ' + msg)
91 
92 
93 def get_fk_related(field, value, label=None):
94     if value is not None:
95         try:
96             value = field.related_model.objects.get(id=value)
97         except ObjectDoesNotExist:
98             msg = 'FK [%s] not found for value %s ' \
99                 '(in %s %s)' % (
100                     field.name, value,
101                     field.model.__name__, label or '---')
102             if value == 0:
103                 # we interpret FK == 0 as actually FK == NONE
104                 value = None
105                 warn(msg + ' => using NONE for zero value')
106             else:
107                 value = make_stub(field.related_model, value)
108                 stubs_list.append((value.id, field))
109                 warn(msg + ' => STUB CREATED')
110         else:
111             assert value
112     return value
113 
114 
115 def get_field(model, fieldname):
116     return model._meta.get_field(fieldname)
117 
118 
119 def exec_sql(sql, db='default'):
120     cursor = connections[db].cursor()
121     cursor.execute(sql)
122     return cursor
123 
124 
125 def iter_sql_records(sql, db):
126     class Record:
127         pass
128     cursor = exec_sql(sql, db)
129     fieldnames = [name[0] for name in cursor.description]
130     for row in cursor.fetchall():
131         record = Record()
132         record.__dict__.update(zip(fieldnames, row))
133         yield record
134 
135 
136 def save_with_id(new, id):
137     sequence_name = '%s_id_seq' % type(new)._meta.db_table
138     cursor = exec_sql('SELECT last_value from %s;' % sequence_name)
139     (last_value,) = cursor.fetchone()
140     if last_value == 1 or id != last_value + 1:
141         # we explicitly set the next id if last_value == 1
142         # because last_value == 1 for a table containing either 0 or 1 records
143         # (we would have trouble for id == 2 and a missing id == 1)
144         exec_sql('ALTER SEQUENCE %s RESTART WITH %s;' % (sequence_name, id))
145     new.save()
146     assert new.id == id, 'New id is different from provided!'
147 
148 
149 def make_stub(model, id):
150     new = mommy.prepare(model)
151     save_with_id(new, id)
152     return new
153 
154 
155 class DataMigrator:
156 
157     def __init__(self):
158         self.field_renames, self.model_renames = get_renames()
159 
160     def populate_renamed_fields(self, new, old):
161         renames = self.field_renames[type(new)]
162 
163         for field in new._meta.fields:
164             old_field_name = renames.get(field.name)
165             field_type = field.get_internal_type()
166 
167             if old_field_name:
168                 old_value = getattr(old, old_field_name)
169                 if isinstance(field, models.ForeignKey):
170                     old_type = type(old)  # not necessarily a model
171                     if hasattr(old_type, '_meta') and \
172                             old_type._meta.pk.name != 'id':
173                         label = old.pk
174                     else:
175                         label = '-- WITHOUT PK --'
176                     value = get_fk_related(field, old_value, label)
177                 else:
178                     value = getattr(old, old_field_name)
179                 if field_type == 'CharField' or field_type == 'TextField':
180                     if value is None:
181                         warn(
182                             "Field %s (%s) from model %s"
183                             " => settig empty string '' for %s value" %
184                             (field.name, field_type, field.model.__name__,
185                              value))
186                         value = ''
187                 setattr(new, field.name, value)
188 
189     def migrate(self, obj=appconfs):
190         # warning: model/app migration order is of utmost importance
191 
192         self.to_delete = []
193         info('Starting %s migration...' % obj)
194         self._do_migrate(obj)
195         # exclude logically deleted in legacy base
196         info('Deleting models with ind_excluido...')
197         for obj in self.to_delete:
198             obj.delete()
199         info('Deleting unnecessary stubs...')
200         self.delete_stubs()
201 
202     def _do_migrate(self, obj):
203         if isinstance(obj, AppConfig):
204             models_to_migrate = (model for model in obj.models.values()
205                                  if model in self.field_renames)
206             self._do_migrate(models_to_migrate)
207         elif isinstance(obj, ModelBase):
208             self.migrate_model(obj)
209         elif hasattr(obj, '__iter__'):
210             for item in obj:
211                 self._do_migrate(item)
212         else:
213             raise TypeError(
214                 'Parameter must be a Model, AppConfig or a sequence of them')
215 
216     def migrate_model(self, model):
217         print('Migrating %s...' % model.__name__)
218 
219         legacy_model_name = self.model_renames.get(model, model.__name__)
220         legacy_model = legacy_app.get_model(legacy_model_name)
221         legacy_pk_name = legacy_model._meta.pk.name
222 
223         # Clear all model entries
224         # They may have been created in a previous migration attempt
225         model.objects.all().delete()
226 
227         # setup migration strategy for tables with or without a pk
228         if legacy_pk_name == 'id':
229             # There is no pk in the legacy table
230             def save(new, old):
231                 new.save()
232 
233             old_records = iter_sql_records(
234                 'select * from ' + legacy_model._meta.db_table, 'legacy')
235         else:
236             def save(new, old):
237                 save_with_id(new, getattr(old, legacy_pk_name))
238 
239             old_records = legacy_model.objects.all().order_by(legacy_pk_name)
240 
241         adjust = MIGRATION_ADJUSTMENTS.get(model)
242 
243         # convert old records to new ones
244         for old in old_records:
245             new = model()
246             self.populate_renamed_fields(new, old)
247             if adjust:
248                 adjust(new, old)
249             save(new, old)
250             if getattr(old, 'ind_excluido', False):
251                 self.to_delete.append(new)
252 
253     def delete_stubs(self):
254         for line in stubs_list:
255             stub, field = line
256             # Filter all objects in model and delete from related model
257             # if quantity is equal to zero
258             if field.model.objects.filter(**{field.name: stub}).exists():
259                 field.related_model.objects.get(**{'id': stub}).delete()
260 
261 
262 def migrate(obj=appconfs):
263     dm = DataMigrator()
264     dm.migrate(obj)
265 
266 
267 # MIGRATION_ADJUSTMENTS #####################################################
268 
269 def adjust_participacao(new_participacao, old):
270     composicao = Composicao()
271     composicao.comissao, composicao.periodo = [
272         get_fk_related(Composicao._meta.get_field(name), value)
273         for name, value in (('comissao', old.cod_comissao),
274                             ('periodo', old.cod_periodo_comp))]
275     # check if there is already an "equal" one in the db
276     already_created = Composicao.objects.filter(
277         comissao=composicao.comissao, periodo=composicao.periodo)
278     if already_created:
279         assert len(already_created) == 1  # we must never have made 2 copies
280         [composicao] = already_created
281     else:
282         composicao.save()
283     new_participacao.composicao = composicao
284 
285 
286 def adjust_parlamentar(new_parlamentar, old):
287     value = new_parlamentar.unidade_deliberativa
288     # Field is defined as not null in legacy db,
289     # but data includes null values
290     #  => transform None to False
291     if value is None:
292         warn('null converted to False')
293         new_parlamentar.unidade_deliberativa = False
294 
295 
296 def adjust_sessaoplenaria(new, old):
297     assert not old.tip_expediente
298 
299 
300 MIGRATION_ADJUSTMENTS = {
301     Participacao: adjust_participacao,
302     Parlamentar: adjust_parlamentar,
303     SessaoPlenaria: adjust_sessaoplenaria,
304 }
305 
306 # CHECKS ####################################################################
307 
308 
309 def get_ind_excluido(obj):
310     legacy_model = legacy_app.get_model(type(obj).__name__)
311     return getattr(legacy_model.objects.get(
312         **{legacy_model._meta.pk.name: obj.id}), 'ind_excluido', False)
313 
314 
315 def check_app_no_ind_excluido(app):
316     for model in app.models.values():
317         assert not any(get_ind_excluido(obj) for obj in model.objects.all())
318     print('OK!')
319
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/legacy/migration.py b/legacy/migration.py
--- a/legacy/migration.py
+++ b/legacy/migration.py
@@ -6,6 +6,7 @@
 from django.apps.config import AppConfig
 from django.core.exceptions import ObjectDoesNotExist
 from django.db import connections, models
+from django.db.models import CharField, TextField
 from django.db.models.base import ModelBase
 from model_mommy import mommy
 
@@ -87,26 +88,41 @@
 
 
 def warn(msg):
-    print('WARNING! ' + msg)
+    print('CUIDADO! ' + msg)
 
 
 def get_fk_related(field, value, label=None):
+    fields_dict = {}
+
+    if value is None and field.null is False:
+        value = 0
     if value is not None:
         try:
             value = field.related_model.objects.get(id=value)
         except ObjectDoesNotExist:
-            msg = 'FK [%s] not found for value %s ' \
-                '(in %s %s)' % (
+            msg = 'FK [%s] não encontrada para valor %s ' \
+                '(em %s %s)' % (
                     field.name, value,
                     field.model.__name__, label or '---')
             if value == 0:
-                # we interpret FK == 0 as actually FK == NONE
-                value = None
-                warn(msg + ' => using NONE for zero value')
+                # se FK == 0, criamos um stub e colocamos o valor '????????
+                # para qualquer CharField ou TextField que possa haver
+                if not field.null:
+                    all_fields = field.related_model._meta.get_fields()
+                    fields_dict = {f.name: '????????????'[:f.max_length]
+                                   for f in all_fields
+                                   if isinstance(f, (CharField, TextField)) and
+                                   not f.choices and not f.blank}
+                    value = mommy.make(field.related_model,
+                                       **fields_dict)
+                    warn(msg + ' => STUB criada para campos não nuláveis!')
+                else:
+                    value = None
+                    warn(msg + ' => usando None para valores iguais a zero!')
             else:
                 value = make_stub(field.related_model, value)
                 stubs_list.append((value.id, field))
-                warn(msg + ' => STUB CREATED')
+                warn(msg + ' => STUB criada!')
         else:
             assert value
     return value
@@ -163,7 +179,6 @@
         for field in new._meta.fields:
             old_field_name = renames.get(field.name)
             field_type = field.get_internal_type()
-
             if old_field_name:
                 old_value = getattr(old, old_field_name)
                 if isinstance(field, models.ForeignKey):

{"golden_diff": "diff --git a/legacy/migration.py b/legacy/migration.py\n--- a/legacy/migration.py\n+++ b/legacy/migration.py\n@@ -6,6 +6,7 @@\n from django.apps.config import AppConfig\n from django.core.exceptions import ObjectDoesNotExist\n from django.db import connections, models\n+from django.db.models import CharField, TextField\n from django.db.models.base import ModelBase\n from model_mommy import mommy\n \n@@ -87,26 +88,41 @@\n \n \n def warn(msg):\n-    print('WARNING! ' + msg)\n+    print('CUIDADO! ' + msg)\n \n \n def get_fk_related(field, value, label=None):\n+    fields_dict = {}\n+\n+    if value is None and field.null is False:\n+        value = 0\n     if value is not None:\n         try:\n             value = field.related_model.objects.get(id=value)\n         except ObjectDoesNotExist:\n-            msg = 'FK [%s] not found for value %s ' \\\n-                '(in %s %s)' % (\n+            msg = 'FK [%s] n\u00e3o encontrada para valor %s ' \\\n+                '(em %s %s)' % (\n                     field.name, value,\n                     field.model.__name__, label or '---')\n             if value == 0:\n-                # we interpret FK == 0 as actually FK == NONE\n-                value = None\n-                warn(msg + ' => using NONE for zero value')\n+                # se FK == 0, criamos um stub e colocamos o valor '????????\n+                # para qualquer CharField ou TextField que possa haver\n+                if not field.null:\n+                    all_fields = field.related_model._meta.get_fields()\n+                    fields_dict = {f.name: '????????????'[:f.max_length]\n+                                   for f in all_fields\n+                                   if isinstance(f, (CharField, TextField)) and\n+                                   not f.choices and not f.blank}\n+                    value = mommy.make(field.related_model,\n+                                       **fields_dict)\n+                    warn(msg + ' => STUB criada para campos n\u00e3o nul\u00e1veis!')\n+                else:\n+                    value = None\n+                    warn(msg + ' => usando None para valores iguais a zero!')\n             else:\n                 value = make_stub(field.related_model, value)\n                 stubs_list.append((value.id, field))\n-                warn(msg + ' => STUB CREATED')\n+                warn(msg + ' => STUB criada!')\n         else:\n             assert value\n     return value\n@@ -163,7 +179,6 @@\n         for field in new._meta.fields:\n             old_field_name = renames.get(field.name)\n             field_type = field.get_internal_type()\n-\n             if old_field_name:\n                 old_value = getattr(old, old_field_name)\n                 if isinstance(field, models.ForeignKey):\n", "issue": "Criar stubs para PK igual a zero\nDurante a migra\u00e7\u00e3o, registros filhos podem apontar para um registro pai com PK igual a zero. Atualmente esse valor \u00e9 mudado para None, por\u00e9m h\u00e1 campos que n\u00e3o podem ser nul\u00e1veis. Nestes casos, um stub de registro pai deve ser criado e o valor do campo do registro filho deve apontar para este novo stub.\n\n", "before_files": [{"content": "import re\n\nimport pkg_resources\nimport yaml\nfrom django.apps import apps\nfrom django.apps.config import AppConfig\nfrom django.core.exceptions import ObjectDoesNotExist\nfrom django.db import connections, models\nfrom django.db.models.base import ModelBase\nfrom model_mommy import mommy\n\nfrom comissoes.models import Composicao, Participacao\nfrom parlamentares.models import Parlamentar\nfrom sessao.models import SessaoPlenaria\n\n# BASE ######################################################################\n\n#  apps to be migrated, in app dependency order (very important)\nappconfs = [apps.get_app_config(n) for n in [\n    'parlamentares',\n    'comissoes',\n    'materia',\n    'norma',\n    'sessao',\n    'lexml',\n    'protocoloadm', ]]\n\nstubs_list = []\n\nname_sets = [set(m.__name__ for m in ac.get_models()) for ac in appconfs]\n\n# apps do not overlap\nfor s1 in name_sets:\n    for s2 in name_sets:\n        if s1 is not s2:\n            assert not s1.intersection(s2)\n\n# apps include all legacy models\nlegacy_app = apps.get_app_config('legacy')\nlegacy_model_names = set(m.__name__ for m in legacy_app.get_models())\n\nmodel_dict = {m.__name__: m for ac in appconfs for m in ac.get_models()}\n\n\n# RENAMES ###################################################################\n\nMODEL_RENAME_PATTERN = re.compile('(.+) \\((.+)\\)')\n\n\ndef get_renames():\n    field_renames = {}\n    model_renames = {}\n    for app in appconfs:\n        app_rename_data = yaml.load(\n            pkg_resources.resource_string(app.module.__name__, 'legacy.yaml'))\n        for model_name, renames in app_rename_data.items():\n            match = MODEL_RENAME_PATTERN.match(model_name)\n            if match:\n                model_name, old_name = match.groups()\n            else:\n                old_name = None\n            model = getattr(app.models_module, model_name)\n            if old_name:\n                model_renames[model] = old_name\n            field_renames[model] = renames\n\n    # collect renames from parent classes\n    for model, renames in field_renames.items():\n        if any(parent in field_renames for parent in model.__mro__[1:]):\n            renames = {}\n            for parent in reversed(model.__mro__):\n                if parent in field_renames:\n                    renames.update(field_renames[parent])\n            field_renames[model] = renames\n\n    # remove abstract classes\n    field_renames = {m: r for m, r in field_renames.items()\n                     if not m._meta.abstract}\n\n    return field_renames, model_renames\n\n\n# MIGRATION #################################################################\n\ndef info(msg):\n    print('INFO: ' + msg)\n\n\ndef warn(msg):\n    print('WARNING! ' + msg)\n\n\ndef get_fk_related(field, value, label=None):\n    if value is not None:\n        try:\n            value = field.related_model.objects.get(id=value)\n        except ObjectDoesNotExist:\n            msg = 'FK [%s] not found for value %s ' \\\n                '(in %s %s)' % (\n                    field.name, value,\n                    field.model.__name__, label or '---')\n            if value == 0:\n                # we interpret FK == 0 as actually FK == NONE\n                value = None\n                warn(msg + ' => using NONE for zero value')\n            else:\n                value = make_stub(field.related_model, value)\n                stubs_list.append((value.id, field))\n                warn(msg + ' => STUB CREATED')\n        else:\n            assert value\n    return value\n\n\ndef get_field(model, fieldname):\n    return model._meta.get_field(fieldname)\n\n\ndef exec_sql(sql, db='default'):\n    cursor = connections[db].cursor()\n    cursor.execute(sql)\n    return cursor\n\n\ndef iter_sql_records(sql, db):\n    class Record:\n        pass\n    cursor = exec_sql(sql, db)\n    fieldnames = [name[0] for name in cursor.description]\n    for row in cursor.fetchall():\n        record = Record()\n        record.__dict__.update(zip(fieldnames, row))\n        yield record\n\n\ndef save_with_id(new, id):\n    sequence_name = '%s_id_seq' % type(new)._meta.db_table\n    cursor = exec_sql('SELECT last_value from %s;' % sequence_name)\n    (last_value,) = cursor.fetchone()\n    if last_value == 1 or id != last_value + 1:\n        # we explicitly set the next id if last_value == 1\n        # because last_value == 1 for a table containing either 0 or 1 records\n        # (we would have trouble for id == 2 and a missing id == 1)\n        exec_sql('ALTER SEQUENCE %s RESTART WITH %s;' % (sequence_name, id))\n    new.save()\n    assert new.id == id, 'New id is different from provided!'\n\n\ndef make_stub(model, id):\n    new = mommy.prepare(model)\n    save_with_id(new, id)\n    return new\n\n\nclass DataMigrator:\n\n    def __init__(self):\n        self.field_renames, self.model_renames = get_renames()\n\n    def populate_renamed_fields(self, new, old):\n        renames = self.field_renames[type(new)]\n\n        for field in new._meta.fields:\n            old_field_name = renames.get(field.name)\n            field_type = field.get_internal_type()\n\n            if old_field_name:\n                old_value = getattr(old, old_field_name)\n                if isinstance(field, models.ForeignKey):\n                    old_type = type(old)  # not necessarily a model\n                    if hasattr(old_type, '_meta') and \\\n                            old_type._meta.pk.name != 'id':\n                        label = old.pk\n                    else:\n                        label = '-- WITHOUT PK --'\n                    value = get_fk_related(field, old_value, label)\n                else:\n                    value = getattr(old, old_field_name)\n                if field_type == 'CharField' or field_type == 'TextField':\n                    if value is None:\n                        warn(\n                            \"Field %s (%s) from model %s\"\n                            \" => settig empty string '' for %s value\" %\n                            (field.name, field_type, field.model.__name__,\n                             value))\n                        value = ''\n                setattr(new, field.name, value)\n\n    def migrate(self, obj=appconfs):\n        # warning: model/app migration order is of utmost importance\n\n        self.to_delete = []\n        info('Starting %s migration...' % obj)\n        self._do_migrate(obj)\n        # exclude logically deleted in legacy base\n        info('Deleting models with ind_excluido...')\n        for obj in self.to_delete:\n            obj.delete()\n        info('Deleting unnecessary stubs...')\n        self.delete_stubs()\n\n    def _do_migrate(self, obj):\n        if isinstance(obj, AppConfig):\n            models_to_migrate = (model for model in obj.models.values()\n                                 if model in self.field_renames)\n            self._do_migrate(models_to_migrate)\n        elif isinstance(obj, ModelBase):\n            self.migrate_model(obj)\n        elif hasattr(obj, '__iter__'):\n            for item in obj:\n                self._do_migrate(item)\n        else:\n            raise TypeError(\n                'Parameter must be a Model, AppConfig or a sequence of them')\n\n    def migrate_model(self, model):\n        print('Migrating %s...' % model.__name__)\n\n        legacy_model_name = self.model_renames.get(model, model.__name__)\n        legacy_model = legacy_app.get_model(legacy_model_name)\n        legacy_pk_name = legacy_model._meta.pk.name\n\n        # Clear all model entries\n        # They may have been created in a previous migration attempt\n        model.objects.all().delete()\n\n        # setup migration strategy for tables with or without a pk\n        if legacy_pk_name == 'id':\n            # There is no pk in the legacy table\n            def save(new, old):\n                new.save()\n\n            old_records = iter_sql_records(\n                'select * from ' + legacy_model._meta.db_table, 'legacy')\n        else:\n            def save(new, old):\n                save_with_id(new, getattr(old, legacy_pk_name))\n\n            old_records = legacy_model.objects.all().order_by(legacy_pk_name)\n\n        adjust = MIGRATION_ADJUSTMENTS.get(model)\n\n        # convert old records to new ones\n        for old in old_records:\n            new = model()\n            self.populate_renamed_fields(new, old)\n            if adjust:\n                adjust(new, old)\n            save(new, old)\n            if getattr(old, 'ind_excluido', False):\n                self.to_delete.append(new)\n\n    def delete_stubs(self):\n        for line in stubs_list:\n            stub, field = line\n            # Filter all objects in model and delete from related model\n            # if quantity is equal to zero\n            if field.model.objects.filter(**{field.name: stub}).exists():\n                field.related_model.objects.get(**{'id': stub}).delete()\n\n\ndef migrate(obj=appconfs):\n    dm = DataMigrator()\n    dm.migrate(obj)\n\n\n# MIGRATION_ADJUSTMENTS #####################################################\n\ndef adjust_participacao(new_participacao, old):\n    composicao = Composicao()\n    composicao.comissao, composicao.periodo = [\n        get_fk_related(Composicao._meta.get_field(name), value)\n        for name, value in (('comissao', old.cod_comissao),\n                            ('periodo', old.cod_periodo_comp))]\n    # check if there is already an \"equal\" one in the db\n    already_created = Composicao.objects.filter(\n        comissao=composicao.comissao, periodo=composicao.periodo)\n    if already_created:\n        assert len(already_created) == 1  # we must never have made 2 copies\n        [composicao] = already_created\n    else:\n        composicao.save()\n    new_participacao.composicao = composicao\n\n\ndef adjust_parlamentar(new_parlamentar, old):\n    value = new_parlamentar.unidade_deliberativa\n    # Field is defined as not null in legacy db,\n    # but data includes null values\n    #  => transform None to False\n    if value is None:\n        warn('null converted to False')\n        new_parlamentar.unidade_deliberativa = False\n\n\ndef adjust_sessaoplenaria(new, old):\n    assert not old.tip_expediente\n\n\nMIGRATION_ADJUSTMENTS = {\n    Participacao: adjust_participacao,\n    Parlamentar: adjust_parlamentar,\n    SessaoPlenaria: adjust_sessaoplenaria,\n}\n\n# CHECKS ####################################################################\n\n\ndef get_ind_excluido(obj):\n    legacy_model = legacy_app.get_model(type(obj).__name__)\n    return getattr(legacy_model.objects.get(\n        **{legacy_model._meta.pk.name: obj.id}), 'ind_excluido', False)\n\n\ndef check_app_no_ind_excluido(app):\n    for model in app.models.values():\n        assert not any(get_ind_excluido(obj) for obj in model.objects.all())\n    print('OK!')\n", "path": "legacy/migration.py"}], "after_files": [{"content": "import re\n\nimport pkg_resources\nimport yaml\nfrom django.apps import apps\nfrom django.apps.config import AppConfig\nfrom django.core.exceptions import ObjectDoesNotExist\nfrom django.db import connections, models\nfrom django.db.models import CharField, TextField\nfrom django.db.models.base import ModelBase\nfrom model_mommy import mommy\n\nfrom comissoes.models import Composicao, Participacao\nfrom parlamentares.models import Parlamentar\nfrom sessao.models import SessaoPlenaria\n\n# BASE ######################################################################\n\n#  apps to be migrated, in app dependency order (very important)\nappconfs = [apps.get_app_config(n) for n in [\n    'parlamentares',\n    'comissoes',\n    'materia',\n    'norma',\n    'sessao',\n    'lexml',\n    'protocoloadm', ]]\n\nstubs_list = []\n\nname_sets = [set(m.__name__ for m in ac.get_models()) for ac in appconfs]\n\n# apps do not overlap\nfor s1 in name_sets:\n    for s2 in name_sets:\n        if s1 is not s2:\n            assert not s1.intersection(s2)\n\n# apps include all legacy models\nlegacy_app = apps.get_app_config('legacy')\nlegacy_model_names = set(m.__name__ for m in legacy_app.get_models())\n\nmodel_dict = {m.__name__: m for ac in appconfs for m in ac.get_models()}\n\n\n# RENAMES ###################################################################\n\nMODEL_RENAME_PATTERN = re.compile('(.+) \\((.+)\\)')\n\n\ndef get_renames():\n    field_renames = {}\n    model_renames = {}\n    for app in appconfs:\n        app_rename_data = yaml.load(\n            pkg_resources.resource_string(app.module.__name__, 'legacy.yaml'))\n        for model_name, renames in app_rename_data.items():\n            match = MODEL_RENAME_PATTERN.match(model_name)\n            if match:\n                model_name, old_name = match.groups()\n            else:\n                old_name = None\n            model = getattr(app.models_module, model_name)\n            if old_name:\n                model_renames[model] = old_name\n            field_renames[model] = renames\n\n    # collect renames from parent classes\n    for model, renames in field_renames.items():\n        if any(parent in field_renames for parent in model.__mro__[1:]):\n            renames = {}\n            for parent in reversed(model.__mro__):\n                if parent in field_renames:\n                    renames.update(field_renames[parent])\n            field_renames[model] = renames\n\n    # remove abstract classes\n    field_renames = {m: r for m, r in field_renames.items()\n                     if not m._meta.abstract}\n\n    return field_renames, model_renames\n\n\n# MIGRATION #################################################################\n\ndef info(msg):\n    print('INFO: ' + msg)\n\n\ndef warn(msg):\n    print('CUIDADO! ' + msg)\n\n\ndef get_fk_related(field, value, label=None):\n    fields_dict = {}\n\n    if value is None and field.null is False:\n        value = 0\n    if value is not None:\n        try:\n            value = field.related_model.objects.get(id=value)\n        except ObjectDoesNotExist:\n            msg = 'FK [%s] n\u00e3o encontrada para valor %s ' \\\n                '(em %s %s)' % (\n                    field.name, value,\n                    field.model.__name__, label or '---')\n            if value == 0:\n                # se FK == 0, criamos um stub e colocamos o valor '????????\n                # para qualquer CharField ou TextField que possa haver\n                if not field.null:\n                    all_fields = field.related_model._meta.get_fields()\n                    fields_dict = {f.name: '????????????'[:f.max_length]\n                                   for f in all_fields\n                                   if isinstance(f, (CharField, TextField)) and\n                                   not f.choices and not f.blank}\n                    value = mommy.make(field.related_model,\n                                       **fields_dict)\n                    warn(msg + ' => STUB criada para campos n\u00e3o nul\u00e1veis!')\n                else:\n                    value = None\n                    warn(msg + ' => usando None para valores iguais a zero!')\n            else:\n                value = make_stub(field.related_model, value)\n                stubs_list.append((value.id, field))\n                warn(msg + ' => STUB criada!')\n        else:\n            assert value\n    return value\n\n\ndef get_field(model, fieldname):\n    return model._meta.get_field(fieldname)\n\n\ndef exec_sql(sql, db='default'):\n    cursor = connections[db].cursor()\n    cursor.execute(sql)\n    return cursor\n\n\ndef iter_sql_records(sql, db):\n    class Record:\n        pass\n    cursor = exec_sql(sql, db)\n    fieldnames = [name[0] for name in cursor.description]\n    for row in cursor.fetchall():\n        record = Record()\n        record.__dict__.update(zip(fieldnames, row))\n        yield record\n\n\ndef save_with_id(new, id):\n    sequence_name = '%s_id_seq' % type(new)._meta.db_table\n    cursor = exec_sql('SELECT last_value from %s;' % sequence_name)\n    (last_value,) = cursor.fetchone()\n    if last_value == 1 or id != last_value + 1:\n        # we explicitly set the next id if last_value == 1\n        # because last_value == 1 for a table containing either 0 or 1 records\n        # (we would have trouble for id == 2 and a missing id == 1)\n        exec_sql('ALTER SEQUENCE %s RESTART WITH %s;' % (sequence_name, id))\n    new.save()\n    assert new.id == id, 'New id is different from provided!'\n\n\ndef make_stub(model, id):\n    new = mommy.prepare(model)\n    save_with_id(new, id)\n    return new\n\n\nclass DataMigrator:\n\n    def __init__(self):\n        self.field_renames, self.model_renames = get_renames()\n\n    def populate_renamed_fields(self, new, old):\n        renames = self.field_renames[type(new)]\n\n        for field in new._meta.fields:\n            old_field_name = renames.get(field.name)\n            field_type = field.get_internal_type()\n            if old_field_name:\n                old_value = getattr(old, old_field_name)\n                if isinstance(field, models.ForeignKey):\n                    old_type = type(old)  # not necessarily a model\n                    if hasattr(old_type, '_meta') and \\\n                            old_type._meta.pk.name != 'id':\n                        label = old.pk\n                    else:\n                        label = '-- WITHOUT PK --'\n                    value = get_fk_related(field, old_value, label)\n                else:\n                    value = getattr(old, old_field_name)\n                if field_type == 'CharField' or field_type == 'TextField':\n                    if value is None:\n                        warn(\n                            \"Field %s (%s) from model %s\"\n                            \" => settig empty string '' for %s value\" %\n                            (field.name, field_type, field.model.__name__,\n                             value))\n                        value = ''\n                setattr(new, field.name, value)\n\n    def migrate(self, obj=appconfs):\n        # warning: model/app migration order is of utmost importance\n\n        self.to_delete = []\n        info('Starting %s migration...' % obj)\n        self._do_migrate(obj)\n        # exclude logically deleted in legacy base\n        info('Deleting models with ind_excluido...')\n        for obj in self.to_delete:\n            obj.delete()\n        info('Deleting unnecessary stubs...')\n        self.delete_stubs()\n\n    def _do_migrate(self, obj):\n        if isinstance(obj, AppConfig):\n            models_to_migrate = (model for model in obj.models.values()\n                                 if model in self.field_renames)\n            self._do_migrate(models_to_migrate)\n        elif isinstance(obj, ModelBase):\n            self.migrate_model(obj)\n        elif hasattr(obj, '__iter__'):\n            for item in obj:\n                self._do_migrate(item)\n        else:\n            raise TypeError(\n                'Parameter must be a Model, AppConfig or a sequence of them')\n\n    def migrate_model(self, model):\n        print('Migrating %s...' % model.__name__)\n\n        legacy_model_name = self.model_renames.get(model, model.__name__)\n        legacy_model = legacy_app.get_model(legacy_model_name)\n        legacy_pk_name = legacy_model._meta.pk.name\n\n        # Clear all model entries\n        # They may have been created in a previous migration attempt\n        model.objects.all().delete()\n\n        # setup migration strategy for tables with or without a pk\n        if legacy_pk_name == 'id':\n            # There is no pk in the legacy table\n            def save(new, old):\n                new.save()\n\n            old_records = iter_sql_records(\n                'select * from ' + legacy_model._meta.db_table, 'legacy')\n        else:\n            def save(new, old):\n                save_with_id(new, getattr(old, legacy_pk_name))\n\n            old_records = legacy_model.objects.all().order_by(legacy_pk_name)\n\n        adjust = MIGRATION_ADJUSTMENTS.get(model)\n\n        # convert old records to new ones\n        for old in old_records:\n            new = model()\n            self.populate_renamed_fields(new, old)\n            if adjust:\n                adjust(new, old)\n            save(new, old)\n            if getattr(old, 'ind_excluido', False):\n                self.to_delete.append(new)\n\n    def delete_stubs(self):\n        for line in stubs_list:\n            stub, field = line\n            # Filter all objects in model and delete from related model\n            # if quantity is equal to zero\n            if field.model.objects.filter(**{field.name: stub}).exists():\n                field.related_model.objects.get(**{'id': stub}).delete()\n\n\ndef migrate(obj=appconfs):\n    dm = DataMigrator()\n    dm.migrate(obj)\n\n\n# MIGRATION_ADJUSTMENTS #####################################################\n\ndef adjust_participacao(new_participacao, old):\n    composicao = Composicao()\n    composicao.comissao, composicao.periodo = [\n        get_fk_related(Composicao._meta.get_field(name), value)\n        for name, value in (('comissao', old.cod_comissao),\n                            ('periodo', old.cod_periodo_comp))]\n    # check if there is already an \"equal\" one in the db\n    already_created = Composicao.objects.filter(\n        comissao=composicao.comissao, periodo=composicao.periodo)\n    if already_created:\n        assert len(already_created) == 1  # we must never have made 2 copies\n        [composicao] = already_created\n    else:\n        composicao.save()\n    new_participacao.composicao = composicao\n\n\ndef adjust_parlamentar(new_parlamentar, old):\n    value = new_parlamentar.unidade_deliberativa\n    # Field is defined as not null in legacy db,\n    # but data includes null values\n    #  => transform None to False\n    if value is None:\n        warn('null converted to False')\n        new_parlamentar.unidade_deliberativa = False\n\n\ndef adjust_sessaoplenaria(new, old):\n    assert not old.tip_expediente\n\n\nMIGRATION_ADJUSTMENTS = {\n    Participacao: adjust_participacao,\n    Parlamentar: adjust_parlamentar,\n    SessaoPlenaria: adjust_sessaoplenaria,\n}\n\n# CHECKS ####################################################################\n\n\ndef get_ind_excluido(obj):\n    legacy_model = legacy_app.get_model(type(obj).__name__)\n    return getattr(legacy_model.objects.get(\n        **{legacy_model._meta.pk.name: obj.id}), 'ind_excluido', False)\n\n\ndef check_app_no_ind_excluido(app):\n    for model in app.models.values():\n        assert not any(get_ind_excluido(obj) for obj in model.objects.all())\n    print('OK!')\n", "path": "legacy/migration.py"}]}