Opened 10 years ago
Last modified 10 years ago
#22689 new Bug
inlines with sliced and ordered querysets save incorrectly when underlying data has changed since page load
Reported by: | Justin Hill | Owned by: | nobody |
---|---|---|---|
Component: | contrib.admin | Version: | 1.7 |
Severity: | Normal | Keywords: | admin inline |
Cc: | Triage Stage: | Accepted | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Our Client model's Change page has an tabular inline for the client's Financial Transactions. We use a custom formset to limit the transactions to the most recent 5 and to order them from newest to oldest.
class FinancialTransactionInlineFormset(BaseInlineFormSet): def get_queryset(self): qs = super(FinancialTransactionInlineFormset, self).get_queryset() return qs.order_by('-id')[:5]
If while you have the Change Client page open, someone else adds a new transaction, when you do the same, you'll find that while yours was added, some of the other transactions have changed. The history page reports that you were the one that changed them. In actuality, all you did was use the blank row at the bottom to add a new one.
Changed transaction_type for financial transaction "client@company.net | 123 Changed transaction_type for financial transaction "client@company.net | 456 Changed transaction_type for financial transaction "client@company.net | 789
This is what I experienced in django 1.7b4.
It seems like django is having trouble deciding which inline forms have changed and which ones are new
In django 1.4.5 it throws an error because
obj = self._existing_object(pk_value)
in BaseModelFormset is setting obj to None
def save_existing_objects(self, commit=True): self.changed_objects = [] self.deleted_objects = [] if not self.initial_forms: return [] saved_instances = [] for form in self.initial_forms: pk_name = self._pk_field.name raw_pk_value = form._raw_value(pk_name) # clean() for different types of PK fields can sometimes return # the model instance, and sometimes the PK. Handle either. pk_value = form.fields[pk_name].clean(raw_pk_value) pk_value = getattr(pk_value, 'pk', pk_value) obj = self._existing_object(pk_value) if self.can_delete and self._should_delete_form(form): self.deleted_objects.append(obj) obj.delete() continue if form.has_changed(): self.changed_objects.append((obj, form.changed_data)) saved_instances.append(self.save_existing(form, obj, commit=commit)) if not commit: self.saved_forms.append(form) return saved_instances
Traceback (most recent call last): File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py", line 111, in get_response response = callback(request, *callback_args, **callback_kwargs) File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/options.py", line 366, in wrapper return self.admin_site.admin_view(view)(*args, **kwargs) File "/usr/local/lib/python2.7/dist-packages/django/utils/decorators.py", line 91, in _wrapped_view response = view_func(request, *args, **kwargs) File "/usr/local/lib/python2.7/dist-packages/django/views/decorators/cache.py", line 89, in _wrapped_view_func response = view_func(request, *args, **kwargs) File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/sites.py", line 196, in inner return view(request, *args, **kwargs) File "/usr/local/lib/python2.7/dist-packages/django/utils/decorators.py", line 25, in _wrapper return bound_func(*args, **kwargs) File "/usr/local/lib/python2.7/dist-packages/django/utils/decorators.py", line 91, in _wrapped_view response = view_func(request, *args, **kwargs) File "/usr/local/lib/python2.7/dist-packages/django/utils/decorators.py", line 21, in bound_func return func(self, *args2, **kwargs2) File "/usr/local/lib/python2.7/dist-packages/django/db/transaction.py", line 224, in inner return func(*args, **kwargs) File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/options.py", line 1056, in change_view change_message = self.construct_change_message(request, form, formsets) File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/options.py", line 682, in construct_change_message 'name': force_unicode(changed_object._meta.verbose_name), AttributeError: 'NoneType' object has no attribute '_meta'
Change History (3)
follow-up: 2 comment:1 by , 10 years ago
Resolution: | → duplicate |
---|---|
Status: | new → closed |
comment:2 by , 10 years ago
Resolution: | duplicate |
---|---|
Status: | closed → new |
Version: | 1.7-beta-2 → 1.7 |
Replying to timo:
I think it's a probably a duplicate of #15574.
Why do you think this is a duplicate of #15574?
I am experiencing exactly the same behavior as the OP reports with 1.7.4 (inlines with slicing on the formset's queryset). In addition, in my case, although the admin logs say that the objects were changed, in fact, they were (re-)added as new (same values, different ids)! It seems that something really bad is happening...
comment:3 by , 10 years ago
Triage Stage: | Unreviewed → Accepted |
---|
I meant that I think solving #15574 would fix the issue. But sure, we can leave this ticket open.
I think it's a probably a duplicate of #15574.