Skip to content

django: Use valid identifiers for application labels

While I was locally playing to change swh-web database backend from SQLite to PostgreSQL, I stumbled across that error while trying to load a JSON dump of the webapp database into the new Postgres one.

17:25 $ django-admin loaddata datadump.json --settings=swh.web.settings.development
[08/Jan/2021 16:26:07] [DEBUG] urllib3.util.retry.from_int:332 - Converted retries value: 1 -> Retry(total=1, connect=None, read=None, redirect=None, status=None)
[08/Jan/2021 16:26:07] [DEBUG] urllib3.util.retry.from_int:332 - Converted retries value: 1 -> Retry(total=1, connect=None, read=None, redirect=None, status=None)
Traceback (most recent call last):
  File "/home/anlambert/.virtualenvs/swh/lib/python3.7/site-packages/django/core/serializers/json.py", line 69, in Deserializer
    yield from PythonDeserializer(objects, **options)
  File "/home/anlambert/.virtualenvs/swh/lib/python3.7/site-packages/django/core/serializers/python.py", line 92, in Deserializer
    Model = _get_model(d["model"])
  File "/home/anlambert/.virtualenvs/swh/lib/python3.7/site-packages/django/core/serializers/python.py", line 154, in _get_model
    return apps.get_model(model_identifier)
  File "/home/anlambert/.virtualenvs/swh/lib/python3.7/site-packages/django/apps/registry.py", line 206, in get_model
    app_label, model_name = app_label.split(".")
ValueError: too many values to unpack (expected 2)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/anlambert/.virtualenvs/swh/bin/django-admin", line 8, in <module>
    sys.exit(execute_from_command_line())
  File "/home/anlambert/.virtualenvs/swh/lib/python3.7/site-packages/django/core/management/__init__.py", line 381, in execute_from_command_line
    utility.execute()
  File "/home/anlambert/.virtualenvs/swh/lib/python3.7/site-packages/django/core/management/__init__.py", line 375, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/anlambert/.virtualenvs/swh/lib/python3.7/site-packages/django/core/management/base.py", line 323, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/anlambert/.virtualenvs/swh/lib/python3.7/site-packages/django/core/management/base.py", line 364, in execute
    output = self.handle(*args, **options)
  File "/home/anlambert/.virtualenvs/swh/lib/python3.7/site-packages/django/core/management/commands/loaddata.py", line 72, in handle
    self.loaddata(fixture_labels)
  File "/home/anlambert/.virtualenvs/swh/lib/python3.7/site-packages/django/core/management/commands/loaddata.py", line 114, in loaddata
    self.load_label(fixture_label)
  File "/home/anlambert/.virtualenvs/swh/lib/python3.7/site-packages/django/core/management/commands/loaddata.py", line 172, in load_label
    for obj in objects:
  File "/home/anlambert/.virtualenvs/swh/lib/python3.7/site-packages/django/core/serializers/json.py", line 73, in Deserializer
    raise DeserializationError() from exc
django.core.serializers.base.DeserializationError: Problem installing fixture '/home/anlambert/swh/swh-environment/swh-web/datadump.json':

This is due to the fact that swh-web declares the following labels for some custom django applications: swh.web.common, swh.web.auth.

But Django application label must be a valid Python identifier and thus must not contain dot characters.

As a result, having invalid identifiers will make database data importing from JSON fails.

This was not clearly indicated in Django documentation and application label validity check has been added a couple of weeks ago in upstream Django source code. https://code.djangoproject.com/ticket/32285

That diff renames the invalid django application labels and ensure migrations can still be applied.

In order to apply the renaming changes to the swh-web instance in production, the migration command will have to be the following:

django-admin migrate --settings=swh.web.settings.production --fake

This will simulate the migrations but without any effects apart filling the django migrations table with the new application labels.

Calling migrate without the fake option will raise errors otherwise as the changes have already been applied to database, see below.

17:38 $ django-admin migrate --settings=swh.web.settings.development
[08/Jan/2021 16:38:12] [DEBUG] urllib3.util.retry.from_int:332 - Converted retries value: 1 -> Retry(total=1, connect=None, read=None, redirect=None, status=None)
[08/Jan/2021 16:38:12] [DEBUG] urllib3.util.retry.from_int:332 - Converted retries value: 1 -> Retry(total=1, connect=None, read=None, redirect=None, status=None)
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, swh_web_auth, swh_web_common
Running migrations:
  Applying swh_web_auth.0001_initial...Traceback (most recent call last):
  File "/home/anlambert/.virtualenvs/swh/lib/python3.7/site-packages/django/db/backends/utils.py", line 82, in _execute
    return self.cursor.execute(sql)
  File "/home/anlambert/.virtualenvs/swh/lib/python3.7/site-packages/django/db/backends/sqlite3/base.py", line 381, in execute
    return Database.Cursor.execute(self, query)
sqlite3.OperationalError: table "oidc_user_offline_tokens" already exists

This could be implemented the following way in debian/postinst:

#!/bin/bash

set -e

if [ -f /var/lib/swh/web.sqlite3 ]; then
    echo "Software Heritage production db detected; running migrations"
    if [ -d /usr/lib/python3/dist-packages/swh.web-0.0.279.egg-info/ ]
    then
        # use fake migrations after django app labels renaming in v0.0.279
        django-admin migrate --settings=swh.web.settings.production --fake
    else
        django-admin migrate --settings=swh.web.settings.production
    fi
fi

Related to swh/infra/sysadm-environment#2945 (closed)


Migrated from D4830 (view on Phabricator)

Merge request reports