DEV Community

Discussion on: Django: Why you might want to set up extra DB level indexes for your ManyToMany relationships

Collapse
 
karolyi profile image
László Károlyi

Oh, I almost forgot to reply to you.

In my apps.py under the django module that contains the models.py with the model, I added the following functions, which is a result of me experimenting around until I found out the right way of doing it. It is basically putting together the SQL for creating the index via MariaDB:

from django.apps import AppConfig
from django.conf import settings
from django.core.checks import Info, Tags, register
from django.db import connection
from django.db.models.signals import post_migrate
from django.db.utils import OperationalError
from django.utils.translation import ugettext_lazy as _


def add_applieddiscount_sr_index() -> list:
    """
    As discussed in the link below, add an index to `seatreservation_id`
    on the `ManyToMany` `AppliedDiscount` relation.
    https://dev.to/karolyi/django-why-you-might-want-to-set-up-extra-db-level-indexes-for-your-manytomany-relationships-1ecg
    """
    from .models import SeatReservation
    errors = []
    m2m_model = SeatReservation.applied_discounts.through
    db_table = m2m_model._meta.db_table
    field_name = m2m_model._meta.get_field('seatreservation').column
    cursor = connection.cursor()
    try:
        cursor.execute(
            f'ALTER TABLE `{db_table}` ADD INDEX sr_id_index (`{field_name}`)')
    except OperationalError as exc:
        if exc.args[0] == settings.MARIADB_INDEX_ALREADY_EXISTS_CODE:
            # Index already exists
            return errors
        raise
    errors.append(Info(msg='Added index on SeatReservation.applied_discounts'))
    return errors


def post_migrate_ensure_defaults(**kwargs):
    """
    Run the default models installer when it is our application's
    `post_migrate` signal.
    """
    if not isinstance(kwargs['sender'], BookingConfig):
        # Running from migration post-signal, but not from our app's
        # signal
        return
    return ensure_defaults()


def ensure_defaults(**kwargs):
    'Run the default setter and maintenance functions.'
    errors = []
    errors.extend(add_applieddiscount_sr_index())
    return errors


class BookingConfig(AppConfig):
    name = f'project.booking'
    verbose_name = _('Project: Booking')
    label = f'project_booking'

    def ready(self):
        'Fix database, run maintenance scripts, etc ...'
        post_migrate.connect(
            post_migrate_ensure_defaults,
            dispatch_uid=f'{self.label}.ensure_defaults'
        )
        register(ensure_defaults, Tags.models)

Also, you'll have to modify the init.py next to the apps.py to contain the following lines:

from django.conf import settings

default_app_config = 'project.booking.apps.BookingConfig'

Where the path in default_app_config is the python import path to your apps.py.