New admin feature: Delete and replace with existing object

211 views
Skip to first unread message

Ric

unread,
Jul 7, 2010, 3:41:40 AM7/7/10
to Django developers
Hello i opened a ticket, but somone suggest me to discuss this new
feature here.

http://code.djangoproject.com/ticket/13900
----
i would like to edit
django.contrib.admin.options.ModelAdmin?.delete_view and add a helpful
feature.

Instead of deleting all the nested objects, the delete_view could
handle 3 options:

1. delete all the related objects (current option)
2. where the foreign key to the current instance is nullable, null or
delete
3. replace all the related objects with an other model instance (we
can make a user choose it with a forms.ModelChoiceField?)
i really would like to contribute to this feature, because i need to
do this in my admin site, but i'm new, so please help me :)

bye bye

about me: i'm riccardo di virgilio, i have programmed sprint24.com
with django. i have learn a lot of things and i think i'm ready to
contribute.

Russell Keith-Magee

unread,
Jul 7, 2010, 9:20:12 AM7/7/10
to django-d...@googlegroups.com
On Wed, Jul 7, 2010 at 3:41 PM, Ric <riccardod...@gmail.com> wrote:
> Hello i opened a ticket, but somone suggest me to discuss this new
> feature here.
>
> http://code.djangoproject.com/ticket/13900
> ----
> i would like to edit
> django.contrib.admin.options.ModelAdmin?.delete_view and add a helpful
> feature.
>
> Instead of deleting all the nested objects, the delete_view could
> handle 3 options:
>
> 1. delete all the related objects (current option)
> 2. where the foreign key to the current instance is nullable, null or
> delete

These first two options are really a duplicate of ticket #7539; it
isn't something that should be handled at the admin level, but at the
model level. The broad ideas behind #7539 are well established; the
patch just needs to be carried the last few steps to trunk.

> 3. replace all the related objects with an other model instance (we
> can make a user choose it with a forms.ModelChoiceField?)
> i really would like to contribute to this feature, because i need to
> do this in my admin site, but i'm new, so please help me :)

This is an interesting idea. At a raw technical level, introducing a
'pre-deletion cleanup' stage for admin deletions shouldn't be too
difficult -- the current deletion confirmation view provides a
convenient place in the workflow that could be replaced with a view
that provides options to nominate how relations should be cleaned up.

However, my concern is whether this could be made a generic
capability. If this is to become a configurable capability of the
admin interface, it needs to work with every possible model, not just
a subset of models. It isn't immediately obvious to me how you could
specify a relation substitution scheme in a completely generic way.

There's also the issue of how to handle this sort of substitution on a
bulk delete - if you're deleting a group of 50 objects using an admin
bulk action or as a result of a cascaded deletion, you don't want to
be presented with an auto-generated dialog of 50 pulldown/raw_id_admin
widgets.

I suspect the best course of action for trunk will be to make it
*possible* to easily introduce a pre-deletion stage, but not actually
provide that capability as part of core admin functionality (i.e.,
cleanup and document the mechanism by which you can define a
pre-delete view, but not actually provide a "cleanup= ('field',
'field')" definition on ModelAdmin that automatically turns into a
pre-configured deletion view.

Yours,
Russ Magee %-)

Felipe Prenholato

unread,
Jul 7, 2010, 5:41:49 PM7/7/10
to django-d...@googlegroups.com
Just last week I need to implement something like option 2 in a generic crud (people that command project doesn't liked admin ...), so what I simple customized delete of model and force app to call delete of model when deleting more than one object.

So, I really vote +1 for this kind of change.

2010/7/7 Russell Keith-Magee <rus...@keith-magee.com>

--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-d...@googlegroups.com.
To unsubscribe from this group, send email to django-develop...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.




--
Felipe 'chronos' Prenholato.
Linux User nº 405489
Home page: http://chronosbox.org/blog
Twitter: http://twitter.com/chronossc

Ric

unread,
Jul 8, 2010, 11:19:20 AM7/8/10
to Django developers
Hi Russ, nice to talk with u,

i think that we can focus only on substitute feature,
to archive it we have 2 options:

simply upgrade the current delete_view or make a new view, something
like
url(r'^(.+)/substitute/$',
wrap(self.substitute_view),
name='%s_%s_substitute' % info),


to make it work for every model we need to build form with one
ModelChoiceField.
this field will be initialized with a queryset of the model_admin
(queryset method), and then call the queryset exclude method to remove
from the field the instance/s that will be deleted.

field = forms.ModelChoiceField(queryset =
self.queryset(request).exclude(**{"%s__in" %
self.model._meta.pk.attname: objects_to_delete})

so imagine that we have 10 object

1 Banana
2 Apple
3 Tree
4 ...
10 Stick

we mark for deletion object 1 and 2, in the view the form will make us
choose objects from 3 to 10, the use choose to substitute with object
3 and all Banana and Apple in the database will be replaced with a
Tree, using the queryset update method.

is it clear?



On 7 Lug, 15:20, Russell Keith-Magee <russ...@keith-magee.com> wrote:

Ric

unread,
Jul 8, 2010, 1:10:26 PM7/8/10
to Django developers
i took this idea from wordpress. in wordpress when you delete an user
instead of deleting all related posts you can choose to assign them to
another user.

we can extend this system on every model class, i think.

whatch here, sorry wordpress is in italian (hope u understand a
little)
http://grab.by/5kn6

we can use a radio field to choose the mode (or we can choose the mode
with a GET param, or with two different urls ^delete/ and
^substitute/), and then a combobox to select the instance...

not too difficult, and it would be a fancy feature.

On Jul 7, 3:20 pm, Russell Keith-Magee <russ...@keith-magee.com>
wrote:

Russell Keith-Magee

unread,
Jul 8, 2010, 8:13:49 PM7/8/10
to django-d...@googlegroups.com

This much was already clear. My concern is about the edge cases.

Lets say we have an Author model that we are deleting. You're giving
the use case of deleting an author, and modifying all the Blog posts
that reference that author so that they point somewhere else. I have
no problems with the concept or implementation of this as you describe
it.

But:

* What if you have multiple models referring to Author? Do you assume
that every related model will be updated the same way?

* What if you have multiple foreign keys from Blog to Author? Do you
assume that every foreign key on a single model will be updated in the
same way?

* What if there is a model with a m2m relation to Author? Do you
delete the m2m relation, or do you replace it with the substituted
object?

* What if you use admin actions to delete N authors? Do we assume
that every one of the authors will be replaced with the same single
substitute?

These sorts of decisions are all business logic decisions. You may
have a good answer for your site, but I'm not convinced that there is
a universal answer that will always be correct for all users, and I'm
not convinced that it would be possible to define an easily
configurable interface to expose all possible interpretations of this
feature.

This is why I'm suggesting that the best approach here is to make it
*possible*, but not to bake this capability into Django itself. If we
provide the appropriate hooks, you (as an end user) should be able to
add whatever implementation of this feature you want.

If there are hooks on Django's admin that need to be added or
modified, then I'm happy to entertain those changes.

Yours,
Russ Magee %-)

Ric

unread,
Jul 9, 2010, 10:37:55 AM7/9/10
to Django developers
i'll reply one by one

* What if you have multiple models referring to Author? Do you
assume
that every related model will be updated the same way?

* What if you have multiple foreign keys from Blog to Author? Do you
assume that every foreign key on a single model will be updated in
the
same way?

i dont' think this is an issue, we do not need to reinvent the wheel.
django uses get_deleted_objects to retrieve all related objects

(deleted_objects, perms_needed) = get_deleted_objects((obj,), opts,
request.user, self.admin_site)

we can use this feature, and then do a function that cycle and update,
or for a more efficient alghoritm we can use a queryset update.

but maybe it's better to call method save on every single object
beacause the save method could be customized.

* What if you use admin actions to delete N authors? Do we assume
that every one of the authors will be replaced with the same single
substitute?

I think yes, beacuse as you said before, a mutiple user choose would
be not so user friendly.

but we can define an attr, something like
admin.ModelAdmin.replace_multiple_objects = True
if True, for every object will be created a replacer field... but i
think this is difficult to archive, and not so smart...


I think that this behaviour is indipendent from the model. and i don't
think there are different interpretations of this feature.

let me explain:
you are going to delete an object.
django is saying to you: "in order to preserve database integrity, i
have to delete all this object."

do you want to delete them or you want to substitute with another one?

replacing the object anywhere in the db keeps the database integrity
safe.


i don't see many hooks here...
what kind of hooks do you have in mind? like define a list of models
in the admin class that will be substituted?


---
i also realized that we can handle this with a single field

replace = forms.ModelChoiceField(queryset = qs, empty_label =
ugettext("Do not substitute, delete all related objects"))

if no object is selected, django deletes all the related objects,
otherwise it will'substitute them.

thanks Russ, but how to involve more people in this discussion, and
take the discussion to a next step?


Russell Keith-Magee

unread,
Jul 10, 2010, 2:27:40 AM7/10/10
to django-d...@googlegroups.com

I understand this. What you seem to be missing is that in the general
case, there isn't a single, canonical answer that will *always* be
correct.

If you are deleting 1 object, there are O(N*M) objects that need to be
updated, where N is the number of models X that have a relation to the
object being deleted, and M is the number of instances of X that have
a relation to the object being deleted.

As an example: I have an Author, Article and Address record. Article
and Address both have a FK on Author. If I delete Author "John", I
need to do something to any Article and Address record that points at
John. In my business logic, I want to update all Articles to point at
a dummy author, but I want to cascade delete all the Address records.

To complicate issues some more - lets say I want some Article records
to point at a dummy author, and some to point at a new author that is
going to take responsibility. I need to be able to select which
authors will be applied to which articles. There might even be a
programatic scheme that I can use to semi-automate this reassignment
(or automate the initial values prior to a formal confirmation by the
site admin).

Next complication -- if you're doing a bulk delete of P objects (using
an admin delete action), all the decisions that had to be done N*M
times now need to be done P*N*M times.

On top of all this, there are cascading problems. For example, lets
say Authors can belong to Teams. If I delete a team, I know I want to
cascade delete all the Authors that belong to that team. But the
Articles associated with members of those teams need to be reassigned.

Then consider the bulk deletion of teams, and so on.

The point I'm trying to make is that there is no single answer that we
can implement that will be right for all business cases. So the best
we can do is to make it *possible* to introduce pre-deletion cleanup
logic, make that interface as clean as we can, and leave it up to the
end user to implement a scheme that is appropriate for their
implementation. These are the 'hooks' that I'm referring to.

Once those hooks are in place, the simple "confirm you want to replace
all instances of X with Y" approach that you're proposing should be a
couple of lines of code, which will probably form the simple example
provided in documentation. However, it will be possible for other
users to implement more complex schemes, as appropriate.

> thanks Russ, but how to involve more people in this discussion, and
> take the discussion to a next step?

A prototype implementation would be a good start. I've been raising
design issues looking entirely from a public API perspective. I
haven't done any exploration of how this would be implemented, so I
have no idea what technical hurdles (if any) exist.

Yours,
Russ Magee %-)

Ric

unread,
Jul 12, 2010, 1:37:46 PM7/12/10
to Django developers
hi russ i have been on a wedding!

ok, now i see the point.

but what i was tring to say is that adding a simple version of this
feature would be a plus to the actual version of django, with hooks it
would be a great plus.

i think that definition of public API is something that should be done
after implementing this feature.

we'll see how django comunity will react to such feature and then
we'll project hooks based on feedbacks and suggestions.

so i propose to develep this feature in two step:
- add a method, something like pre_deletion_cleanup(request, object,
deleted_object)
this must return the deleted_objects var

- add hooks and document them. hooks will be triggered inside this
function

take in mind one thing:

one hook of that kind, in not so easy to implement for some django
design principles.

1.
in the template admin/delete_confirmation.html the deleted_objects as
rendered in this way

<ul>{{ deleted_objects|unordered_list }}</ul>

so it is not easy to mantain this template, we should add two list of
objects: a deleted object list and a replaced object list.

2.
adding such a hook needs to write a differente admin_log, adding a
note on what has been replaced.




my proposal :

for now we could add some one simple hook: defining a new var inside
the admin class,
something like on_delete_replace = True, that allows this feature.

then we add a method inside the admin_class, this method wille takes
those objects:
pre_deletion_cleanup(request, object, deleted_objects)

it takes a form (with another subclassable method) and cycle throught
objects to replace Fk and ManyTomanyFields.

inside the template two simple change
1.
{% if deleting %} All of the following related items will be deleted:
{% else %} All of the following related items will be replaced with
"{{ replace_object_name }}":{% endif %}
2.
form rendering


what do you think? am i missing something?

how can i do such a prototype? i mean, how i propose to django
comunity such a prototype?

thanks



On Jul 10, 8:27 am, Russell Keith-Magee <russ...@keith-magee.com>
wrote:

Russell Keith-Magee

unread,
Jul 12, 2010, 7:32:39 PM7/12/10
to django-d...@googlegroups.com

I'm starting to feel like I'm repeating myself.

This isn't something that can be described as a general rule. It
requires an understanding of business logic.

Therefore, it makes no sense to implement this as a builtin feature of
admin that is available as a simple exposed setting. The best we can
hope to do is to implement a hook.

> how can i do such a prototype? i mean, how i propose to django
> comunity such a prototype?

The way this works is that you write the patch, and then we consider
it for inclusion in trunk. I've given you as much guidance as I can on
the sort of approach that will be accepted into core. It's up to you
to follow up on that guidance.

Yours,
Russ Magee %-)

Reply all
Reply to author
Forward
0 new messages