Opened 14 years ago

Closed 9 years ago

#13223 closed Bug (duplicate)

ValueError with inline and save as new

Reported by: Luca Lenardi Owned by: Igor Sobreira
Component: contrib.admin Version: 1.1
Severity: Normal Keywords: admin, save-as-new
Cc: kevin1024, andrew@…, igor@…, samstudio8@… Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description

Given two models that are related to each other with a foreign key, as follows:

class Post(models.Model):
    title = models.CharField()
    slug = models.CharField(max_length=200, unique = True)

class Block(models.Model):
    post = models.ForeignKey(Post)
    slug = models.CharField(max_length=200, unique = True)

Suppose you have set save_as = true in the admin.py for the class PostAdmin. When you try to create a new Post, generated from an existing one with the "save as new" button after correcting some validation errors, the following exception is being generated:
ValueError: invalid literal for int() with base 10: ''

If you take a look at the request POST parameters, you could find some empty values for the block_set-N-post block_set-N-id (FK and id).

To replicate the error, follow this sequence:

  1. Create a new object with at least one inline
  2. Save it as new with some validation errors
  3. Try to save it again, after correcting the errors of the form

Attachments (6)

13223.diff (4.8 KB ) - added by kevin1024 13 years ago.
13223.v2.diff (4.9 KB ) - added by kevin1024 13 years ago.
13223.templatefix.diff (5.0 KB ) - added by Preston Timmons 13 years ago.
Patch that modifies add_view to maintain the _saveasnew parameter
13223-1.diff (6.1 KB ) - added by Ramiro Morales 13 years ago.
Patch using the fix strategy proposed by contributor prestontimmons with enhanced tests
13223-2.diff (6.8 KB ) - added by Ramiro Morales 12 years ago.
Patch updated to current trunk status, still needs work
13223.newview-prototype.diff (8.9 KB ) - added by Igor Sobreira 12 years ago.
Prototype for an intermediate clone/ view

Download all attachments as: .zip

Change History (32)

comment:1 by Karen Tracey, 14 years ago

Triage Stage: UnreviewedAccepted

#10448 was an earlier ticket in the same area, but it does not seem to have addressed the inline issue.

comment:2 by Luca Lenardi, 14 years ago

The error appears to be related with the value of save_as_new in options.py around line 730.

When the form is submitted the first time, it is set to True, while the second time, after the validation errors, it is set to False since it actually creates a new object. Forcing the value to True, the object is being saved without exceptions (as expected).

comment:3 by gptvnt, 14 years ago

Owner: changed from nobody to gptvnt
Status: newassigned

comment:4 by paulos, 13 years ago

If inline formset contains an image or file field, form will always fail to validate on save_as (#14760).

in reply to:  3 comment:5 by paulos, 13 years ago

Replying to gptvnt:

I think the most reliable way to correct this is creating a new admin URL like:

{{app_name}}/{{model_name}}/{{id}}/save_as/

or

{{app_name}}/{{model_name}}/clone/{{id}}/

comment:6 by Shu Zong Chen, 13 years ago

Unless a new admin URL is created, the only way to get the current add_view form to behave is to replace the values for INITIAL_FORMS and TOTAL_FORMS, as well as the fk_field value. The INITIAL_FORMS is the cause of the ValueError of the ticket (setting it to 0 makes things happy), and the TOTAL_FORMS/fk_field mess with the form if you try to add any new inlines while you are fixing the validation errors. The TOTAL_FORMS needs to be set to the old INITIAL_FORMS value, and the fk_field needs to be blank.

Last edited 13 years ago by Shu Zong Chen (previous) (diff)

comment:7 by Luke Plant, 13 years ago

Type: Bug

comment:8 by Luke Plant, 13 years ago

Severity: Normal

by kevin1024, 13 years ago

Attachment: 13223.diff added

comment:9 by kevin1024, 13 years ago

Cc: kevin1024 added
Easy pickings: unset
Has patch: set

OK, I spent some time hacking on this today. Here is a patch. Be gentle; it's my first contribution to Django. :)

comment:10 by Preston Timmons, 13 years ago

Patch needs improvement: set

Hi Kevin,

Thanks for putting a patch together. I tested it out. It applies cleanly to revision 16181 and seems to fix the issue here, but the changes to django/forms/formsets.py introduce problems into the model formsets:

ERROR: test_add_form_deletion_when_invalid (modeltests.model_formsets.tests.DeletionTests)
    ...
AttributeError: 'PoetFormFormSet' object has no attribute 'save_as_new'

I'm marking this one as needing improvement.

comment:11 by anonymous, 13 years ago

Patch needs improvement: unset

OK, let's try this again.

by kevin1024, 13 years ago

Attachment: 13223.v2.diff added

comment:12 by Preston Timmons, 13 years ago

Thanks, Kevin. I verified your new patch fixes the problem.

I'm hesitant to mark it ready for check-in. The "save_as_new" property is really a part of BaseInlineFormSet. Adding a check for it in BaseFormSet seems wrong. It might be more appropriate to put this fix in a custom "_management_form" method on the BaseInlineFormSet instead.

I'm also attaching a separate patch. Instead of modifying the formset classes it modifies the add_view method to post the value "_saveasnew" on subsequent requests, even when form errors appear. When the form is redisplayed the admin switches from the change_view to the add_view and loses this piece of information in the process.

I'm not sure what the best solution is.

by Preston Timmons, 13 years ago

Attachment: 13223.templatefix.diff added

Patch that modifies add_view to maintain the _saveasnew parameter

comment:13 by Preston Timmons, 13 years ago

After some more testing of 13223.v2.diff, I found one problem with it. Since it doesn't bind posted data to the management form, when the form is redisplayed it loses inline data if you add more blocks than the initial form count at the same time you use "save as new".

The newer patch that maintains "_saveasnew" in the template doesn't suffer from that problem.

comment:14 by candlerb, 13 years ago

UI/UX: unset

I have a related problem:

  • model admin has save_as = True
  • browse to existing model, click "Save as new"
  • the model fails to validate (e.g. because of a failed uniqueness constraint)
  • when the form is redisplayed with the error, I see "Save and add another" instead of "Save as new"

This behaviour still remains after applying 13223.templatefix.diff (onto released django-1.3), although I've not tried any of the other patches in this thread.

(Platform: python 2.6.5, Ubuntu 10.04.2 LTS)

comment:15 by Andrew Macgregor <andrew@…>, 13 years ago

Cc: andrew@… added

in reply to:  14 comment:16 by Ramiro Morales, 13 years ago

Replying to candlerb:

I have a related problem:

  • model admin has save_as = True
  • browse to existing model, click "Save as new"
  • the model fails to validate (e.g. because of a failed uniqueness constraint)
  • when the form is redisplayed with the error, I see "Save and add another" instead of "Save as new"

This behaviour still remains after applying 13223.templatefix.diff (onto released django-1.3), although I've not tried any of the other patches in this thread.

This would be #5681 -- In particular, look at this comment for some discussion about why that behavior isn't wrong IMHO.

comment:17 by Ramiro Morales, 13 years ago

Owner: gptvnt removed
Status: assignednew

I've updated the patch contributed by prestontimmons with a more complete test case that simulates a complete workflow as originally reported.

by Ramiro Morales, 13 years ago

Attachment: 13223-1.diff added

Patch using the fix strategy proposed by contributor prestontimmons with enhanced tests

by Ramiro Morales, 12 years ago

Attachment: 13223-2.diff added

Patch updated to current trunk status, still needs work

comment:18 by Ramiro Morales, 12 years ago

Patch needs improvement: set

I've attached an updated version of the patch that simply allows it to apply cleanly in trunk.

But the OP in ticket #17417 (slightly related to this one) makes a good point regarding the current buggy behavior that isn't fixed by this patch:

If you press "Save as New" without changing the slug field a ValidationError? is raised, and the form is displayed to the user with a warning (as it should). However, the url is that of the old entity ...

That means that at that point the user is presented with the admin add view/form for a new instance of the model, pre-filled with values of the original instance (the one whose edit view/form she was looking at when she decided to press the Save as new button) but the URL in the browser address bar hasn't changed.

IMHO this is semantically incorrect and puts comment:5 in a new perspective. Perhaps we need to implement an intermediate view as suggested there?

Last edited 12 years ago by Ramiro Morales (previous) (diff)

comment:19 by Igor Sobreira, 12 years ago

Owner: set to Igor Sobreira
Status: newassigned

I was wondering how this intermediate view could be implemented.

The idea is to replace the "Save as New" button for a link to a clone/ view. This view would present a pre-filled form with the data from the original object you'd like to clone. It's (very) similar to add_view , but using initial to fill the form based on the original object.
The formsets are also pre-filled using initial .

All of this happens on GET. POSTing is the same as add_view's POST.

This is the same to call add_view passing initial values as GET parameters. But I think a clone/ url is much cleaner.

Let me know if I'm on the right track so I can continue with the implementation. It's ugly with lot's of duplication and incomplete at this moment, the patch is just a prototype. But I need some feedback before moving on...

Version 0, edited 12 years ago by Igor Sobreira (next)

by Igor Sobreira, 12 years ago

Prototype for an intermediate clone/ view

comment:20 by Igor Sobreira, 12 years ago

Cc: igor@… added

comment:21 by Igor Sobreira, 12 years ago

I'm pushing small commits to my git fork: https://github.com/igorsobreira/django/commits/master (if anyone is interesting in follow or review step-by-step).

When I'm done I'll attach the full patch here.

comment:22 by anonymous, 11 years ago

Did anyone else pick this up? The github link above has disappeared to try and pick up his line of work.

comment:23 by dakinsloss, 11 years ago

Did anyone else pick this up? The github link above has disappeared to try and pick up his line of work.

comment:24 by Igor Sobreira, 11 years ago

Sorry for the broken link @dakinsloss. Since the review/feedback was running to slow I ended up hacking it as an external app.

https://github.com/RealGeeks/django-modelclone

It's basically the intermediate 'clone' view, same form as 'add_view' but already pre-populated.

comment:25 by samstudio8@…, 11 years ago

Cc: samstudio8@… added

comment:26 by Tim Graham, 9 years ago

Resolution: duplicate
Status: assignedclosed

Duplicate of #23387 which has been fixed in master (1.9) by only showing the "Save as new" button if it's selected and there are validation errors.

Note: See TracTickets for help on using tickets.
Back to Top