Question: Better Indication of Squash Migration State in showmigrations Description In the discussion of () it became clear that there was a disconnect between the current output of showmigrations and the actual recorded applied state of squashed migrations. Currently if all of the replaced/original migrations have been run, showmigrations will output that the related squashed migration has been applied with an [X] in the output even if that has not yet been recorded by the migration recorder. However, it is currently a requirement that migrate be run to record this applied state for the squashed migration before the original migrations are removed. If a deployment process is looking for an empty [ ] to know to run the migration then this may trip it up. This case is to consider an output for showmigrations which can indicate that this migration has only been "soft" applied, that is applied but not recorded yet. Changes to the planner for such an output may also impact .

from django.core.management.base import BaseCommand from django.db import DEFAULT_DB_ALIAS, connections from django.db.migrations.loader import MigrationLoader<ins>from django.db.migrations.recorder import MigrationRecorder</ins>   class Command(BaseCommand):


Question: Better Indication of Squash Migration State in showmigrations Description In the discussion of () it became clear that there was a disconnect between the current output of showmigrations and the actual recorded applied state of squashed migrations. Currently if all of the replaced/original migrations have been run, showmigrations will output that the related squashed migration has been applied with an [X] in the output even if that has not yet been recorded by the migration recorder. However, it is currently a requirement that migrate be run to record this applied state for the squashed migration before the original migrations are removed. If a deployment process is looking for an empty [ ] to know to run the migration then this may trip it up. This case is to consider an output for showmigrations which can indicate that this migration has only been "soft" applied, that is applied but not recorded yet. Changes to the planner for such an output may also impact .

"""         # Load migrations from disk/DB         loader = MigrationLoader(connection, ignore_no_migrations=True)<ins>        recorder = MigrationRecorder(connection)        recorded_migrations = recorder.applied_migrations()</ins>         graph = loader.graph         # If we were passed a list of apps, validate it         if app_names:


Question: Better Indication of Squash Migration State in showmigrations Description In the discussion of () it became clear that there was a disconnect between the current output of showmigrations and the actual recorded applied state of squashed migrations. Currently if all of the replaced/original migrations have been run, showmigrations will output that the related squashed migration has been applied with an [X] in the output even if that has not yet been recorded by the migration recorder. However, it is currently a requirement that migrate be run to record this applied state for the squashed migration before the original migrations are removed. If a deployment process is looking for an empty [ ] to know to run the migration then this may trip it up. This case is to consider an output for showmigrations which can indicate that this migration has only been "soft" applied, that is applied but not recorded yet. Changes to the planner for such an output may also impact .

applied_migration = loader.applied_migrations.get(plan_node)                         # Mark it as applied/unapplied                         if applied_migration:                            output = ' [X] %s' % title                            if plan_node in recorded_migrations:                                output = ' [X] %s' % title                            else:                                title += " Run 'manage.py migrate' to finish recording."                                output = ' [-] %s' % title                             if self.verbosity >= 2 and hasattr(applied_migration, 'applied'):                                 output += ' (applied at %s)' % applied_migration.applied.strftime('%Y-%m-%d %H:%M:%S')                             self.stdout.write(output)


Question: Include in CsrfViewMiddleware's bad CSRF token message where the token is from Description (last modified by Chris Jerdonek) Currently, if CsrfViewMiddleware encounters a bad CSRF token, it will reject the request with a message like-- "CSRF token incorrect" "CSRF token has incorrect length" I noticed that it would be relatively easy to include in these messages whether the token was obtained from POST data or a custom header, which would be useful for troubleshooting. The messages are specified here in the code. The new messages could look e.g. like-- "CSRF token (from POST) incorrect" "CSRF token (from 'X-CSRFToken' header) has incorrect length" The changes to CsrfViewMiddlewareTestMixin proposed in would make these cases easy to test.

from django.conf import settings from django.core.exceptions import DisallowedHost, ImproperlyConfigured<ins>from django.http.request import HttpHeaders</ins> from django.urls import get_callable from django.utils.cache import patch_vary_headers from django.utils.crypto import constant_time_compare, get_random_string


Question: Include in CsrfViewMiddleware's bad CSRF token message where the token is from Description (last modified by Chris Jerdonek) Currently, if CsrfViewMiddleware encounters a bad CSRF token, it will reject the request with a message like-- "CSRF token incorrect" "CSRF token has incorrect length" I noticed that it would be relatively easy to include in these messages whether the token was obtained from POST data or a custom header, which would be useful for troubleshooting. The messages are specified here in the code. The new messages could look e.g. like-- "CSRF token (from POST) incorrect" "CSRF token (from 'X-CSRFToken' header) has incorrect length" The changes to CsrfViewMiddlewareTestMixin proposed in would make these cases easy to test.

REASON_NO_REFERER = "Referer checking failed - no Referer." REASON_BAD_REFERER = "Referer checking failed - %s does not match any trusted origins." REASON_NO_CSRF_COOKIE = "CSRF cookie not set."<del>REASON_CSRF_TOKEN_INCORRECT = 'CSRF token incorrect.'</del> REASON_CSRF_TOKEN_MISSING = 'CSRF token missing.' REASON_MALFORMED_REFERER = "Referer checking failed - Referer is malformed." REASON_INSECURE_REFERER = "Referer checking failed - Referer is insecure while host is secure."


Question: Include in CsrfViewMiddleware's bad CSRF token message where the token is from Description (last modified by Chris Jerdonek) Currently, if CsrfViewMiddleware encounters a bad CSRF token, it will reject the request with a message like-- "CSRF token incorrect" "CSRF token has incorrect length" I noticed that it would be relatively easy to include in these messages whether the token was obtained from POST data or a custom header, which would be useful for troubleshooting. The messages are specified here in the code. The new messages could look e.g. like-- "CSRF token (from POST) incorrect" "CSRF token (from 'X-CSRFToken' header) has incorrect length" The changes to CsrfViewMiddlewareTestMixin proposed in would make these cases easy to test.

if not is_same_domain(referer.netloc, good_referer):             raise RejectRequest(REASON_BAD_REFERER % referer.geturl()) <ins>    def _bad_token_message(self, reason, token_source):        if token_source != 'POST':            # Assume it is a settings.CSRF_HEADER_NAME value.            header_name = HttpHeaders.parse_header_name(token_source)            token_source = f'the {header_name!r} HTTP header'        return f'CSRF token from {token_source} {reason}.'</ins>     def _check_token(self, request):         # Access csrf_token via self._get_token() as rotate_token() may have         # been called by an authentication middleware during the


Question: Include in CsrfViewMiddleware's bad CSRF token message where the token is from Description (last modified by Chris Jerdonek) Currently, if CsrfViewMiddleware encounters a bad CSRF token, it will reject the request with a message like-- "CSRF token incorrect" "CSRF token has incorrect length" I noticed that it would be relatively easy to include in these messages whether the token was obtained from POST data or a custom header, which would be useful for troubleshooting. The messages are specified here in the code. The new messages could look e.g. like-- "CSRF token (from POST) incorrect" "CSRF token (from 'X-CSRFToken' header) has incorrect length" The changes to CsrfViewMiddlewareTestMixin proposed in would make these cases easy to test.

request_csrf_token = request.META[settings.CSRF_HEADER_NAME]             except KeyError:                 raise RejectRequest(REASON_CSRF_TOKEN_MISSING)            token_source = settings.CSRF_HEADER_NAME        else:            token_source = 'POST'          try:             request_csrf_token = _sanitize_token(request_csrf_token)         except InvalidTokenFormat as exc:            raise RejectRequest(f'CSRF token {exc.reason}.')            reason = self._bad_token_message(exc.reason, token_source)            raise RejectRequest(reason)          if not _compare_masked_tokens(request_csrf_token, csrf_token):            raise RejectRequest(REASON_CSRF_TOKEN_INCORRECT)            reason = self._bad_token_message('incorrect', token_source)            raise RejectRequest(reason)      def process_request(self, request):         try:


Question: BoundWidget.idforlabel ignores id set by ChoiceWidget.options Description If you look at the implementation of BoundField.subwidgets class BoundField: ... def subwidgets(self): id = ('id') or attrs = {'id': id} if id else {} attrs = self.buildwidgetattrs(attrs) return [ BoundWidget(, widget, ) for widget in (self.htmlname, (), attrs=attrs) ] one sees that (self.htmlname, (), attrs=attrs) returns a dict and assigns it to widget. Now widget['attrs']['id'] contains the "id" we would like to use when rendering the label of our CheckboxSelectMultiple. However BoundWidget.idforlabel() is implemented as class BoundWidget: ... def idforlabel(self): return 'id%s%s' % (['name'], ['index']) ignoring the id available through ['attrs']['id']. This re-implementation for rendering the "id" is confusing and presumably not intended. Nobody has probably realized that so far, because rarely the autoid-argument is overridden when initializing a form. If however we do, one would assume that the method BoundWidget.idforlabel renders that string as specified through the autoid format-string. By changing the code from above to class BoundWidget: ... def idforlabel(self): return ['attrs']['id'] that function behaves as expected. Please note that this error only occurs when rendering the subwidgets of a widget of type CheckboxSelectMultiple. This has nothing to do with the method BoundField.idforlabel().

@property     def id_for_label(self):        return 'id_%s_%s' % (self.data['name'], self.data['index'])        return self.data['attrs'].get('id')      @property     def choice_label(self):


Question: urlize() does not handle html escaped string and trailing punctuation correctly Description Example: urlize('Search for ! and see.') 'Search for <a href=""</a! and see.' 'Search for <a href=""</alt! and see.'

middle_unescaped = html.unescape(middle)             stripped = middle_unescaped.rstrip(TRAILING_PUNCTUATION_CHARS)             if middle_unescaped != stripped:                trail = middle[len(stripped):] + trail                middle = middle[:len(stripped) - len(middle_unescaped)]                punctuation_count = len(middle_unescaped) - len(stripped)                trail = middle[-punctuation_count:] + trail                middle = middle[:-punctuation_count]                 trimmed_something = True         return lead, middle, trail


Question: JSONField.bounddata raises TypeError when data is None Description Hi, When a bound form with no value provided for the JSONField is rendered, it will currently crash. class JSONForm(Form): jsonfield = JSONField(required=False) form = JSONForm({}) assert form.as_p() raises TypeError: the JSON object must be str, bytes or bytearray, not NoneType A fix has been created already by here:

def bound_data(self, data, initial):         if self.disabled:             return initial<ins>        if data is None:            return None</ins>         try:             return json.loads(data, cls=self.decoder)         except json.JSONDecodeError:


Question: Include number of rows matched in bulkupdate() return value Description Currently, bulkupdate() returns None, unlike update(), which returns the number of rows matched. It looks like it would be easy to add the same functionality to bulkupdate() since bulkupdate() simply calls update() repeatedly:  I.e. the return values could simply be and returned.

if any(f.primary_key for f in fields):             raise ValueError('bulk_update() cannot be used with primary key fields.')         if not objs:            return            return 0         # PK is used twice in the resulting update query, once in the filter         # and once in the WHEN. Each field will also have one CAST.         max_batch_size = connections[self.db].ops.bulk_batch_size(['pk', 'pk'] + fields, objs)


Question: Include number of rows matched in bulkupdate() return value Description Currently, bulkupdate() returns None, unlike update(), which returns the number of rows matched. It looks like it would be easy to add the same functionality to bulkupdate() since bulkupdate() simply calls update() repeatedly:  I.e. the return values could simply be and returned.

case_statement = Cast(case_statement, output_field=field)                 update_kwargs[field.attname] = case_statement             updates.append(([obj.pk for obj in batch_objs], update_kwargs))        rows_updated = 0         with transaction.atomic(using=self.db, savepoint=False):             for pks, update_kwargs in updates:                self.filter(pk__in=pks).update(**update_kwargs)                rows_updated += self.filter(pk__in=pks).update(**update_kwargs)        return rows_updated     bulk_update.alters_data = True      def get_or_create(self, defaults=None, **kwargs):


Question: Missing import statement in generated migration (NameError: name 'models' is not defined) Description I found a bug in Django's latest release: 3.2.4. Given the following contents of from import models class MyField(models.TextField): pass class MyBaseModel(models.Model): class Meta: abstract = True class MyMixin: pass class MyModel(MyMixin, MyBaseModel): name = MyField(primarykey=True) The makemigrations command will generate the following migration file: import from import migrations class Migration(migrations.Migration): initial = True dependencies = [ ] operations = [ migrations.CreateModel( name='MyModel', fields=[ ('name', (primarykey=True, serialize=False)), ], options={ 'abstract': False, }, bases=(, models.Model), ), ] Which will then fail with the following error: File "", line 7, in <moduleclass Migration(migrations.Migration): File "", line 23, in Migration bases=(, models.Model), NameError: name 'models' is not defined Expected behavior: Django generates a migration file that is valid Python. Actual behavior: Django generates a migration file that is missing an import statement. I think this is a bug of the module , but I'm not sure. I will be happy to assist with debugging. Thanks for your attention, Jaap Joris

class TypeSerializer(BaseSerializer):     def serialize(self):         special_cases = [            (models.Model, "models.Model", []),            (models.Model, "models.Model", ['from django.db import models']),             (type(None), 'type(None)', []),         ]         for case, string, imports in special_cases:


Question: logging output should include the database alias Description (last modified by David Winterbottom) As this is essential information when working with database routing. PR:

'time': '%.3f' % duration,             })             logger.debug(                '(%.3f) %s; args=%s',                '(%.3f) %s; args=%s; alias=%s',                 duration,                 sql,                 params,                extra={'duration': duration, 'sql': sql, 'params': params},                self.db.alias,                extra={'duration': duration, 'sql': sql, 'params': params, 'alias': self.db.alias},             )


Question: CsrfViewMiddleware.processresponse()'s csrfcookieneedsreset and csrfcookieset logic isn't right Description I noticed that the csrfcookieneedsreset and csrfcookieset logic inside CsrfViewMiddleware.processresponse() isn't right:  Consequently-- self.settoken(request, response) can get called twice in some circumstances, even if response.csrfcookieset is true at the beginning, and the cookie can fail to be reset in some circumstances, even if csrfcookieneeds_reset is true at the beginning. (I previously let know about this issue, and they said it was okay to resolve this publicly.)

return self._accept(request)      def process_response(self, request, response):        if not getattr(request, 'csrf_cookie_needs_reset', False):            if getattr(response, 'csrf_cookie_set', False):                return response        if not request.META.get("CSRF_COOKIE_USED", False):            return response        # Send the CSRF cookie whenever the cookie is being used (even if the        # client already has it) in order to renew the expiry timer, but only        # if it hasn't already been sent during this request-response cycle.        # Also, send the cookie no matter what if a reset was requested.        if (            getattr(request, 'csrf_cookie_needs_reset', False) or (                request.META.get('CSRF_COOKIE_USED') and                not getattr(response, 'csrf_cookie_set', False)            )        ):            self._set_token(request, response)            # Update state to prevent _set_token() from being unnecessarily            # called again in process_response() by other instances of            # CsrfViewMiddleware. This can happen e.g. when both a decorator            # and middleware are used. However, the csrf_cookie_needs_reset            # attribute is still respected in subsequent calls e.g. in case            # rotate_token() is called in process_response() later by custom            # middleware but before those subsequent calls.            response.csrf_cookie_set = True            request.csrf_cookie_needs_reset = False         # Set the CSRF cookie even if it's already set, so we renew        # the expiry timer.        self._set_token(request, response)        response.csrf_cookie_set = True         return response


Question: Tighten up the regular expression used by parsetime to accept less 'invalid' options. Description As per discussion in the ticket and on the Github comments for same, currently the timere allows for some variations which it arguably shouldn't. For the historical record's sake, the current regex is: (?P<hour\d{1,2}):(?P<minute\d{1,2})(?::(?P<second\d{1,2})(?:\d{0,6})?)? where you can see a whole lot of it ends up optional, and there are some ways in which that can be made to accept what we'd probably call 'invalid' (though strictly speaking the result is correct for the input portions): from import parsetime parsetime('0:5: ') (0, 5) If possible, we should derive examples of which strings might current pass and decide which, if any of them, shouldn't be accepted. It's probably also fine to leave the whole thing as-is (be liberal in what you accept etc) and just add them as necessary to the examples of valid inputs, so in future it doesn't come up again beyond "thats just an accepted quirk"

time_re = _lazy_re_compile(     r'(?P<hour>\d{1,2}):(?P<minute>\d{1,2})'    r'(?::(?P<second>\d{1,2})(?:[\.,](?P<microsecond>\d{1,6})\d{0,6})?)?'    r'(?::(?P<second>\d{1,2})(?:[\.,](?P<microsecond>\d{1,6})\d{0,6})?)?$' )  datetime_re = _lazy_re_compile(


Question: Add CSS class for non form errors in FormSets Description (last modified by Ties Jan Hefting) Forms add the nonfield CSS class for non field errors in ErrorList instances. This is documented in a section on rendering form error messages. Similarly, in FormSets I'd expect to see the nonform CSS class for non form errors. This would allow a custom ErrorList to make a distinction in form field errors, non field errors (forms) and non form errors (FormSets) when rendering error messages. Therefore I'd suggest to add this nonform CSS class and document it for developers to use.

self._non_form_errors.         """         self._errors = []        self._non_form_errors = self.error_class()        self._non_form_errors = self.error_class(error_class='nonform')         empty_forms_count = 0          if not self.is_bound:  # Stop further processing.


Question: Add CSS class for non form errors in FormSets Description (last modified by Ties Jan Hefting) Forms add the nonfield CSS class for non field errors in ErrorList instances. This is documented in a section on rendering form error messages. Similarly, in FormSets I'd expect to see the nonform CSS class for non form errors. This would allow a custom ErrorList to make a distinction in form field errors, non field errors (forms) and non form errors (FormSets) when rendering error messages. Therefore I'd suggest to add this nonform CSS class and document it for developers to use.

# Give self.clean() a chance to do cross-form validation.             self.clean()         except ValidationError as e:            self._non_form_errors = self.error_class(e.error_list)            self._non_form_errors = self.error_class(                e.error_list,                error_class='nonform'            )      def clean(self):         """


Question: BaseForm's cleanfields() and changeddata should access values via BoundField Description (last modified by Chris Jerdonek) While working on , I noticed that BaseForm.cleanfields() and BaseForm.changeddata don't currently access their values through a BoundField object. It would be better for consistency if they did, and to reduce the number of code paths. One consequence of the current code is that form.cleanfields() can return a different value from form[name].initial when they should be the same. This case is almost, but not quite, covered by testdatetimecleaninitialcallabledisabled() (the test can be adjusted to cover this case). As part of this ticket and in line with accessing data through the BoundField objects, I noticed that the code would also be simpler if the per-field logic of changeddata() were moved into a method of the BoundField class. It could be called something like bf.didchange(). This would be more appropriate because whether form data changed for a field is a property of its BoundField (as it depends on the underlying form data), as opposed to the unbound field. With this change, the method could change from its current ~20 lines to something like this-- def changeddata(self): return [name for name, bf in self.bounditems() if bf.didchange()] A similar change could be made to BaseForm.clean_fields().

import datetime import re <ins>from django.core.exceptions import ValidationError</ins> from django.forms.utils import flatatt, pretty_name from django.forms.widgets import Textarea, TextInput from django.utils.functional import cached_property


Question: BaseForm's cleanfields() and changeddata should access values via BoundField Description (last modified by Chris Jerdonek) While working on , I noticed that BaseForm.cleanfields() and BaseForm.changeddata don't currently access their values through a BoundField object. It would be better for consistency if they did, and to reduce the number of code paths. One consequence of the current code is that form.cleanfields() can return a different value from form[name].initial when they should be the same. This case is almost, but not quite, covered by testdatetimecleaninitialcallabledisabled() (the test can be adjusted to cover this case). As part of this ticket and in line with accessing data through the BoundField objects, I noticed that the code would also be simpler if the per-field logic of changeddata() were moved into a method of the BoundField class. It could be called something like bf.didchange(). This would be more appropriate because whether form data changed for a field is a property of its BoundField (as it depends on the underlying form data), as opposed to the unbound field. With this change, the method could change from its current ~20 lines to something like this-- def changeddata(self): return [name for name, bf in self.bounditems() if bf.didchange()] A similar change could be made to BaseForm.clean_fields().

"""         Return the data for this BoundField, or None if it wasn't given.         """        return self.form._field_data_value(self.field, self.html_name)        return self.form._widget_data_value(self.field.widget, self.html_name)      def value(self):         """


Question: BaseForm's cleanfields() and changeddata should access values via BoundField Description (last modified by Chris Jerdonek) While working on , I noticed that BaseForm.cleanfields() and BaseForm.changeddata don't currently access their values through a BoundField object. It would be better for consistency if they did, and to reduce the number of code paths. One consequence of the current code is that form.cleanfields() can return a different value from form[name].initial when they should be the same. This case is almost, but not quite, covered by testdatetimecleaninitialcallabledisabled() (the test can be adjusted to cover this case). As part of this ticket and in line with accessing data through the BoundField objects, I noticed that the code would also be simpler if the per-field logic of changeddata() were moved into a method of the BoundField class. It could be called something like bf.didchange(). This would be more appropriate because whether form data changed for a field is a property of its BoundField (as it depends on the underlying form data), as opposed to the unbound field. With this change, the method could change from its current ~20 lines to something like this-- def changeddata(self): return [name for name, bf in self.bounditems() if bf.didchange()] A similar change could be made to BaseForm.clean_fields().

data = self.field.bound_data(self.data, data)         return self.field.prepare_value(data)     def _has_changed(self):        field = self.field        if field.show_hidden_initial:            hidden_widget = field.hidden_widget()            initial_value = self.form._widget_data_value(                hidden_widget, self.html_initial_name,            )            try:                initial_value = field.to_python(initial_value)            except ValidationError:                # Always assume data has changed if validation fails.                return True        else:            initial_value = self.initial        return field.has_changed(initial_value, self.data)     def label_tag(self, contents=None, attrs=None, label_suffix=None):         """         Wrap the given contents in a , if the field has an ID attribute.diff --git a/django/forms/forms.py b/django/forms/forms.py-- a/django/forms/forms.py++ b/django/forms/forms.py


Question: BaseForm's cleanfields() and changeddata should access values via BoundField Description (last modified by Chris Jerdonek) While working on , I noticed that BaseForm.cleanfields() and BaseForm.changeddata don't currently access their values through a BoundField object. It would be better for consistency if they did, and to reduce the number of code paths. One consequence of the current code is that form.cleanfields() can return a different value from form[name].initial when they should be the same. This case is almost, but not quite, covered by testdatetimecleaninitialcallabledisabled() (the test can be adjusted to cover this case). As part of this ticket and in line with accessing data through the BoundField objects, I noticed that the code would also be simpler if the per-field logic of changeddata() were moved into a method of the BoundField class. It could be called something like bf.didchange(). This would be more appropriate because whether form data changed for a field is a property of its BoundField (as it depends on the underlying form data), as opposed to the unbound field. With this change, the method could change from its current ~20 lines to something like this-- def changeddata(self): return [name for name, bf in self.bounditems() if bf.didchange()] A similar change could be made to BaseForm.clean_fields().

'fields': ';'.join(self.fields),         }     def _bound_items(self):        """Yield (name, bf) pairs, where bf is a BoundField object."""        for name in self.fields:            yield name, self[name]     def __iter__(self):        """Yield the form's fields as BoundField objects."""         for name in self.fields:             yield self[name]


Question: BaseForm's cleanfields() and changeddata should access values via BoundField Description (last modified by Chris Jerdonek) While working on , I noticed that BaseForm.cleanfields() and BaseForm.changeddata don't currently access their values through a BoundField object. It would be better for consistency if they did, and to reduce the number of code paths. One consequence of the current code is that form.cleanfields() can return a different value from form[name].initial when they should be the same. This case is almost, but not quite, covered by testdatetimecleaninitialcallabledisabled() (the test can be adjusted to cover this case). As part of this ticket and in line with accessing data through the BoundField objects, I noticed that the code would also be simpler if the per-field logic of changeddata() were moved into a method of the BoundField class. It could be called something like bf.didchange(). This would be more appropriate because whether form data changed for a field is a property of its BoundField (as it depends on the underlying form data), as opposed to the unbound field. With this change, the method could change from its current ~20 lines to something like this-- def changeddata(self): return [name for name, bf in self.bounditems() if bf.didchange()] A similar change could be made to BaseForm.clean_fields().

# widgets split data over several HTML fields.         return widget.value_from_datadict(self.data, self.files, html_name)     def _field_data_value(self, field, html_name):        return self._widget_data_value(field.widget, html_name)     def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row):         "Output HTML. Used by as_table(), as_ul(), as_p()."         # Errors that should be displayed above all fields.         top_errors = self.non_field_errors().copy()         output, hidden_fields = [], []         for name, field in self.fields.items():        for name, bf in self._bound_items():            field = bf.field             html_class_attr = ''            bf = self[name]             bf_errors = self.error_class(bf.errors)             if bf.is_hidden:                 if bf_errors:


Question: BaseForm's cleanfields() and changeddata should access values via BoundField Description (last modified by Chris Jerdonek) While working on , I noticed that BaseForm.cleanfields() and BaseForm.changeddata don't currently access their values through a BoundField object. It would be better for consistency if they did, and to reduce the number of code paths. One consequence of the current code is that form.cleanfields() can return a different value from form[name].initial when they should be the same. This case is almost, but not quite, covered by testdatetimecleaninitialcallabledisabled() (the test can be adjusted to cover this case). As part of this ticket and in line with accessing data through the BoundField objects, I noticed that the code would also be simpler if the per-field logic of changeddata() were moved into a method of the BoundField class. It could be called something like bf.didchange(). This would be more appropriate because whether form data changed for a field is a property of its BoundField (as it depends on the underlying form data), as opposed to the unbound field. With this change, the method could change from its current ~20 lines to something like this-- def changeddata(self): return [name for name, bf in self.bounditems() if bf.didchange()] A similar change could be made to BaseForm.clean_fields().

self._post_clean()      def _clean_fields(self):        for name, field in self.fields.items():            if field.disabled:                value = self.get_initial_for_field(field, name)            else:                value = self._field_data_value(field, self.add_prefix(name))        for name, bf in self._bound_items():            field = bf.field            value = bf.initial if field.disabled else bf.data             try:                 if isinstance(field, FileField):                    initial = self.get_initial_for_field(field, name)                    value = field.clean(value, initial)                    value = field.clean(value, bf.initial)                 else:                     value = field.clean(value)                 self.cleaned_data[name] = value


Question: BaseForm's cleanfields() and changeddata should access values via BoundField Description (last modified by Chris Jerdonek) While working on , I noticed that BaseForm.cleanfields() and BaseForm.changeddata don't currently access their values through a BoundField object. It would be better for consistency if they did, and to reduce the number of code paths. One consequence of the current code is that form.cleanfields() can return a different value from form[name].initial when they should be the same. This case is almost, but not quite, covered by testdatetimecleaninitialcallabledisabled() (the test can be adjusted to cover this case). As part of this ticket and in line with accessing data through the BoundField objects, I noticed that the code would also be simpler if the per-field logic of changeddata() were moved into a method of the BoundField class. It could be called something like bf.didchange(). This would be more appropriate because whether form data changed for a field is a property of its BoundField (as it depends on the underlying form data), as opposed to the unbound field. With this change, the method could change from its current ~20 lines to something like this-- def changeddata(self): return [name for name, bf in self.bounditems() if bf.didchange()] A similar change could be made to BaseForm.clean_fields().

@cached_property     def changed_data(self):        data = []        for name, field in self.fields.items():            data_value = self._field_data_value(field, self.add_prefix(name))            if not field.show_hidden_initial:                # Use the BoundField's initial as this is the value passed to                # the widget.                initial_value = self[name].initial            else:                initial_prefixed_name = self.add_initial_prefix(name)                hidden_widget = field.hidden_widget()                try:                    initial_value = field.to_python(                        self._widget_data_value(hidden_widget, initial_prefixed_name)                    )                except ValidationError:                    # Always assume data has changed if validation fails.                    data.append(name)                    continue            if field.has_changed(initial_value, data_value):                data.append(name)        return data        return [name for name, bf in self._bound_items() if bf._has_changed()]      @property     def media(self):


Question: Allow delete to provide a success message through a mixin. Description Add a mixin to show a message on successful object deletion.

from django.core.exceptions import ImproperlyConfiguredfrom django.forms import models as model_formsfrom django.forms import Form, models as model_forms from django.http import HttpResponseRedirect from django.views.generic.base import ContextMixin, TemplateResponseMixin, View from django.views.generic.detail import (


Question: Allow delete to provide a success message through a mixin. Description Add a mixin to show a message on successful object deletion.

"No URL to redirect to. Provide a success_url.")  class BaseDeleteView(DeletionMixin, BaseDetailView):class BaseDeleteView(DeletionMixin, FormMixin, BaseDetailView):     """     Base view for deleting an object.      Using this base class requires subclassing to provide a response mixin.     """    form_class = Form    def post(self, request, *args, **kwargs):        # Set self.object before the usual form processing flow.        # Inlined because having DeletionMixin as the first base, for        # get_success_url(), makes leveraging super() with ProcessFormView        # overly complex.        self.object = self.get_object()        form = self.get_form()        if form.is_valid():            return self.form_valid(form)        else:            return self.form_invalid(form)    def form_valid(self, form):        success_url = self.get_success_url()        self.object.delete()        return HttpResponseRedirect(success_url)   class DeleteView(SingleObjectTemplateResponseMixin, BaseDeleteView):


Question: Move special-case logic in BoundField.initial() to BaseForm.getinitialforfield() Description This is another follow-up to ticket . Currently, BoundField.initial() has logic to special-case time and datetime objects:  I noticed that this logic can be moved to BaseForm.getinitialforfield(), and in particular under the if callable(value) block:  Eventually, I think it could make sense to go further and move some of this logic to a new method of the Field class, which could permit the special-casing to be handled by overriding in sub-classes that use times and datetimes.

<del>import datetime</del> import re  from django.core.exceptions import ValidationError


Question: Move special-case logic in BoundField.initial() to BaseForm.getinitialforfield() Description This is another follow-up to ticket . Currently, BoundField.initial() has logic to special-case time and datetime objects:  I noticed that this logic can be moved to BaseForm.getinitialforfield(), and in particular under the if callable(value) block:  Eventually, I think it could make sense to go further and move some of this logic to a new method of the Field class, which could permit the special-casing to be handled by overriding in sub-classes that use times and datetimes.

@cached_property     def initial(self):        data = self.form.get_initial_for_field(self.field, self.name)        # If this is an auto-generated default date, nix the microseconds for        # standardized handling. See #22502.        if (isinstance(data, (datetime.datetime, datetime.time)) and                not self.field.widget.supports_microseconds):            data = data.replace(microsecond=0)        return data        return self.form.get_initial_for_field(self.field, self.name)      def build_widget_attrs(self, attrs, widget=None):         widget = widget or self.field.widgetdiff --git a/django/forms/forms.py b/django/forms/forms.py-- a/django/forms/forms.py++ b/django/forms/forms.py


Question: Move special-case logic in BoundField.initial() to BaseForm.getinitialforfield() Description This is another follow-up to ticket . Currently, BoundField.initial() has logic to special-case time and datetime objects:  I noticed that this logic can be moved to BaseForm.getinitialforfield(), and in particular under the if callable(value) block:  Eventually, I think it could make sense to go further and move some of this logic to a new method of the Field class, which could permit the special-casing to be handled by overriding in sub-classes that use times and datetimes.

"""  import copy<ins>import datetime</ins>  from django.core.exceptions import NON_FIELD_ERRORS, ValidationError from django.forms.fields import Field, FileField


Question: Move special-case logic in BoundField.initial() to BaseForm.getinitialforfield() Description This is another follow-up to ticket . Currently, BoundField.initial() has logic to special-case time and datetime objects:  I noticed that this logic can be moved to BaseForm.getinitialforfield(), and in particular under the if callable(value) block:  Eventually, I think it could make sense to go further and move some of this logic to a new method of the Field class, which could permit the special-casing to be handled by overriding in sub-classes that use times and datetimes.

value = self.initial.get(field_name, field.initial)         if callable(value):             value = value()<ins>        # If this is an auto-generated default date, nix the microseconds        # for standardized handling. See #22502.        if (isinstance(value, (datetime.datetime, datetime.time)) and                not field.widget.supports_microseconds):            value = value.replace(microsecond=0)</ins>         return value


Question: decimal.InvalidOperation error on DecimalField form field Description I have a form with DecimalField and maxvalue argument: class OrderForm(forms.ModelForm): sum = DecimalField(maxvalue=12) class Meta: model = Order fields = ['sum'] class Order(models.Model): sum = models.DecimalField( 'Sum', maxdigits=18, decimalplaces=2, default=0 ) If I pass "NaN" value to this form it will fail with decimal.InvalidOperation error.

raise ValidationError(self.error_messages['invalid'], code='invalid')         return value <ins>    def validate(self, value):        super().validate(value)        if value in self.empty_values:            return        if not value.is_finite():            raise ValidationError(                self.error_messages['invalid'],                code='invalid',                params={'value': value},            )</ins>     def widget_attrs(self, widget):         attrs = super().widget_attrs(widget)         if isinstance(widget, NumberInput) and 'step' not in widget.attrs:


Question: ResolverMatch with some views is not pickleable Description given something like the following: def myfbv(request): return HttpResponse('yay') urlpatterns = [ url('whatever', myfbv, name='myfbv'), ] It is possible to do the following: from import resolve from pickle import dumps, loads loads(dumps(resolve('whatever'))) and end up with a working ResolverMatch However, given a Class Based View (mapped to a urlconf via ()), or something like , you get something like the following: dumps(resolve('/admin/myapp/')) [...] PicklingError: Can't pickle <function appindex at 0x109f05de8: it's not found as PicklingError: Can't pickle <function Homepage at 0x109f16b90: it's not the same object as Both of which are raised by pickle's saveglobal(self, obj, name, pack) which recognises that it's a module+name combo (thus should be in scope) but isn't the same object in identity (if x is not y) Ordinarily, this is not a problem, but resolvermatch is set onto a request, and I'm using the with multiprocessing, which requires the ability to pickle data to send back and forth, and evidently somewhere within the TemplateResponses I'm dealing with, the request is being serialised (if I had to guess -- probably in the context) and is sometimes failing (depending on the type of view mounted) Ideally, every ResolverMatch should be serialisable, or none should be, instead of the current situation where the project's urlconf may denote success or failure.

import re import string from importlib import import_module<ins>from pickle import PicklingError</ins> from urllib.parse import quote  from asgiref.local import Local


Question: ResolverMatch with some views is not pickleable Description given something like the following: def myfbv(request): return HttpResponse('yay') urlpatterns = [ url('whatever', myfbv, name='myfbv'), ] It is possible to do the following: from import resolve from pickle import dumps, loads loads(dumps(resolve('whatever'))) and end up with a working ResolverMatch However, given a Class Based View (mapped to a urlconf via ()), or something like , you get something like the following: dumps(resolve('/admin/myapp/')) [...] PicklingError: Can't pickle <function appindex at 0x109f05de8: it's not found as PicklingError: Can't pickle <function Homepage at 0x109f16b90: it's not the same object as Both of which are raised by pickle's saveglobal(self, obj, name, pack) which recognises that it's a module+name combo (thus should be in scope) but isn't the same object in identity (if x is not y) Ordinarily, this is not a problem, but resolvermatch is set onto a request, and I'm using the with multiprocessing, which requires the ability to pickle data to send back and forth, and evidently somewhere within the TemplateResponses I'm dealing with, the request is being serialised (if I had to guess -- probably in the context) and is sometimes failing (depending on the type of view mounted) Ideally, every ResolverMatch should be serialisable, or none should be, instead of the current situation where the project's urlconf may denote success or failure.

)         ) <ins>    def __reduce_ex__(self, protocol):        raise PicklingError(f'Cannot pickle {self.__class__.__qualname__}.')</ins>  def get_resolver(urlconf=None):     if urlconf is None:


Question: () doesn't clear deferred field when chaining with only(). Description Considering a simple Company model with four fields: id, name, tradenumber and country. If we evaluate a queryset containing a .defer() following a .only(), the generated sql query selects unexpected fields. For example: ("name").defer("name") loads all the fields with the following query: SELECT "company"."id", "company"."name", "company"."tradenumber", "company"."country" FROM "company" and ("name").defer("name").defer("country") also loads all the fields with the same query: SELECT "company"."id", "company"."name", "company"."trade_number", "company"."country" FROM "company" In those two cases, i would expect the sql query to be: SELECT "company"."id" FROM "company" In the following example, we get the expected behavior: ("name", "country").defer("name") only loads "id" and "country" fields with the following query: SELECT "company"."id", "company"."country" FROM "company"

self.deferred_loading = existing.union(field_names), True         else:             # Remove names from the set of any existing "immediate load" names.            self.deferred_loading = existing.difference(field_names), False            if new_existing := existing.difference(field_names):                self.deferred_loading = new_existing, False            else:                self.clear_deferred_loading()                if new_only := set(field_names).difference(existing):                    self.deferred_loading = new_only, True      def add_immediate_loading(self, field_names):         """


Question: Missing call on in Description In 3.2 identity property has been to all ForeignObjectRel to make it possible to compare them. A hash is derived from said identity and it's possible because identity is a tuple. To make limitchoicesto hashable (one of this tuple elements), there's a call to makehashable. It happens that throughfields can be a list. In such case, this makehashable call is missing in ManyToManyRel. For some reason it only fails on checking proxy model. I think proxy models have 29 checks and normal ones 24, hence the issue, but that's just a guess. Minimal repro: class Parent(models.Model): name = models.CharField(maxlength=256) class ProxyParent(Parent): class Meta: proxy = True class Child(models.Model): parent = models.ForeignKey(Parent, ondelete=models.CASCADE) manytomanyfield = models.ManyToManyField( to=Parent, through="ManyToManyModel", throughfields=['child', 'parent'], relatedname="something" ) class ManyToManyModel(models.Model): parent = models.ForeignKey(Parent, ondelete=models.CASCADE, relatedname='+') child = models.ForeignKey(Child, ondelete=models.CASCADE, relatedname='+') secondchild = models.ForeignKey(Child, ondelete=models.CASCADE, null=True, default=None) Which will result in File "", line 23, in <modulemain() File "", line 19, in main executefromcommandline() File "/home/tom/PycharmProjects/brokenm2mproject/venv/lib/python3.8/site-", line 419, in executefromcommandline utility.execute() File "/home/tom/PycharmProjects/brokenm2mproject/venv/lib/python3.8/site-", line 413, in execute self.fetchcommand(subcommand).runfromargv() File "/home/tom/PycharmProjects/brokenm2mproject/venv/lib/python3.8/site-", line 354, in runfromargv self.execute(args, cmdoptions) File "/home/tom/PycharmProjects/brokenm2mproject/venv/lib/python3.8/site-", line 393, in execute () File "/home/tom/PycharmProjects/brokenm2mproject/venv/lib/python3.8/site-", line 419, in check allissues = checks.runchecks( File "/home/tom/PycharmProjects/brokenm2mproject/venv/lib/python3.8/site-", line 76, in runchecks newerrors = check(appconfigs=appconfigs, databases=databases) File "/home/tom/PycharmProjects/brokenm2mproject/venv/lib/python3.8/site-", line 34, in checkallmodels ((kwargs)) File "/home/tom/PycharmProjects/brokenm2mproject/venv/lib/python3.8/site-", line 1277, in check cls.checkfieldnameclashes(), File "/home/tom/PycharmProjects/djangbrokenm2mprojectProject/venv/lib/python3.8/site-", line 1465, in checkfieldnameclashes if f not in usedfields: File "/home/tom/PycharmProjects/brokenm2mproject/venv/lib/python3.8/site-", line 140, in hash return hash(self.identity) TypeError: unhashable type: 'list' Solution: Add missing makehashable call on self.throughfields in ManyToManyRel. Missing call on in Description In 3.2 identity property has been to all ForeignObjectRel to make it possible to compare them. A hash is derived from said identity and it's possible because identity is a tuple. To make limitchoicesto hashable (one of this tuple elements), there's a call to makehashable. It happens that throughfields can be a list. In such case, this makehashable call is missing in ManyToManyRel. For some reason it only fails on checking proxy model. I think proxy models have 29 checks and normal ones 24, hence the issue, but that's just a guess. Minimal repro: class Parent(models.Model): name = models.CharField(maxlength=256) class ProxyParent(Parent): class Meta: proxy = True class Child(models.Model): parent = models.ForeignKey(Parent, ondelete=models.CASCADE) manytomanyfield = models.ManyToManyField( to=Parent, through="ManyToManyModel", throughfields=['child', 'parent'], relatedname="something" ) class ManyToManyModel(models.Model): parent = models.ForeignKey(Parent, ondelete=models.CASCADE, relatedname='+') child = models.ForeignKey(Child, ondelete=models.CASCADE, relatedname='+') secondchild = models.ForeignKey(Child, ondelete=models.CASCADE, null=True, default=None) Which will result in File "", line 23, in <modulemain() File "", line 19, in main executefromcommandline() File "/home/tom/PycharmProjects/brokenm2mproject/venv/lib/python3.8/site-", line 419, in executefromcommandline utility.execute() File "/home/tom/PycharmProjects/brokenm2mproject/venv/lib/python3.8/site-", line 413, in execute self.fetchcommand(subcommand).runfromargv() File "/home/tom/PycharmProjects/brokenm2mproject/venv/lib/python3.8/site-", line 354, in runfromargv self.execute(args, cmdoptions) File "/home/tom/PycharmProjects/brokenm2mproject/venv/lib/python3.8/site-", line 393, in execute () File "/home/tom/PycharmProjects/brokenm2mproject/venv/lib/python3.8/site-", line 419, in check allissues = checks.runchecks( File "/home/tom/PycharmProjects/brokenm2mproject/venv/lib/python3.8/site-", line 76, in runchecks newerrors = check(appconfigs=appconfigs, databases=databases) File "/home/tom/PycharmProjects/brokenm2mproject/venv/lib/python3.8/site-", line 34, in checkallmodels ((kwargs)) File "/home/tom/PycharmProjects/brokenm2mproject/venv/lib/python3.8/site-", line 1277, in check cls.checkfieldnameclashes(), File "/home/tom/PycharmProjects/djangbrokenm2mprojectProject/venv/lib/python3.8/site-", line 1465, in checkfieldnameclashes if f not in usedfields: File "/home/tom/PycharmProjects/brokenm2mproject/venv/lib/python3.8/site-", line 140, in hash return hash(self.identity) TypeError: unhashable type: 'list' Solution: Add missing makehashable call on self.throughfields in ManyToManyRel.

def identity(self):         return super().identity + (             self.through,            self.through_fields,            make_hashable(self.through_fields),             self.db_constraint,         )


Question: CSRF failure incorrectly reported on upload when there is a problem with storage Description Minimal reproduction app is in the attachment, although the only changes from the default template are: MEDIAURL = '/media/' MEDIAROOT = 'media/' FILEUPLOADMAXMEMORYSIZE = 1024 * 1024 FILEUPLOADTEMPDIR = MEDIAROOT + 'tmp' class File(models.Model): file = models.FileField() from .models import File (File) Required setup for the attached app: python migrate python createsuperuser Steps to reproduce 1) runserver 2) navigate and login to /admin/ 3) navigate to /admin/app/file/add/ Scenario 1. default state - file uploads works as expected Scenario 2. remove media/tmp directory - file uploads works only for files that fit in FILEUPLOADMAXMEMORYSIZE, error otherwise (see below) Scenario 3. remove whole media directory - error reported for all file uploads (see below) Exact error message: Forbidden (403) CSRF verification failed. Request aborted. Reason given for failure: CSRF token missing or incorrect. Expected behaviour: Filesystem error or similar reporting incorrect media storage setup. Comment: Yes, the setup in this scenario is invalid to begin with, but the error message has nothing to do with the actual problem. I suspect a real problem with an underlying filesystem will also get covered in the same way.

from django.conf import settings from django.core.exceptions import DisallowedHost, ImproperlyConfigured<ins>from django.http import UnreadablePostError</ins> from django.http.request import HttpHeaders from django.urls import get_callable from django.utils.cache import patch_vary_headers


Question: CSRF failure incorrectly reported on upload when there is a problem with storage Description Minimal reproduction app is in the attachment, although the only changes from the default template are: MEDIAURL = '/media/' MEDIAROOT = 'media/' FILEUPLOADMAXMEMORYSIZE = 1024 * 1024 FILEUPLOADTEMPDIR = MEDIAROOT + 'tmp' class File(models.Model): file = models.FileField() from .models import File (File) Required setup for the attached app: python migrate python createsuperuser Steps to reproduce 1) runserver 2) navigate and login to /admin/ 3) navigate to /admin/app/file/add/ Scenario 1. default state - file uploads works as expected Scenario 2. remove media/tmp directory - file uploads works only for files that fit in FILEUPLOADMAXMEMORYSIZE, error otherwise (see below) Scenario 3. remove whole media directory - error reported for all file uploads (see below) Exact error message: Forbidden (403) CSRF verification failed. Request aborted. Reason given for failure: CSRF token missing or incorrect. Expected behaviour: Filesystem error or similar reporting incorrect media storage setup. Comment: Yes, the setup in this scenario is invalid to begin with, but the error message has nothing to do with the actual problem. I suspect a real problem with an underlying filesystem will also get covered in the same way.

if request.method == 'POST':             try:                 request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')            except OSError:            except UnreadablePostError:                 # Handle a broken connection before we've completed reading the                 # POST data. process_view shouldn't raise any exceptions, so                 # we'll ignore and serve the user a 403 (assuming they're still


Question: Time-related checkfixdefaultvalue() methods can be optimized / simplified and have a bug Description I noticed that three of the checkfixdefaultvalue() method definitions in can be simplified. Here is one of them:  For example, in each of them, () is called even when the return value isn't needed / won't be used.

}  <ins>def _to_naive(value):    if timezone.is_aware(value):        value = timezone.make_naive(value, timezone.utc)    return valuedef _get_naive_now():    return _to_naive(timezone.now())</ins> class DateTimeCheckMixin:      def check(self, **kwargs):


Question: Time-related checkfixdefaultvalue() methods can be optimized / simplified and have a bug Description I noticed that three of the checkfixdefaultvalue() method definitions in can be simplified. Here is one of them:  For example, in each of them, () is called even when the return value isn't needed / won't be used.

def _check_fix_default_value(self):         return [] <ins>    # Concrete subclasses use this in their implementations of    # _check_fix_default_value().    def _check_if_value_fixed(self, value, now=None):        """        Check if the given value appears to have been provided as a "fixed"        time value, and include a warning in the returned list if it does. The        value argument must be a date object or aware/naive datetime object. If        now is provided, it must be a naive datetime object.        """        if now is None:            now = _get_naive_now()        offset = datetime.timedelta(seconds=10)        lower = now - offset        upper = now + offset        if isinstance(value, datetime.datetime):            value = _to_naive(value)        else:            assert isinstance(value, datetime.date)            lower = lower.date()            upper = upper.date()        if lower <= value <= upper:            return [                checks.Warning(                    'Fixed default value provided.',                    hint=(                        'It seems you set a fixed date / time / datetime '                        'value as default for this field. This may not be '                        'what you want. If you want to have the current date '                        'as default, use `django.utils.timezone.now`'                    ),                    obj=self,                    id='fields.W161',                )            ]        return []</ins>  class DateField(DateTimeCheckMixin, Field):     empty_strings_allowed = False


Question: Time-related checkfixdefaultvalue() methods can be optimized / simplified and have a bug Description I noticed that three of the checkfixdefaultvalue() method definitions in can be simplified. Here is one of them:  For example, in each of them, () is called even when the return value isn't needed / won't be used.

if not self.has_default():             return []         now = timezone.now()        if not timezone.is_naive(now):            now = timezone.make_naive(now, timezone.utc)         value = self.default         if isinstance(value, datetime.datetime):            if not timezone.is_naive(value):                value = timezone.make_naive(value, timezone.utc)            value = value.date()            value = _to_naive(value).date()         elif isinstance(value, datetime.date):            # Nothing to do, as dates don't have tz information             pass         else:             # No explicit date / datetime value -- no checks necessary             return []        offset = datetime.timedelta(days=1)        lower = (now - offset).date()        upper = (now + offset).date()        if lower <= value <= upper:            return [                checks.Warning(                    'Fixed default value provided.',                    hint='It seems you set a fixed date / time / datetime '                         'value as default for this field. This may not be '                         'what you want. If you want to have the current date '                         'as default, use `django.utils.timezone.now`',                    obj=self,                    id='fields.W161',                )            ]        return []        # At this point, value is a date object.        return self._check_if_value_fixed(value)      def deconstruct(self):         name, path, args, kwargs = super().deconstruct()


Question: Time-related checkfixdefaultvalue() methods can be optimized / simplified and have a bug Description I noticed that three of the checkfixdefaultvalue() method definitions in can be simplified. Here is one of them:  For example, in each of them, () is called even when the return value isn't needed / won't be used.

if not self.has_default():             return []         now = timezone.now()        if not timezone.is_naive(now):            now = timezone.make_naive(now, timezone.utc)         value = self.default        if isinstance(value, datetime.datetime):            second_offset = datetime.timedelta(seconds=10)            lower = now - second_offset            upper = now + second_offset            if timezone.is_aware(value):                value = timezone.make_naive(value, timezone.utc)        elif isinstance(value, datetime.date):            second_offset = datetime.timedelta(seconds=10)            lower = now - second_offset            lower = datetime.datetime(lower.year, lower.month, lower.day)            upper = now + second_offset            upper = datetime.datetime(upper.year, upper.month, upper.day)            value = datetime.datetime(value.year, value.month, value.day)        else:            # No explicit date / datetime value -- no checks necessary            return []        if lower <= value <= upper:            return [                checks.Warning(                    'Fixed default value provided.',                    hint='It seems you set a fixed date / time / datetime '                         'value as default for this field. This may not be '                         'what you want. If you want to have the current date '                         'as default, use `django.utils.timezone.now`',                    obj=self,                    id='fields.W161',                )            ]        if isinstance(value, (datetime.datetime, datetime.date)):            return self._check_if_value_fixed(value)        # No explicit date / datetime value -- no checks necessary.         return []      def get_internal_type(self):


Question: Time-related checkfixdefaultvalue() methods can be optimized / simplified and have a bug Description I noticed that three of the checkfixdefaultvalue() method definitions in can be simplified. Here is one of them:  For example, in each of them, () is called even when the return value isn't needed / won't be used.

if not self.has_default():             return []         now = timezone.now()        if not timezone.is_naive(now):            now = timezone.make_naive(now, timezone.utc)         value = self.default         if isinstance(value, datetime.datetime):            second_offset = datetime.timedelta(seconds=10)            lower = now - second_offset            upper = now + second_offset            if timezone.is_aware(value):                value = timezone.make_naive(value, timezone.utc)            now = None         elif isinstance(value, datetime.time):            second_offset = datetime.timedelta(seconds=10)            lower = now - second_offset            upper = now + second_offset            now = _get_naive_now()            # This will not use the right date in the race condition where now            # is just before the date change and value is just past 0:00.             value = datetime.datetime.combine(now.date(), value)            if timezone.is_aware(value):                value = timezone.make_naive(value, timezone.utc).time()         else:             # No explicit time / datetime value -- no checks necessary             return []        if lower <= value <= upper:            return [                checks.Warning(                    'Fixed default value provided.',                    hint='It seems you set a fixed date / time / datetime '                         'value as default for this field. This may not be '                         'what you want. If you want to have the current date '                         'as default, use `django.utils.timezone.now`',                    obj=self,                    id='fields.W161',                )            ]        return []        # At this point, value is a datetime object.        return self._check_if_value_fixed(value, now=now)      def deconstruct(self):         name, path, args, kwargs = super().deconstruct()


Question: Moving a unique constraint from uniquetogether to generate an invalid migration. Description You can see a demo example to show the bug at . I met a problem when I convert a uniquetogether to the unique=True attribute. first commit, everything is ok I create a model file. // class MyModel(models.Model): name = models.CharField(maxlength=32) class Meta: uniquetogether = ("name", ) the migrations file looks like this. // from import migrations, models class Migration(migrations.Migration): dependencies = [ ('testapp', '0001initial'), ] operations = [ migrations.AlterField( modelname='mymodel', name='name', field=models.CharField(maxlength=32, unique=True), ), migrations.AlterUniqueTogether( name='mymodel', uniquetogether=set(), ), ] second commit: then I remove the uniquetogether and add unique=True to the field model file class MyModel(models.Model): name = models.CharField(maxlength=32, unique=True) class Meta: pass # uniquetogether = ("name", ) 0002 migrations file class Migration(migrations.Migration): dependencies = [ ('testapp', '0001initial'), ] operations = [ migrations.AlterField( modelname='mymodel', name='name', field=models.CharField(maxlength=32, unique=True), ), migrations.AlterUniqueTogether( name='mymodel', uniquetogether=set(), ), ] However, when I apply the migrations, an error occurs; wangx python3 migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions, testapp Running migrations: Applying contenttypes.0001initial... OK Applying auth.0001initial... OK Applying admin.0001initial... OK Applying admin.0002logentryremoveautoadd... OK Applying admin.0003logentryaddactionflagchoices... OK Applying contenttypes.0002removecontenttypename... OK Applying auth.0002alterpermissionnamemaxlength... OK Applying auth.0003alteruseremailmaxlength... OK Applying auth.0004alteruserusernameopts... OK Applying auth.0005alteruserlastloginnull... OK Applying auth.0006requirecontenttypes0002... OK Applying auth.0007altervalidatorsadderrormessages... OK Applying auth.0008alteruserusernamemaxlength... OK Applying auth.0009alteruserlastnamemaxlength... OK Applying auth.0010altergroupnamemaxlength... OK Applying auth.0011updateproxypermissions... OK Applying sessions.0001initial... OK Applying testapp.0001initial... OK Applying testapp.0002auto202004221247...Traceback (most recent call last): File "/usr/local/lib/python3.6/dist-", line 86, in execute return (sql, params) File "/usr/local/lib/python3.6/dist-", line 74, in execute return (query, args) File "/usr/local/lib/python3.6/dist-", line 209, in execute res = (query) File "/usr/local/lib/python3.6/dist-", line 315, in query (q) File "/usr/local/lib/python3.6/dist-", line 239, in query (self, query) MySQLdb.exceptions.OperationalError: (1061, "Duplicate key name 'testappmymodelnameba5e2bd2uniq'") The above exception was the direct cause of the following exception: Traceback (most recent call last): File "", line 21, in <modulemain() File "", line 17, in main executefromcommandline() File "/usr/local/lib/python3.6/dist-", line 401, in executefromcommandline utility.execute() File "/usr/local/lib/python3.6/dist-", line 395, in execute self.fetchcommand(subcommand).runfromargv() File "/usr/local/lib/python3.6/dist-", line 328, in runfromargv self.execute(args, cmdoptions) File "/usr/local/lib/python3.6/dist-", line 369, in execute output = (args, options) File "/usr/local/lib/python3.6/dist-", line 83, in wrapped res = handlefunc(args, *kwargs) File "/usr/local/lib/python3.6/dist-", line 233, in handle fakeinitial=fakeinitial, File "/usr/local/lib/python3.6/dist-", line 117, in migrate state = self.migrateallforwards(state, plan, fullplan, fake=fake, fakeinitial=fakeinitial) File "/usr/local/lib/python3.6/dist-", line 147, in migrateallforwards state = self.applymigration(state, migration, fake=fake, fakeinitial=fakeinitial) File "/usr/local/lib/python3.6/dist-", line 245, in applymigration state = (state, schemaeditor) File "/usr/local/lib/python3.6/dist-", line 124, in apply operation.databaseforwards(self.applabel, schemaeditor, oldstate, projectstate) File "/usr/local/lib/python3.6/dist-", line 249, in databaseforwards schemaeditor.alterfield(frommodel, fromfield, tofield) File "/usr/local/lib/python3.6/dist-", line 565, in alterfield olddbparams, newdbparams, strict) File "/usr/local/lib/python3.6/dist-", line 745, in alterfield self.execute(self.createuniquesql(model, [])) File "/usr/local/lib/python3.6/dist-", line 142, in execute cursor.execute(sql, params) File "/usr/local/lib/python3.6/dist-", line 100, in execute return super().execute(sql, params) File "/usr/local/lib/python3.6/dist-", line 68, in execute return self.executewithwrappers(sql, params, many=False, executor=self.execute) File "/usr/local/lib/python3.6/dist-", line 77, in executewithwrappers return executor(sql, params, many, context) File "/usr/local/lib/python3.6/dist-", line 86, in execute return (sql, params) File "/usr/local/lib/python3.6/dist-", line 90, in exit raise djexcvalue.withtraceback(traceback) from excvalue File "/usr/local/lib/python3.6/dist-", line 86, in execute return (sql, params) File "/usr/local/lib/python3.6/dist-", line 74, in execute return (query, args) File "/usr/local/lib/python3.6/dist-", line 209, in execute res = (query) File "/usr/local/lib/python3.6/dist-", line 315, in query (q) File "/usr/local/lib/python3.6/dist-", line 239, in query (self, query) (1061, "Duplicate key name 'testappmymodelnameba5e2bd2uniq'") I check the sql for these migrations, it shows: wangx python3 sqlmigrate testapp 0001 -- Create model MyModel CREATE TABLE ( integer AUTOINCREMENT NOT NULL PRIMARY KEY, varchar(32) NOT NULL); ALTER TABLE ADD CONSTRAINT UNIQUE (); wangx python3 sqlmigrate testapp 0002 -- Alter field name on mymodel ALTER TABLE ADD CONSTRAINT UNIQUE (); -- Alter uniquetogether for mymodel (0 constraint(s)) ALTER TABLE DROP INDEX ; it looks like django will first create the index for unique=True second drop the index for uniquetogether=('name', ) but the program for creating index name generates the same index name :testappmymodelnameba5e2bd2uniq, so when django create the same index, Duplicate key name error occurs.

# Generate index removal operations before field is removed         self.generate_removed_constraints()         self.generate_removed_indexes()        # Generate field operations        # Generate field renaming operations.         self.generate_renamed_fields()        # Generate removal of foo together.        self.generate_removed_altered_unique_together()        self.generate_removed_altered_index_together()        # Generate field operations.         self.generate_removed_fields()         self.generate_added_fields()         self.generate_altered_fields()


Question: Moving a unique constraint from uniquetogether to generate an invalid migration. Description You can see a demo example to show the bug at . I met a problem when I convert a uniquetogether to the unique=True attribute. first commit, everything is ok I create a model file. // class MyModel(models.Model): name = models.CharField(maxlength=32) class Meta: uniquetogether = ("name", ) the migrations file looks like this. // from import migrations, models class Migration(migrations.Migration): dependencies = [ ('testapp', '0001initial'), ] operations = [ migrations.AlterField( modelname='mymodel', name='name', field=models.CharField(maxlength=32, unique=True), ), migrations.AlterUniqueTogether( name='mymodel', uniquetogether=set(), ), ] second commit: then I remove the uniquetogether and add unique=True to the field model file class MyModel(models.Model): name = models.CharField(maxlength=32, unique=True) class Meta: pass # uniquetogether = ("name", ) 0002 migrations file class Migration(migrations.Migration): dependencies = [ ('testapp', '0001initial'), ] operations = [ migrations.AlterField( modelname='mymodel', name='name', field=models.CharField(maxlength=32, unique=True), ), migrations.AlterUniqueTogether( name='mymodel', uniquetogether=set(), ), ] However, when I apply the migrations, an error occurs; wangx python3 migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions, testapp Running migrations: Applying contenttypes.0001initial... OK Applying auth.0001initial... OK Applying admin.0001initial... OK Applying admin.0002logentryremoveautoadd... OK Applying admin.0003logentryaddactionflagchoices... OK Applying contenttypes.0002removecontenttypename... OK Applying auth.0002alterpermissionnamemaxlength... OK Applying auth.0003alteruseremailmaxlength... OK Applying auth.0004alteruserusernameopts... OK Applying auth.0005alteruserlastloginnull... OK Applying auth.0006requirecontenttypes0002... OK Applying auth.0007altervalidatorsadderrormessages... OK Applying auth.0008alteruserusernamemaxlength... OK Applying auth.0009alteruserlastnamemaxlength... OK Applying auth.0010altergroupnamemaxlength... OK Applying auth.0011updateproxypermissions... OK Applying sessions.0001initial... OK Applying testapp.0001initial... OK Applying testapp.0002auto202004221247...Traceback (most recent call last): File "/usr/local/lib/python3.6/dist-", line 86, in execute return (sql, params) File "/usr/local/lib/python3.6/dist-", line 74, in execute return (query, args) File "/usr/local/lib/python3.6/dist-", line 209, in execute res = (query) File "/usr/local/lib/python3.6/dist-", line 315, in query (q) File "/usr/local/lib/python3.6/dist-", line 239, in query (self, query) MySQLdb.exceptions.OperationalError: (1061, "Duplicate key name 'testappmymodelnameba5e2bd2uniq'") The above exception was the direct cause of the following exception: Traceback (most recent call last): File "", line 21, in <modulemain() File "", line 17, in main executefromcommandline() File "/usr/local/lib/python3.6/dist-", line 401, in executefromcommandline utility.execute() File "/usr/local/lib/python3.6/dist-", line 395, in execute self.fetchcommand(subcommand).runfromargv() File "/usr/local/lib/python3.6/dist-", line 328, in runfromargv self.execute(args, cmdoptions) File "/usr/local/lib/python3.6/dist-", line 369, in execute output = (args, options) File "/usr/local/lib/python3.6/dist-", line 83, in wrapped res = handlefunc(args, *kwargs) File "/usr/local/lib/python3.6/dist-", line 233, in handle fakeinitial=fakeinitial, File "/usr/local/lib/python3.6/dist-", line 117, in migrate state = self.migrateallforwards(state, plan, fullplan, fake=fake, fakeinitial=fakeinitial) File "/usr/local/lib/python3.6/dist-", line 147, in migrateallforwards state = self.applymigration(state, migration, fake=fake, fakeinitial=fakeinitial) File "/usr/local/lib/python3.6/dist-", line 245, in applymigration state = (state, schemaeditor) File "/usr/local/lib/python3.6/dist-", line 124, in apply operation.databaseforwards(self.applabel, schemaeditor, oldstate, projectstate) File "/usr/local/lib/python3.6/dist-", line 249, in databaseforwards schemaeditor.alterfield(frommodel, fromfield, tofield) File "/usr/local/lib/python3.6/dist-", line 565, in alterfield olddbparams, newdbparams, strict) File "/usr/local/lib/python3.6/dist-", line 745, in alterfield self.execute(self.createuniquesql(model, [])) File "/usr/local/lib/python3.6/dist-", line 142, in execute cursor.execute(sql, params) File "/usr/local/lib/python3.6/dist-", line 100, in execute return super().execute(sql, params) File "/usr/local/lib/python3.6/dist-", line 68, in execute return self.executewithwrappers(sql, params, many=False, executor=self.execute) File "/usr/local/lib/python3.6/dist-", line 77, in executewithwrappers return executor(sql, params, many, context) File "/usr/local/lib/python3.6/dist-", line 86, in execute return (sql, params) File "/usr/local/lib/python3.6/dist-", line 90, in exit raise djexcvalue.withtraceback(traceback) from excvalue File "/usr/local/lib/python3.6/dist-", line 86, in execute return (sql, params) File "/usr/local/lib/python3.6/dist-", line 74, in execute return (query, args) File "/usr/local/lib/python3.6/dist-", line 209, in execute res = (query) File "/usr/local/lib/python3.6/dist-", line 315, in query (q) File "/usr/local/lib/python3.6/dist-", line 239, in query (self, query) (1061, "Duplicate key name 'testappmymodelnameba5e2bd2uniq'") I check the sql for these migrations, it shows: wangx python3 sqlmigrate testapp 0001 -- Create model MyModel CREATE TABLE ( integer AUTOINCREMENT NOT NULL PRIMARY KEY, varchar(32) NOT NULL); ALTER TABLE ADD CONSTRAINT UNIQUE (); wangx python3 sqlmigrate testapp 0002 -- Alter field name on mymodel ALTER TABLE ADD CONSTRAINT UNIQUE (); -- Alter uniquetogether for mymodel (0 constraint(s)) ALTER TABLE DROP INDEX ; it looks like django will first create the index for unique=True second drop the index for uniquetogether=('name', ) but the program for creating index name generates the same index name :testappmymodelnameba5e2bd2uniq, so when django create the same index, Duplicate key name error occurs.

dependencies.append((through_app_label, through_object_name, None, True))         return dependencies     def _generate_altered_foo_together(self, operation):        option_name = operation.option_name    def _get_altered_foo_together_operations(self, option_name):         for app_label, model_name in sorted(self.kept_model_keys):             old_model_name = self.renamed_models.get((app_label, model_name), model_name)             old_model_state = self.from_state.models[app_label, old_model_name]


Question: Moving a unique constraint from uniquetogether to generate an invalid migration. Description You can see a demo example to show the bug at . I met a problem when I convert a uniquetogether to the unique=True attribute. first commit, everything is ok I create a model file. // class MyModel(models.Model): name = models.CharField(maxlength=32) class Meta: uniquetogether = ("name", ) the migrations file looks like this. // from import migrations, models class Migration(migrations.Migration): dependencies = [ ('testapp', '0001initial'), ] operations = [ migrations.AlterField( modelname='mymodel', name='name', field=models.CharField(maxlength=32, unique=True), ), migrations.AlterUniqueTogether( name='mymodel', uniquetogether=set(), ), ] second commit: then I remove the uniquetogether and add unique=True to the field model file class MyModel(models.Model): name = models.CharField(maxlength=32, unique=True) class Meta: pass # uniquetogether = ("name", ) 0002 migrations file class Migration(migrations.Migration): dependencies = [ ('testapp', '0001initial'), ] operations = [ migrations.AlterField( modelname='mymodel', name='name', field=models.CharField(maxlength=32, unique=True), ), migrations.AlterUniqueTogether( name='mymodel', uniquetogether=set(), ), ] However, when I apply the migrations, an error occurs; wangx python3 migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions, testapp Running migrations: Applying contenttypes.0001initial... OK Applying auth.0001initial... OK Applying admin.0001initial... OK Applying admin.0002logentryremoveautoadd... OK Applying admin.0003logentryaddactionflagchoices... OK Applying contenttypes.0002removecontenttypename... OK Applying auth.0002alterpermissionnamemaxlength... OK Applying auth.0003alteruseremailmaxlength... OK Applying auth.0004alteruserusernameopts... OK Applying auth.0005alteruserlastloginnull... OK Applying auth.0006requirecontenttypes0002... OK Applying auth.0007altervalidatorsadderrormessages... OK Applying auth.0008alteruserusernamemaxlength... OK Applying auth.0009alteruserlastnamemaxlength... OK Applying auth.0010altergroupnamemaxlength... OK Applying auth.0011updateproxypermissions... OK Applying sessions.0001initial... OK Applying testapp.0001initial... OK Applying testapp.0002auto202004221247...Traceback (most recent call last): File "/usr/local/lib/python3.6/dist-", line 86, in execute return (sql, params) File "/usr/local/lib/python3.6/dist-", line 74, in execute return (query, args) File "/usr/local/lib/python3.6/dist-", line 209, in execute res = (query) File "/usr/local/lib/python3.6/dist-", line 315, in query (q) File "/usr/local/lib/python3.6/dist-", line 239, in query (self, query) MySQLdb.exceptions.OperationalError: (1061, "Duplicate key name 'testappmymodelnameba5e2bd2uniq'") The above exception was the direct cause of the following exception: Traceback (most recent call last): File "", line 21, in <modulemain() File "", line 17, in main executefromcommandline() File "/usr/local/lib/python3.6/dist-", line 401, in executefromcommandline utility.execute() File "/usr/local/lib/python3.6/dist-", line 395, in execute self.fetchcommand(subcommand).runfromargv() File "/usr/local/lib/python3.6/dist-", line 328, in runfromargv self.execute(args, cmdoptions) File "/usr/local/lib/python3.6/dist-", line 369, in execute output = (args, options) File "/usr/local/lib/python3.6/dist-", line 83, in wrapped res = handlefunc(args, *kwargs) File "/usr/local/lib/python3.6/dist-", line 233, in handle fakeinitial=fakeinitial, File "/usr/local/lib/python3.6/dist-", line 117, in migrate state = self.migrateallforwards(state, plan, fullplan, fake=fake, fakeinitial=fakeinitial) File "/usr/local/lib/python3.6/dist-", line 147, in migrateallforwards state = self.applymigration(state, migration, fake=fake, fakeinitial=fakeinitial) File "/usr/local/lib/python3.6/dist-", line 245, in applymigration state = (state, schemaeditor) File "/usr/local/lib/python3.6/dist-", line 124, in apply operation.databaseforwards(self.applabel, schemaeditor, oldstate, projectstate) File "/usr/local/lib/python3.6/dist-", line 249, in databaseforwards schemaeditor.alterfield(frommodel, fromfield, tofield) File "/usr/local/lib/python3.6/dist-", line 565, in alterfield olddbparams, newdbparams, strict) File "/usr/local/lib/python3.6/dist-", line 745, in alterfield self.execute(self.createuniquesql(model, [])) File "/usr/local/lib/python3.6/dist-", line 142, in execute cursor.execute(sql, params) File "/usr/local/lib/python3.6/dist-", line 100, in execute return super().execute(sql, params) File "/usr/local/lib/python3.6/dist-", line 68, in execute return self.executewithwrappers(sql, params, many=False, executor=self.execute) File "/usr/local/lib/python3.6/dist-", line 77, in executewithwrappers return executor(sql, params, many, context) File "/usr/local/lib/python3.6/dist-", line 86, in execute return (sql, params) File "/usr/local/lib/python3.6/dist-", line 90, in exit raise djexcvalue.withtraceback(traceback) from excvalue File "/usr/local/lib/python3.6/dist-", line 86, in execute return (sql, params) File "/usr/local/lib/python3.6/dist-", line 74, in execute return (query, args) File "/usr/local/lib/python3.6/dist-", line 209, in execute res = (query) File "/usr/local/lib/python3.6/dist-", line 315, in query (q) File "/usr/local/lib/python3.6/dist-", line 239, in query (self, query) (1061, "Duplicate key name 'testappmymodelnameba5e2bd2uniq'") I check the sql for these migrations, it shows: wangx python3 sqlmigrate testapp 0001 -- Create model MyModel CREATE TABLE ( integer AUTOINCREMENT NOT NULL PRIMARY KEY, varchar(32) NOT NULL); ALTER TABLE ADD CONSTRAINT UNIQUE (); wangx python3 sqlmigrate testapp 0002 -- Alter field name on mymodel ALTER TABLE ADD CONSTRAINT UNIQUE (); -- Alter uniquetogether for mymodel (0 constraint(s)) ALTER TABLE DROP INDEX ; it looks like django will first create the index for unique=True second drop the index for uniquetogether=('name', ) but the program for creating index name generates the same index name :testappmymodelnameba5e2bd2uniq, so when django create the same index, Duplicate key name error occurs.

dependencies.extend(self._get_dependencies_for_foreign_key(                                 app_label, model_name, field, self.to_state,                             ))                yield (                    old_value,                    new_value,                    app_label,                    model_name,                    dependencies,                )     def _generate_removed_altered_foo_together(self, operation):        for (            old_value,            new_value,            app_label,            model_name,            dependencies,        ) in self._get_altered_foo_together_operations(operation.option_name):            removal_value = new_value.intersection(old_value)            if removal_value or old_value:                 self.add_operation(                     app_label,                    operation(                        name=model_name,                        **{option_name: new_value}                    ),                    operation(name=model_name, **{operation.option_name: removal_value}),                    dependencies=dependencies,                )    def generate_removed_altered_unique_together(self):        self._generate_removed_altered_foo_together(operations.AlterUniqueTogether)    def generate_removed_altered_index_together(self):        self._generate_removed_altered_foo_together(operations.AlterIndexTogether)    def _generate_altered_foo_together(self, operation):        for (            old_value,            new_value,            app_label,            model_name,            dependencies,        ) in self._get_altered_foo_together_operations(operation.option_name):            removal_value = new_value.intersection(old_value)            if new_value != removal_value:                self.add_operation(                    app_label,                    operation(name=model_name, **{operation.option_name: new_value}),                     dependencies=dependencies,                 )


Question: Provide a way for model formsets to disallow new object creation Description Model formsets don't provide a way to create an "edit only" view of objects. We see users trying to use extra=0 to accomplish this, but that's not reliable as extra is merely meant for the extra number of forms to display. You can add more forms with Javascript (or just send additional post data).

for form in self.saved_forms:                     form.save_m2m()             self.save_m2m = save_m2m        return self.save_existing_objects(commit) + self.save_new_objects(commit)        if self.edit_only:            return self.save_existing_objects(commit)        else:            return self.save_existing_objects(commit) + self.save_new_objects(commit)      save.alters_data = True


Question: Provide a way for model formsets to disallow new object creation Description Model formsets don't provide a way to create an "edit only" view of objects. We see users trying to use extra=0 to accomplish this, but that's not reliable as extra is merely meant for the extra number of forms to display. You can add more forms with Javascript (or just send additional post data).

widgets=None, validate_max=False, localized_fields=None,                          labels=None, help_texts=None, error_messages=None,                          min_num=None, validate_min=False, field_classes=None,                         absolute_max=None, can_delete_extra=True, renderer=None):                         absolute_max=None, can_delete_extra=True, renderer=None,                         edit_only=False):     """Return a FormSet class for the given Django model class."""     meta = getattr(form, 'Meta', None)     if (getattr(meta, 'fields', fields) is None and


Question: Provide a way for model formsets to disallow new object creation Description Model formsets don't provide a way to create an "edit only" view of objects. We see users trying to use extra=0 to accomplish this, but that's not reliable as extra is merely meant for the extra number of forms to display. You can add more forms with Javascript (or just send additional post data).

absolute_max=absolute_max, can_delete_extra=can_delete_extra,                               renderer=renderer)     FormSet.model = model<ins>    FormSet.edit_only = edit_only</ins>     return FormSet


Question: Provide a way for model formsets to disallow new object creation Description Model formsets don't provide a way to create an "edit only" view of objects. We see users trying to use extra=0 to accomplish this, but that's not reliable as extra is merely meant for the extra number of forms to display. You can add more forms with Javascript (or just send additional post data).

widgets=None, validate_max=False, localized_fields=None,                           labels=None, help_texts=None, error_messages=None,                           min_num=None, validate_min=False, field_classes=None,                          absolute_max=None, can_delete_extra=True, renderer=None):                          absolute_max=None, can_delete_extra=True, renderer=None,                          edit_only=False):     """     Return an ``InlineFormSet`` for the given kwargs.


Question: Provide a way for model formsets to disallow new object creation Description Model formsets don't provide a way to create an "edit only" view of objects. We see users trying to use extra=0 to accomplish this, but that's not reliable as extra is merely meant for the extra number of forms to display. You can add more forms with Javascript (or just send additional post data).

'absolute_max': absolute_max,         'can_delete_extra': can_delete_extra,         'renderer': renderer,<ins>        'edit_only': edit_only,</ins>     }     FormSet = modelformset_factory(model, **kwargs)     FormSet.fk = fk


Question: Naming an incompletely applied squashed migration as a migration target fails with bare NodeNotFoundError Description In Line 205-208 in replacement migrations (created with squash) are checked if they can be applied. If any of the to be replaced migrations isn't already applied the replacement migration is not to the nodes list. This leads to the fact that if some of the migrations are removed or not completely applied before the squash is and there is a dependency on the replacement migration, the user gets a 'NodeNotFoundError' where the replacement migration that is not being applied because of line 206 is the missing one. This is very confusing to the user, raising a warning in line 208 would inform the user that the squashed migration can not be applied because not all the 'child' migrations are applied. Had to debug into that to figure that out.

except KeyError:                     raise CommandError("Cannot find a migration matching '%s' from app '%s'." % (                         migration_name, app_label))                targets = [(app_label, migration.name)]                target = (app_label, migration.name)                # Partially applied squashed migrations are not included in the                # graph, use the last replacement instead.                if (                    target not in executor.loader.graph.nodes and                    target in executor.loader.replacements                ):                    incomplete_migration = executor.loader.replacements[target]                    target = incomplete_migration.replaces[-1]                targets = [target]             target_app_labels_only = False         elif options['app_label']:             targets = [key for key in executor.loader.graph.leaf_nodes() if key[0] == app_label]diff --git a/django/core/management/commands/sqlmigrate.py b/django/core/management/commands/sqlmigrate.py-- a/django/core/management/commands/sqlmigrate.py++ b/django/core/management/commands/sqlmigrate.py


Question: Naming an incompletely applied squashed migration as a migration target fails with bare NodeNotFoundError Description In Line 205-208 in replacement migrations (created with squash) are checked if they can be applied. If any of the to be replaced migrations isn't already applied the replacement migration is not to the nodes list. This leads to the fact that if some of the migrations are removed or not completely applied before the squash is and there is a dependency on the replacement migration, the user gets a 'NodeNotFoundError' where the replacement migration that is not being applied because of line 206 is the missing one. This is very confusing to the user, raising a warning in line 208 would inform the user that the squashed migration can not be applied because not all the 'child' migrations are applied. Had to debug into that to figure that out.

# Get the database we're operating from         connection = connections[options['database']]         # Load up an loader to get all the migration data, but don't replace        # Load up a loader to get all the migration data, but don't replace         # migrations.         loader = MigrationLoader(connection, replace_migrations=False) diff --git a/django/db/migrations/loader.py b/django/db/migrations/loader.py-- a/django/db/migrations/loader.py++ b/django/db/migrations/loader.py


Question: Naming an incompletely applied squashed migration as a migration target fails with bare NodeNotFoundError Description In Line 205-208 in replacement migrations (created with squash) are checked if they can be applied. If any of the to be replaced migrations isn't already applied the replacement migration is not to the nodes list. This leads to the fact that if some of the migrations are removed or not completely applied before the squash is and there is a dependency on the replacement migration, the user gets a 'NodeNotFoundError' where the replacement migration that is not being applied because of line 206 is the missing one. This is very confusing to the user, raising a warning in line 208 would inform the user that the squashed migration can not be applied because not all the 'child' migrations are applied. Had to debug into that to figure that out.

"There is more than one migration for '%s' with the prefix '%s'" % (app_label, name_prefix)             )         elif not results:            raise KeyError("There no migrations for '%s' with the prefix '%s'" % (app_label, name_prefix))            raise KeyError(                f"There is no migration for '{app_label}' with the prefix "                f"'{name_prefix}'"            )         else:             return self.disk_migrations[results[0]]


Question: Prevent developers from defining a relatedname on symmetrical ManyToManyFields Description In ManyToManyField, if the symmetrical argument is passed, or if it's a self-referential ManyToMany relationship, the related field on the target model is not created. However, if a developer passes in the relatedname not understanding this fact, they may be confused until they find the information about symmetrical relationship. Thus, it is proposed to raise an error when the user defines a ManyToManyField in this condition.

)             ) <ins>        if self.remote_field.symmetrical and self._related_name:            warnings.append(                checks.Warning(                    'related_name has no effect on ManyToManyField '                    'with a symmetrical relationship, e.g. to "self".',                    obj=self,                    id='fields.W345',                )            )</ins>         return warnings      def _check_relationship_model(self, from_model=None, **kwargs):


Question: Allow overriding of deletion widget in formsets Description In Django 3.0 orderingwidget and getorderingwidget() were introduced (see ). The typical use case was to easily override the ORDER field in formsets that are updated in the frontend. For the exact same use case, I'd find it useful to see deletionwidget and getdeletionwidget() getting introduced. Discussion initiated here for this feature.

from django.forms import Form from django.forms.fields import BooleanField, IntegerField from django.forms.utils import ErrorListfrom django.forms.widgets import HiddenInput, NumberInputfrom django.forms.widgets import CheckboxInput, HiddenInput, NumberInput from django.utils.functional import cached_property from django.utils.html import html_safe from django.utils.safestring import mark_safe


Question: Allow overriding of deletion widget in formsets Description In Django 3.0 orderingwidget and getorderingwidget() were introduced (see ). The typical use case was to easily override the ORDER field in formsets that are updated in the frontend. For the exact same use case, I'd find it useful to see deletionwidget and getdeletionwidget() getting introduced. Discussion initiated here for this feature.

"""     A collection of instances of the same Form class.     """<ins>    deletion_widget = CheckboxInput</ins>     ordering_widget = NumberInput     default_error_messages = {         'missing_management_form': _(


Question: Allow overriding of deletion widget in formsets Description In Django 3.0 orderingwidget and getorderingwidget() were introduced (see ). The typical use case was to easily override the ORDER field in formsets that are updated in the frontend. For the exact same use case, I'd find it useful to see deletionwidget and getdeletionwidget() getting introduced. Discussion initiated here for this feature.

def get_default_prefix(cls):         return 'form' <ins>    @classmethod    def get_deletion_widget(cls):        return cls.deletion_widget</ins>     @classmethod     def get_ordering_widget(cls):         return cls.ordering_widget


Question: Allow overriding of deletion widget in formsets Description In Django 3.0 orderingwidget and getorderingwidget() were introduced (see ). The typical use case was to easily override the ORDER field in formsets that are updated in the frontend. For the exact same use case, I'd find it useful to see deletionwidget and getdeletionwidget() getting introduced. Discussion initiated here for this feature.

widget=self.get_ordering_widget(),                 )         if self.can_delete and (self.can_delete_extra or index < initial_form_count):            form.fields[DELETION_FIELD_NAME] = BooleanField(label=_('Delete'), required=False)            form.fields[DELETION_FIELD_NAME] = BooleanField(                label=_('Delete'),                required=False,                widget=self.get_deletion_widget(),            )      def add_prefix(self, index):         return '%s-%s' % (self.prefix, index)