﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
33724	Changing from list to set in `exclude` raises errors, and is not documented.	אורי	Mariusz Felisiak	"The commit:
https://github.com/django/django/commit/1ea7e3157d1f9b4db71e768d75ea57e47dbd49f9

My use case:

I used `def clean_fields` in a mixin inherited by some of my models:

{{{
class CleanAndValidateAllFieldsMixin(object):
    def clean_fields(self, exclude=None):
        """"""
        Allows to have different slug and username validators for Entity and User.
        """"""
        if (exclude is None):
            exclude = []

        self.clean_all_fields(exclude=exclude)

        try:
            super().clean_fields(exclude=exclude)
        except ValidationError as e:
            errors = e.error_dict
        else:
            errors = {}

        self.validate_all_fields(errors=errors, exclude=exclude)

    def clean_all_fields(self, exclude=None):
        pass

}}}

In some of the models that use it, I defined `clean_fields` and added values to `exclude`:

{{{
    def clean_fields(self, exclude=None):
        self.normalize_slug_and_username()
        self.validate_username_for_slug()
        self.validate_username_required()
        self.validate_username_unique()

        if (exclude is None):
            exclude = []

        # Reserved username can be less than 6 characters, and any alphanumeric sequence.
        exclude += ['username', 'slug']

        return super().clean_fields(exclude=exclude)
}}}

{{{
    def clean_fields(self, exclude=None):
        """"""
        Allows to have different slug and username validators for Entity and User.
        """"""
        if (exclude is None):
            exclude = []

        # If special username is true, don't validate username.
        if (self.special_username):
            self.normalize_slug_and_username()
            self.validate_username_for_slug()
            self.validate_username_required()
            self.validate_username_unique()
            exclude += ['username', 'slug']

        return super().clean_fields(exclude=exclude)
}}}

The results: model tests fail with Django 4.1 alpha:

{{{
======================================================================
ERROR: test_username_too_long_exception_4 (speedy.core.accounts.tests.test_models.ReservedUsernameHebrewTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ""...\speedy\core\accounts\tests\test_models.py"", line 395, in test_username_too_long_exception_4
    reserved_username.save()
  File ""...\speedy\core\base\models.py"", line 21, in save
    return super().save(*args, **kwargs)
  File ""...\speedy\core\base\models.py"", line 12, in save
    self.full_clean()
  File ""...\.venv_3.9\lib\site-packages\django\db\models\base.py"", line 1464, in full_clean
    self.clean_fields(exclude=exclude)
  File ""...\speedy\core\accounts\models.py"", line 182, in clean_fields
    exclude += ['username', 'slug']
TypeError: unsupported operand type(s) for +=: 'set' and 'list'

----------------------------------------------------------------------
}}}

Is `exclude` a set now (instead of a list) and where is it documented? If it's not documented, please document it. I didn't find it documented on https://docs.djangoproject.com/en/dev/releases/4.1/.

What is the best written code to change the line `exclude += ['username', 'slug']` in my code? Is it `exclude |= set(['username', 'slug'])` or `exclude |= {'username', 'slug'}`? Or should I convert to list and then back to set?

What is the reason `exclude` was changed to a set?

How should `exclude` be defined in `clean_fields` and what should I do if I receive `exclude is None`?

Thanks,
Uri."	Bug	closed	Database layer (models, ORM)	4.1	Release blocker	fixed		Carlton Gibson	Ready for checkin	1	0	0	0	0	0
