|
| 1 | +"""Data migration: collapse ``REQUIRE_VALID_EMAIL_FOR`` into the boolean |
| 2 | +``EMAIL_VALIDATION_REQUIRED``. |
| 3 | +
|
| 4 | +New default is ``True`` (email validation required) - the safer default |
| 5 | +for fresh installs. Upgrading sites that had the old default |
| 6 | +(``'nothing'`` / no row) while carrying real content (any ``Post`` rows) |
| 7 | +get an explicit ``False`` row written to preserve their previous |
| 8 | +behavior. Sites that had ``'see-content'`` (email was required) have any |
| 9 | +stale row removed so the new ``True`` default applies. Fresh installs |
| 10 | +(no posts) get no row either and inherit the new ``True`` default. |
| 11 | +
|
| 12 | +The forward function is schema-agnostic: it uses |
| 13 | +``apps.get_model('livesettings', 'Setting')`` instead of raw SQL against |
| 14 | +``livesettings_setting``. That shields the migration from livesettings |
| 15 | +schema changes and DB-specific quoting of the reserved word ``group``. |
| 16 | +
|
| 17 | +``Post.objects.exists()`` is used (not ``User.objects.exists()``) so the |
| 18 | +``createsuperuser`` before ``migrate`` sequence does not falsely mark a |
| 19 | +fresh install as an upgrading site. Any real activity on the forum |
| 20 | +produces at least one Post. |
| 21 | +""" |
| 22 | +from django.db import migrations, transaction |
| 23 | + |
| 24 | + |
| 25 | +OLD_KEY = 'REQUIRE_VALID_EMAIL_FOR' |
| 26 | +NEW_KEY = 'EMAIL_VALIDATION_REQUIRED' |
| 27 | +GROUP = 'ACCESS_CONTROL' |
| 28 | + |
| 29 | + |
| 30 | +def forward(apps, schema_editor): |
| 31 | + Setting = apps.get_model('livesettings', 'Setting') |
| 32 | + Site = apps.get_model('sites', 'Site') |
| 33 | + Post = apps.get_model('askbot', 'Post') |
| 34 | + |
| 35 | + has_posts = Post.objects.exists() |
| 36 | + |
| 37 | + with transaction.atomic(): |
| 38 | + for site in Site.objects.all(): |
| 39 | + old_rows = list(Setting.objects.filter( |
| 40 | + site=site, group=GROUP, key=OLD_KEY |
| 41 | + )) |
| 42 | + old_value = old_rows[0].value if old_rows else 'nothing' |
| 43 | + |
| 44 | + # Preserve old behavior for upgrading sites that had validation |
| 45 | + # off ('nothing' was the old default). Fresh installs (no Post |
| 46 | + # rows) get no row written and inherit the new True default. |
| 47 | + if old_value != 'see-content' and has_posts: |
| 48 | + Setting.objects.update_or_create( |
| 49 | + site=site, group=GROUP, key=NEW_KEY, |
| 50 | + defaults={'value': 'False'}, |
| 51 | + ) |
| 52 | + else: |
| 53 | + # 'see-content' (validation was required) or fresh install: |
| 54 | + # delete any stale NEW_KEY row so the new True default |
| 55 | + # applies cleanly. |
| 56 | + Setting.objects.filter( |
| 57 | + site=site, group=GROUP, key=NEW_KEY |
| 58 | + ).delete() |
| 59 | + |
| 60 | + # Always drop the obsolete OLD_KEY row. |
| 61 | + for row in old_rows: |
| 62 | + row.delete() |
| 63 | + |
| 64 | + |
| 65 | +def reverse(apps, schema_editor): |
| 66 | + """Reverse: reconstruct ``REQUIRE_VALID_EMAIL_FOR`` from the boolean. |
| 67 | +
|
| 68 | + Mapping is the inverse of forward: |
| 69 | +
|
| 70 | + - ``EMAIL_VALIDATION_REQUIRED = False`` (explicit) -> write |
| 71 | + ``REQUIRE_VALID_EMAIL_FOR = 'nothing'``. |
| 72 | + - ``EMAIL_VALIDATION_REQUIRED = True`` / missing -> write |
| 73 | + ``REQUIRE_VALID_EMAIL_FOR = 'see-content'`` only when there are |
| 74 | + posts (an upgrading site). Fresh sites get no row so the old |
| 75 | + ``'nothing'`` default applies. |
| 76 | +
|
| 77 | + The old default was ``'nothing'`` while the new default is ``True``, |
| 78 | + so a naive reverse would silently flip fresh sites from "no |
| 79 | + validation" (pre-migration) to "validation required" (post-reverse). |
| 80 | + Gating the ``'see-content'`` write on ``has_posts`` preserves the |
| 81 | + pre-migration default on fresh installs and round-trips cleanly for |
| 82 | + upgrading sites. |
| 83 | + """ |
| 84 | + Setting = apps.get_model('livesettings', 'Setting') |
| 85 | + Site = apps.get_model('sites', 'Site') |
| 86 | + Post = apps.get_model('askbot', 'Post') |
| 87 | + |
| 88 | + has_posts = Post.objects.exists() |
| 89 | + |
| 90 | + with transaction.atomic(): |
| 91 | + for site in Site.objects.all(): |
| 92 | + new_rows = list(Setting.objects.filter( |
| 93 | + site=site, group=GROUP, key=NEW_KEY |
| 94 | + )) |
| 95 | + new_value = new_rows[0].value if new_rows else 'True' |
| 96 | + |
| 97 | + if new_value == 'False': |
| 98 | + Setting.objects.update_or_create( |
| 99 | + site=site, group=GROUP, key=OLD_KEY, |
| 100 | + defaults={'value': 'nothing'}, |
| 101 | + ) |
| 102 | + elif has_posts: |
| 103 | + Setting.objects.update_or_create( |
| 104 | + site=site, group=GROUP, key=OLD_KEY, |
| 105 | + defaults={'value': 'see-content'}, |
| 106 | + ) |
| 107 | + else: |
| 108 | + Setting.objects.filter( |
| 109 | + site=site, group=GROUP, key=OLD_KEY |
| 110 | + ).delete() |
| 111 | + |
| 112 | + for row in new_rows: |
| 113 | + row.delete() |
| 114 | + |
| 115 | + |
| 116 | +class Migration(migrations.Migration): |
| 117 | + |
| 118 | + dependencies = [ |
| 119 | + ('askbot', '0035_set_global_group_used_for_analytics'), |
| 120 | + ('sites', '0001_initial'), |
| 121 | + ('livesettings', '0001_initial'), |
| 122 | + ] |
| 123 | + |
| 124 | + operations = [ |
| 125 | + migrations.RunPython(forward, reverse), |
| 126 | + ] |
0 commit comments