def clean_slug()

in src/olympia/addons/models.py [0:0]


def clean_slug(instance, slug_field='slug'):
    """Cleans a model instance slug.

    This strives to be as generic as possible but is only used
    by Add-ons at the moment.

    :param instance: The instance to clean the slug for.
    :param slug_field: The field where to get the currently set slug from.
    """
    slug = getattr(instance, slug_field, None) or instance.name

    if not slug:
        # Initialize the slug with what we have available: a name translation
        # or in last resort a random slug.
        translations = Translation.objects.filter(id=instance.name_id)
        if translations.exists():
            slug = translations[0]

    max_length = instance._meta.get_field(slug_field).max_length
    # We have to account for slug being reduced to '' by slugify
    slug = slugify(slug or '')[:max_length] or get_random_slug()

    if DeniedSlug.blocked(slug):
        slug = slug[: max_length - 1] + '~'

    # The following trick makes sure we are using a manager that returns
    # all the objects, as otherwise we could have a slug clash on our hands.
    # Eg with the "Addon.objects" manager, which doesn't list deleted addons,
    # we could have a "clean" slug which is in fact already assigned to an
    # already existing (deleted) addon. Also, make sure we use the base class.
    manager = models.Manager()
    manager.model = instance._meta.proxy_for_model or instance.__class__

    qs = manager.values_list(slug_field, flat=True)  # Get list of all slugs.
    if instance.id:
        qs = qs.exclude(pk=instance.id)  # Can't clash with itself.

    # We first need to make sure there's a clash, before trying to find a
    # suffix that is available. Eg, if there's a "foo-bar" slug, "foo" is still
    # available.
    clash = qs.filter(**{slug_field: slug})

    if clash.exists():
        max_postfix_length = len(str(MAX_SLUG_INCREMENT))

        slug = slugify(slug)[: max_length - max_postfix_length]

        # There is a clash, so find a suffix that will make this slug unique.
        lookup = {'%s__startswith' % slug_field: slug}
        clashes = qs.filter(**lookup)

        prefix_len = len(slug)
        used_slug_numbers = [value[prefix_len:] for value in clashes]

        # find the next free slug number
        slug_numbers = {int(i) for i in used_slug_numbers if i.isdigit()}
        unused_numbers = SLUG_INCREMENT_SUFFIXES - slug_numbers

        if unused_numbers:
            num = min(unused_numbers)
        elif max_length is None:
            num = max(slug_numbers) + 1
        else:
            # This could happen. The current implementation (using
            # ``[:max_length -2]``) only works for the first 100 clashes in the
            # worst case (if the slug is equal to or longuer than
            # ``max_length - 2`` chars).
            # After that, {verylongslug}-100 will be trimmed down to
            # {verylongslug}-10, which is already assigned, but it's the last
            # solution tested.
            raise RuntimeError(f'No suitable slug increment for {slug} found')

        slug = f'{slug}{num}'

    setattr(instance, slug_field, slug)

    return instance