Skip to content

Commit 8156023

Browse files
committed
This is a series of 50 migrations that changes the Document and DocAlias
primary keys from character strings to integers, and makes corresponding code changes. This was prompted by database limitations discovered when trying to make DocAlias use a m2m document field; with 255 long strings as primary keys for Document and DocAlias this violated the MySQL database limitations. Changing the primary keys to integers should also improve efficiency. Due to the data migrations which create the new integer primary keys and adds corresponding integer foreign keys matching the previous string foreign keys in all tables having foreign keys to Document and DocAlias, some of these migrations take a long time. The total set of migrations are expected to have a runtime on the order of 2 hours. - Legacy-Id: 16237
1 parent 1f949d7 commit 8156023

88 files changed

Lines changed: 4657 additions & 127 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.11.20 on 2019-05-21 14:23
3+
from __future__ import unicode_literals
4+
5+
from django.db import migrations, models
6+
import django.db.models.deletion
7+
import ietf.utils.models
8+
9+
10+
class Migration(migrations.Migration):
11+
12+
dependencies = [
13+
('community', '0002_auto_20180220_1052'),
14+
]
15+
16+
operations = [
17+
migrations.CreateModel(
18+
name='CommunityListDocs',
19+
fields=[
20+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21+
('communitylist', ietf.utils.models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='community.CommunityList')),
22+
('document', ietf.utils.models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='doc.Document', to_field=b'id')),
23+
],
24+
),
25+
migrations.AddField(
26+
model_name='communitylist',
27+
name='added_docs2',
28+
field=models.ManyToManyField(related_name='communitylists', through='community.CommunityListDocs', to='doc.Document'),
29+
),
30+
migrations.CreateModel(
31+
name='SearchRuleDocs',
32+
fields=[
33+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
34+
('document', ietf.utils.models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='doc.Document', to_field=b'id')),
35+
('searchrule', ietf.utils.models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='community.SearchRule')),
36+
],
37+
),
38+
migrations.AddField(
39+
model_name='searchrule',
40+
name='name_contains_index2',
41+
field=models.ManyToManyField(related_name='searchrules', through='community.SearchRuleDocs', to='doc.Document'),
42+
),
43+
]
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.11.20 on 2019-05-21 14:27
3+
from __future__ import unicode_literals
4+
5+
import sys
6+
7+
from tqdm import tqdm
8+
9+
from django.db import migrations
10+
11+
12+
def forward(apps, schema_editor):
13+
14+
Document = apps.get_model('doc','Document')
15+
CommunityList = apps.get_model('community', 'CommunityList')
16+
CommunityListDocs = apps.get_model('community', 'CommunityListDocs')
17+
SearchRule = apps.get_model('community', 'SearchRule')
18+
SearchRuleDocs = apps.get_model('community', 'SearchRuleDocs')
19+
20+
# Document id fixup ------------------------------------------------------------
21+
22+
objs = Document.objects.in_bulk()
23+
nameid = { o.name: o.id for id, o in objs.iteritems() }
24+
25+
sys.stderr.write('\n')
26+
27+
sys.stderr.write(' %s.%s:\n' % (CommunityList.__name__, 'added_docs'))
28+
count = 0
29+
for l in tqdm(CommunityList.objects.all()):
30+
for d in l.added_docs.all():
31+
count += 1
32+
CommunityListDocs.objects.get_or_create(communitylist=l, document_id=nameid[d.name])
33+
sys.stderr.write(' %s CommunityListDocs objects created\n' % (count, ))
34+
35+
sys.stderr.write(' %s.%s:\n' % (SearchRule.__name__, 'name_contains_index'))
36+
count = 0
37+
for r in tqdm(SearchRule.objects.all()):
38+
for d in r.name_contains_index.all():
39+
count += 1
40+
SearchRuleDocs.objects.get_or_create(searchrule=r, document_id=nameid[d.name])
41+
sys.stderr.write(' %s SearchRuleDocs objects created\n' % (count, ))
42+
43+
def reverse(apps, schema_editor):
44+
pass
45+
46+
class Migration(migrations.Migration):
47+
48+
dependencies = [
49+
('community', '0003_add_communitylist_docs2_m2m'),
50+
('doc', '0014_set_document_docalias_id'),
51+
]
52+
53+
operations = [
54+
migrations.RunPython(forward, reverse),
55+
]
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.11.20 on 2019-05-22 08:15
3+
from __future__ import unicode_literals
4+
5+
from django.db import migrations
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
('community', '0004_set_document_m2m_keys'),
12+
]
13+
14+
# The implementation of AlterField in Django 1.11 applies
15+
# 'ALTER TABLE <table> MODIFY <field> ...;' in order to fix foregn keys
16+
# to the altered field, but as it seems does _not_ fix up m2m
17+
# intermediary tables in an equivalent manner, so here we remove and
18+
# then recreate the m2m tables so they will have the appropriate field
19+
# types.
20+
21+
operations = [
22+
# Remove fields
23+
migrations.RemoveField(
24+
model_name='communitylist',
25+
name='added_docs',
26+
),
27+
migrations.RemoveField(
28+
model_name='searchrule',
29+
name='name_contains_index',
30+
),
31+
]
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.11.20 on 2019-05-22 08:15
3+
from __future__ import unicode_literals
4+
5+
from django.db import migrations, models
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
('community', '0005_1_del_docs_m2m_table'),
12+
]
13+
14+
# The implementation of AlterField in Django 1.11 applies
15+
# 'ALTER TABLE <table> MODIFY <field> ...;' in order to fix foregn keys
16+
# to the altered field, but as it seems does _not_ fix up m2m
17+
# intermediary tables in an equivalent manner, so here we remove and
18+
# then recreate the m2m tables so they will have the appropriate field
19+
# types.
20+
21+
operations = [
22+
# Add fields back (will create the m2m tables with the right field types)
23+
migrations.AddField(
24+
model_name='communitylist',
25+
name='added_docs',
26+
field=models.ManyToManyField(to='doc.Document'),
27+
),
28+
migrations.AddField(
29+
model_name='searchrule',
30+
name='name_contains_index',
31+
field=models.ManyToManyField(to='doc.Document'),
32+
),
33+
]
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.11.20 on 2019-05-27 05:56
3+
from __future__ import unicode_literals
4+
5+
from django.db import migrations
6+
7+
import sys, time
8+
9+
from tqdm import tqdm
10+
11+
12+
def forward(apps, schema_editor):
13+
14+
CommunityList = apps.get_model('community', 'CommunityList')
15+
CommunityListDocs = apps.get_model('community', 'CommunityListDocs')
16+
SearchRule = apps.get_model('community', 'SearchRule')
17+
SearchRuleDocs = apps.get_model('community', 'SearchRuleDocs')
18+
19+
# Document id fixup ------------------------------------------------------------
20+
21+
22+
sys.stderr.write('\n')
23+
24+
sys.stderr.write(' %s.%s:\n' % (CommunityList.__name__, 'added_docs'))
25+
for l in tqdm(CommunityList.objects.all()):
26+
l.added_docs.set([ d.document for d in CommunityListDocs.objects.filter(communitylist=l) ])
27+
28+
sys.stderr.write(' %s.%s:\n' % (SearchRule.__name__, 'name_contains_index'))
29+
for r in tqdm(SearchRule.objects.all()):
30+
r.name_contains_index.set([ d.document for d in SearchRuleDocs.objects.filter(searchrule=r) ])
31+
32+
def reverse(apps, schema_editor):
33+
pass
34+
35+
def timestamp(apps, schema_editor):
36+
sys.stderr.write('\n %s' % time.strftime('%Y-%m-%d %H:%M:%S'))
37+
38+
class Migration(migrations.Migration):
39+
40+
dependencies = [
41+
('community', '0005_2_add_docs_m2m_table'),
42+
]
43+
44+
operations = [
45+
#migrations.RunPython(forward, reverse),
46+
# Alternative:
47+
migrations.RunPython(timestamp, timestamp),
48+
migrations.RunSQL(
49+
"INSERT INTO community_communitylist_added_docs SELECT * FROM community_communitylistdocs;",
50+
""),
51+
migrations.RunPython(timestamp, timestamp),
52+
migrations.RunSQL(
53+
"INSERT INTO community_searchrule_name_contains_index SELECT * FROM community_searchruledocs;",
54+
""),
55+
migrations.RunPython(timestamp, timestamp),
56+
]
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.11.20 on 2019-05-30 03:06
3+
from __future__ import unicode_literals
4+
5+
from django.db import migrations
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
('community', '0006_copy_docs_m2m_table'),
12+
]
13+
14+
operations = [
15+
migrations.RemoveField(
16+
model_name='communitylistdocs',
17+
name='communitylist',
18+
),
19+
migrations.RemoveField(
20+
model_name='communitylistdocs',
21+
name='document',
22+
),
23+
migrations.RemoveField(
24+
model_name='searchruledocs',
25+
name='document',
26+
),
27+
migrations.RemoveField(
28+
model_name='searchruledocs',
29+
name='searchrule',
30+
),
31+
migrations.RemoveField(
32+
model_name='communitylist',
33+
name='added_docs2',
34+
),
35+
migrations.RemoveField(
36+
model_name='searchrule',
37+
name='name_contains_index2',
38+
),
39+
migrations.DeleteModel(
40+
name='CommunityListDocs',
41+
),
42+
migrations.DeleteModel(
43+
name='SearchRuleDocs',
44+
),
45+
]

ietf/community/tests.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def test_manage_personal_list(self):
107107
self.assertEqual(r.status_code, 200)
108108

109109
# add document
110-
r = self.client.post(url, { "action": "add_documents", "documents": draft.pk })
110+
r = self.client.post(url, { "action": "add_documents", "documents": draft.name })
111111
self.assertEqual(r.status_code, 302)
112112
clist = CommunityList.objects.get(user__username="plain")
113113
self.assertTrue(clist.added_docs.filter(pk=draft.pk))
@@ -118,7 +118,7 @@ def test_manage_personal_list(self):
118118
self.assertTrue(draft.name in unicontent(r))
119119

120120
# remove document
121-
r = self.client.post(url, { "action": "remove_document", "document": draft.pk })
121+
r = self.client.post(url, { "action": "remove_document", "document": draft.name })
122122
self.assertEqual(r.status_code, 302)
123123
clist = CommunityList.objects.get(user__username="plain")
124124
self.assertTrue(not clist.added_docs.filter(pk=draft.pk))

ietf/community/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def augment_docs_with_tracking_info(docs, user):
6161
if user and user.is_authenticated:
6262
clist = CommunityList.objects.filter(user=user).first()
6363
if clist:
64-
tracked.update(docs_tracked_by_community_list(clist).filter(pk__in=docs).values_list("pk", flat=True))
64+
tracked.update(docs_tracked_by_community_list(clist).filter(pk__in=[ d.pk for d in docs ]).values_list("pk", flat=True))
6565

6666
for d in docs:
6767
d.tracked_in_personal_community_list = d.pk in tracked

ietf/community/views.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@ def manage_list(request, username=None, acronym=None, group_type=None):
5757
add_doc_form = AddDocumentsForm()
5858

5959
if request.method == 'POST' and action == 'remove_document':
60-
document_pk = request.POST.get('document')
61-
if clist.pk is not None and document_pk:
62-
document = get_object_or_404(clist.added_docs, pk=document_pk)
60+
document_name = request.POST.get('document')
61+
if clist.pk is not None and document_name:
62+
document = get_object_or_404(clist.added_docs, name=document_name)
6363
clist.added_docs.remove(document)
6464

6565
return HttpResponseRedirect("")
@@ -209,7 +209,7 @@ def feed(request, username=None, acronym=None, group_type=None):
209209
since = datetime.datetime.now() - datetime.timedelta(days=14)
210210

211211
events = DocEvent.objects.filter(
212-
doc__in=documents,
212+
doc__id__in=documents,
213213
time__gte=since,
214214
).distinct().order_by('-time', '-id').select_related("doc")
215215

ietf/doc/fields.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from django.utils.html import escape
44
from django import forms
5+
from django.db.models import Q
56
from django.urls import reverse as urlreverse
67

78
import debug # pyflakes:ignore
@@ -52,8 +53,10 @@ def prepare_value(self, value):
5253
if isinstance(value, (int, long)):
5354
value = str(value)
5455
if isinstance(value, basestring):
55-
pks = self.parse_select2_value(value)
56-
value = self.model.objects.filter(pk__in=pks)
56+
items = self.parse_select2_value(value)
57+
names = [ i for i in items if not i.isdigit() ]
58+
ids = [ i for i in items if i.isdigit() ]
59+
value = self.model.objects.filter(Q(name__in=names)|Q(id__in=ids))
5760
filter_args = {}
5861
if self.model == DocAlias:
5962
filter_args["document__type"] = self.doc_type
@@ -76,17 +79,17 @@ def prepare_value(self, value):
7679

7780
def clean(self, value):
7881
value = super(SearchableDocumentsField, self).clean(value)
79-
pks = self.parse_select2_value(value)
82+
names = self.parse_select2_value(value)
8083

81-
objs = self.model.objects.filter(pk__in=pks)
84+
objs = self.model.objects.filter(name__in=names)
8285

83-
found_pks = [str(o.pk) for o in objs]
84-
failed_pks = [x for x in pks if x not in found_pks]
85-
if failed_pks:
86-
raise forms.ValidationError(u"Could not recognize the following documents: {pks}. You can only input documents already registered in the Datatracker.".format(pks=", ".join(failed_pks)))
86+
found_names = [str(o.name) for o in objs]
87+
failed_names = [x for x in names if x not in found_names]
88+
if failed_names:
89+
raise forms.ValidationError(u"Could not recognize the following documents: {names}. You can only input documents already registered in the Datatracker.".format(names=", ".join(failed_names)))
8790

8891
if self.max_entries != None and len(objs) > self.max_entries:
89-
raise forms.ValidationError(u"You can select at most %s entries only." % self.max_entries)
92+
raise forms.ValidationError(u"You can select at most %s entries." % self.max_entries)
9093

9194
return objs
9295

0 commit comments

Comments
 (0)