Projet

Général

Profil

0003-migrations-set-existing-user-phone-fields-to-E.164-v.patch

Paul Marillonnet, 06 octobre 2022 12:00

Télécharger (7,44 ko)

Voir les différences:

Subject: [PATCH 3/3] migrations: set existing user phone fields to E.164 value
 format (#69365)

 src/authentic2/managers.py                    |  7 +-
 .../0043_attribute_alter_model_managers.py    | 27 ++++++++
 .../migrations/0044_migrate_phone_field.py    | 57 ++++++++++++++++
 tests/test_migrations.py                      | 66 +++++++++++++++++++
 4 files changed, 155 insertions(+), 2 deletions(-)
 create mode 100644 src/authentic2/migrations/0043_attribute_alter_model_managers.py
 create mode 100644 src/authentic2/migrations/0044_migrate_phone_field.py
src/authentic2/managers.py
109 109

  
110 110

  
111 111
class AttributeManager(managers.QueryManager.from_queryset(GetByNameQuerySet)):
112
    pass
112
    use_in_migrations = True
113

  
114

  
115
class AttributeValueManager(managers.QueryManager.from_queryset(AttributeValueQuerySet)):
116
    use_in_migrations = True
113 117

  
114 118

  
115 119
ServiceManager = BaseServiceManager.from_queryset(ServiceQuerySet)
116
AttributeValueManager = managers.QueryManager.from_queryset(AttributeValueQuerySet)
src/authentic2/migrations/0043_attribute_alter_model_managers.py
1
from django.db import migrations
2

  
3
import authentic2.managers
4

  
5

  
6
class Migration(migrations.Migration):
7

  
8
    dependencies = [
9
        ('authentic2', '0042_api_client'),
10
    ]
11

  
12
    operations = [
13
        migrations.AlterModelManagers(
14
            name='attribute',
15
            managers=[
16
                ('all_objects', authentic2.managers.AttributeManager()),
17
                ('objects', authentic2.managers.AttributeManager(disabled=False)),
18
            ],
19
        ),
20
        migrations.AlterModelManagers(
21
            name='attributevalue',
22
            managers=[
23
                ('all_objects', authentic2.managers.AttributeValueManager()),
24
                ('objects', authentic2.managers.AttributeValueManager(attribute__disabled=False)),
25
            ],
26
        ),
27
    ]
src/authentic2/migrations/0044_migrate_phone_field.py
1
import phonenumbers
2
from django.conf import settings
3
from django.db import migrations
4

  
5

  
6
def migrate_phone_field(apps, schema_editor):
7
    Attribute = apps.get_model('authentic2', 'Attribute')
8
    AttributeValue = apps.get_model('authentic2', 'AttributeValue')
9
    User = apps.get_model(*settings.AUTH_USER_MODEL.split('.', maxsplit=1))
10
    ContentType = apps.get_model('contenttypes', 'ContentType')
11

  
12
    user_ct = ContentType.objects.get_for_model(User)
13
    try:
14
        phone_attr = Attribute.objects.get(name='phone')
15
    except Attribute.DoesNotExist:
16
        return
17

  
18
    for atv in AttributeValue.objects.filter(attribute=phone_attr):
19
        pn = None
20
        try:
21
            pn = phonenumbers.parse(atv.content)
22
        except phonenumbers.NumberParseException:
23
            try:
24
                pn = phonenumbers.parse(
25
                    atv.content,
26
                    settings.PHONE_COUNTRY_CODES[settings.DEFAULT_COUNTRY_CODE],
27
                )
28
            except phonenumbers.NumberParseException:
29
                continue
30

  
31
        number_e164 = phonenumbers.format_number(pn, phonenumbers.PhoneNumberFormat.E164)
32
        atv.content = number_e164
33
        atv.save()
34

  
35
        if not atv.content_type == user_ct:
36
            continue
37
        try:
38
            user = User.objects.get(id=atv.object_id)
39
        except User.DoesNotExist:
40
            # stale attribute value, should not happen as it is deleted on user deletion
41
            continue
42
        if hasattr(user, 'phone'):
43
            user.phone = number_e164
44
            user.save()
45

  
46

  
47
class Migration(migrations.Migration):
48

  
49
    dependencies = [
50
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
51
        ('custom_user', '0032_auto_20220919_1230'),
52
        ('authentic2', '0043_attribute_alter_model_managers'),
53
    ]
54

  
55
    operations = [
56
        migrations.RunPython(migrate_phone_field, reverse_code=migrations.RunPython.noop),
57
    ]
tests/test_migrations.py
14 14
# You should have received a copy of the GNU Affero General Public License
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17
from django.conf import settings
17 18
from django.contrib.auth.models import AbstractUser
18 19
from django.utils.timezone import now
19 20

  
......
58 59
    User = new_apps.get_model('custom_user', 'User')
59 60
    user = User.objects.get()
60 61
    assert user.email_verified_date == user.date_joined
62

  
63

  
64
def test_migration_authentic2_0044_migrate_phone_field(transactional_db, migration):
65
    old_apps = migration.before([('authentic2', '0043_attribute_alter_model_managers')])
66

  
67
    Attribute = old_apps.get_model('authentic2', 'Attribute')
68
    AttributeValue = old_apps.get_model('authentic2', 'AttributeValue')
69
    User = old_apps.get_model(*settings.AUTH_USER_MODEL.split('.', maxsplit=1))
70
    ContentType = old_apps.get_model('contenttypes', 'ContentType')
71

  
72
    at = Attribute.objects.create(
73
        name='phone',
74
        label='Phone number',
75
        kind='phone_number',
76
    )
77

  
78
    user = User.objects.create(
79
        email='john.doe@example.com',
80
        username='john.doe',
81
        phone='12345',
82
    )
83
    user2 = User.objects.create(
84
        email='billy.smith@example.com',
85
        username='billy.smith',
86
        phone='abcdef',  # erroneous phone number
87
    )
88

  
89
    AttributeValue.objects.create(
90
        attribute=at,
91
        content_type=ContentType.objects.get_for_model(User),
92
        object_id=user.id,
93
        content='12345',
94
    )
95

  
96
    AttributeValue.objects.create(
97
        attribute=at,
98
        content_type=ContentType.objects.get_for_model(User),
99
        object_id=user2.id,
100
        content='abcdef',
101
    )
102

  
103
    new_apps = migration.apply([('authentic2', '0044_migrate_phone_field')])
104
    User = new_apps.get_model('custom_user', 'User')
105

  
106
    assert User.objects.get(username='john.doe').phone == '+3312345'
107
    assert User.objects.get(username='billy.smith').phone == 'abcdef'
108

  
109
    Attribute = new_apps.get_model('authentic2', 'Attribute')
110
    AttributeValue = new_apps.get_model('authentic2', 'AttributeValue')
111
    ContentType = new_apps.get_model('contenttypes', 'ContentType')
112

  
113
    at = Attribute.objects.get(name='phone')
114
    atv = AttributeValue.objects.get(
115
        attribute=at,
116
        content_type=ContentType.objects.get_for_model(User),
117
        object_id=user.id,
118
    )
119
    assert atv.content == '+3312345'
120

  
121
    atv2 = AttributeValue.objects.get(
122
        attribute=at,
123
        content_type=ContentType.objects.get_for_model(User),
124
        object_id=user2.id,
125
    )
126
    assert atv2.content == 'abcdef'
61
-