Django

Code

Ticket #12521 (closed: fixed)

Opened 8 months ago

Last modified 8 months ago

ModelAdmin save_model request.user idiom is broken by model validation

Reported by: simon Assigned to: jkocherhans
Milestone: 1.2 Component: django.contrib.admin
Version: SVN Keywords:
Cc: em@kth.se, jezdez, knutin@gmail.com, zbyte64@gmail.com, Maniac@SoftwareManiacs.Org, aribao@gmail.com Triage Stage: Design decision needed
Has patch: 1 Needs documentation: 0
Needs tests: 1 Patch needs improvement: 0

Description

The common (documented) pattern for automatically setting the "author" field on an object to request.user by over-riding the save_model method on a ModelAdmin? subclass breaks with the new model validation code. Here are the example models.py and admin.py files:

from django.db import models

class Entry(models.Model):
    title = models.CharField(max_length = 255)
    body = models.TextField()
    published = models.DateTimeField(auto_now_add = True)
    author = models.ForeignKey('auth.User')
    
    def __unicode__(self):
        return self.title

admin.py:

from models import Entry
from django.contrib import admin

class EntryAdmin(admin.ModelAdmin):
    list_display = ('title', 'published', 'author')
    exclude = ('author',)
    
    def save_model(self, request, obj, form, change):
        obj.author = request.user
        obj.save()

admin.site.register(Entry, EntryAdmin)

And here's the exception:

Traceback:
File "/Users/simon/Development/playing-with-django-1.2/ve/lib/python2.6/site-packages/django/core/handlers/base.py" in get_response
  99.                     response = callback(request, *callback_args, **callback_kwargs)
File "/Users/simon/Development/playing-with-django-1.2/ve/lib/python2.6/site-packages/django/contrib/admin/options.py" in wrapper
  237.                 return self.admin_site.admin_view(view)(*args, **kwargs)
File "/Users/simon/Development/playing-with-django-1.2/ve/lib/python2.6/site-packages/django/utils/decorators.py" in __call__
  36.         return self.decorator(self.func)(*args, **kwargs)
File "/Users/simon/Development/playing-with-django-1.2/ve/lib/python2.6/site-packages/django/utils/decorators.py" in _wrapped_view
  86.                     response = view_func(request, *args, **kwargs)
File "/Users/simon/Development/playing-with-django-1.2/ve/lib/python2.6/site-packages/django/utils/decorators.py" in __call__
  36.         return self.decorator(self.func)(*args, **kwargs)
File "/Users/simon/Development/playing-with-django-1.2/ve/lib/python2.6/site-packages/django/views/decorators/cache.py" in _wrapped_view_func
  70.         response = view_func(request, *args, **kwargs)
File "/Users/simon/Development/playing-with-django-1.2/ve/lib/python2.6/site-packages/django/contrib/admin/sites.py" in inner
  187.             return view(request, *args, **kwargs)
File "/Users/simon/Development/playing-with-django-1.2/ve/lib/python2.6/site-packages/django/utils/decorators.py" in _wrapped_view
  86.                     response = view_func(request, *args, **kwargs)
File "/Users/simon/Development/playing-with-django-1.2/ve/lib/python2.6/site-packages/django/db/transaction.py" in _commit_on_success
  295.                     res = func(*args, **kw)
File "/Users/simon/Development/playing-with-django-1.2/ve/lib/python2.6/site-packages/django/contrib/admin/options.py" in add_view
  760.             if form.is_valid():
File "/Users/simon/Development/playing-with-django-1.2/ve/lib/python2.6/site-packages/django/forms/forms.py" in is_valid
  120.         return self.is_bound and not bool(self.errors)
File "/Users/simon/Development/playing-with-django-1.2/ve/lib/python2.6/site-packages/django/forms/forms.py" in _get_errors
  111.             self.full_clean()
File "/Users/simon/Development/playing-with-django-1.2/ve/lib/python2.6/site-packages/django/forms/forms.py" in full_clean
  286.             self.cleaned_data = self.clean()
File "/Users/simon/Development/playing-with-django-1.2/ve/lib/python2.6/site-packages/django/forms/models.py" in clean
  270.                 raise UnresolvableValidationError(e.message_dict)

Exception Type: UnresolvableValidationError at /admin/blog/entry/add/
Exception Value: 

The validation error is "This field cannot be null".

This pattern is documented here: http://docs.djangoproject.com/en/dev/ref/contrib/admin/#modeladmin-methods - we should at the very least update the documentation, but even having done that we'd still break existing code.

Attachments

only_validate_fields_on_modelform_not_entire_model.diff (6.6 kB) - added by Honza_Kral on 01/08/10 11:28:26.
Simple quick patch implementing desired behavior, just a case study, still needs work
12521.tests.diff (7.0 kB) - added by isagalaev on 01/09/10 12:06:54.
Correct tests failing on current trunk
12521.diff (18.9 kB) - added by jkocherhans on 01/09/10 13:42:18.
This combines my fixes, Honza fixes, and Ivan's tests. Almost there.
12521.2.diff (24.4 kB) - added by jkocherhans on 01/09/10 16:40:50.
Similar to the last patch, but splits full_validate into validate_fields, validate_unique, validate.
12521.3.diff (27.5 kB) - added by jkocherhans on 01/10/10 09:48:47.
Now with updated docs for model's validation methods.
12521.4.diff (42.1 kB) - added by jkocherhans on 01/11/10 12:35:12.
A rework based on conversations with Honza

Change History

01/06/10 19:47:34 changed by jkocherhans

  • status changed from new to assigned.
  • needs_better_patch changed.
  • needs_tests changed.
  • owner changed from nobody to jkocherhans.
  • needs_docs changed.
  • has_patch set to 1.

01/06/10 19:51:18 changed by jkocherhans

  • stage changed from Unreviewed to Design decision needed.

See #12513 and this thread on django-dev for more info.

01/07/10 05:46:55 changed by EmilStenstrom

  • cc set to em@kth.se.

01/07/10 12:00:36 changed by jezdez

  • cc changed from em@kth.se to em@kth.se, jezdez.

01/08/10 03:50:16 changed by knutin

  • cc changed from em@kth.se, jezdez to em@kth.se, jezdez, knutin@gmail.com.

01/08/10 11:28:26 changed by Honza_Kral

  • attachment only_validate_fields_on_modelform_not_entire_model.diff added.

Simple quick patch implementing desired behavior, just a case study, still needs work

01/08/10 12:55:39 changed by zbyte64

  • cc changed from em@kth.se, jezdez, knutin@gmail.com to em@kth.se, jezdez, knutin@gmail.com, zbyte64@gmail.com.

01/09/10 06:47:03 changed by isagalaev

  • cc changed from em@kth.se, jezdez, knutin@gmail.com, zbyte64@gmail.com to em@kth.se, jezdez, knutin@gmail.com, zbyte64@gmail.com, Maniac@SoftwareManiacs.Org.

(follow-up: ↓ 9 ) 01/09/10 09:48:23 changed by isagalaev

  • owner changed from jkocherhans to isagalaev.
  • status changed from assigned to new.
  • needs_tests set to 1.

I'm about to add some tests to jkocherhans' patch. Honza, your patch is supposedly does the same thing, can you comment if you want yours as a replacement or not?

(in reply to: ↑ 8 ) 01/09/10 10:21:48 changed by Honza_Kral

Replying to isagalaev:

I'm about to add some tests to jkocherhans' patch. Honza, your patch is supposedly does the same thing, can you comment if you want yours as a replacement or not?

yes, the only problem with my patch, afaik, i sthat it only looks at exclude and not in at fields in django/forms/models.py line 252. As I said earlier, it's a quick patch (part of it copy paste, which is plain wrong) just to show the direction to go.

01/09/10 10:53:22 changed by tolano

  • cc changed from em@kth.se, jezdez, knutin@gmail.com, zbyte64@gmail.com, Maniac@SoftwareManiacs.Org to em@kth.se, jezdez, knutin@gmail.com, zbyte64@gmail.com, Maniac@SoftwareManiacs.Org, aribao@gmail.com.
  • owner changed from isagalaev to tolano.
  • status changed from new to assigned.

01/09/10 10:55:00 changed by tolano

  • owner changed from tolano to isagalaev.
  • status changed from assigned to new.

What happened? I didn't change the owner nor the status! I'll try to set it back.

01/09/10 12:06:54 changed by isagalaev

  • attachment 12521.tests.diff added.

Correct tests failing on current trunk

01/09/10 12:11:50 changed by isagalaev

  • owner changed from isagalaev to jkocherhans.

Added a couple of tests that fail with current trunk. Some comments:

  • admin_views tests are testing UserCreationForm? from contrib.auth. It used not to break after model-validation merge because it was correcting model instance as a side effect of validation. It's not rewritten in more idiomatic way applying its non-form data in save() (and currently it correctly fails due to missing "password" field in the form)
  • model_forms tests are exercising different ways to set field on a ModelForm? and also make sure that the form still provides an incomplete and unsaved instance of a model

01/09/10 13:42:18 changed by jkocherhans

  • attachment 12521.diff added.

This combines my fixes, Honza fixes, and Ivan's tests. Almost there.

01/09/10 13:44:08 changed by jkocherhans

  • status changed from new to assigned.

My current patch is almost there I think. I still need to talk to Honza about where the model.validate() hook should be called, but other than that I thinks it's ready. It also fixes #12520, #12537, and #12552.

01/09/10 16:40:50 changed by jkocherhans

  • attachment 12521.2.diff added.

Similar to the last patch, but splits full_validate into validate_fields, validate_unique, validate.

01/10/10 09:48:47 changed by jkocherhans

  • attachment 12521.3.diff added.

Now with updated docs for model's validation methods.

01/10/10 10:00:55 changed by isagalaev

Confirming that current patch fixes my use-case with PreviewForm?.

01/11/10 12:35:12 changed by jkocherhans

  • attachment 12521.4.diff added.

A rework based on conversations with Honza

01/12/10 07:03:44 changed by lukeplant

  • status changed from assigned to closed.
  • resolution set to fixed.

This was fixed in [12206] by Joseph Kocherhans (typo in commit message says #12512 instead of #12521).


Add/Change #12521 (ModelAdmin save_model request.user idiom is broken by model validation)




Change Properties
Action