2009-09-24 16 views
31

que tienen un modelo:Django modelo: eliminar() no provocó

class MyModel(models.Model): 
... 
    def save(self): 
     print "saving" 
     ... 
    def delete(self): 
     print "deleting" 
     ... 

El Save() - Método se dispara, pero el borrado() no lo es. Utilizo la última versión de svn (Django versión 1.2 pre-alpha SVN-11593), y con respecto a la documentación en http://www.djangoproject.com/documentation/models/save_delete_hooks/ esto debería funcionar. Alguna idea?

+0

¿Tal vez no está borrando nada? ¿Puedes mostrarnos en qué estás invocando delete()? – artagnon

+0

Lo probé simplemente borrando un elemento en el área de administración y no lo llamé manualmente. – schneck

Respuesta

68

Creo que probablemente esté utilizando la función de eliminación masiva del administrador, y se está ejecutando el hecho de que el método de eliminación masiva del administrador no llama al delete() (consulte el relacionado ticket).

He redondeado esto en el pasado al escribir una acción de administrador personalizada para eliminar modelos.

Si no está utilizando el método de eliminación masiva del administrador (por ejemplo, está haciendo clic en el botón Eliminar en la página de edición del objeto), entonces está sucediendo algo más.

Véase la advertencia here:

La acción “eliminar objetos seleccionados” utiliza QuerySet.delete() para la eficiencia razones, que cuenta con una importante salvedad: método de su modelo delete() no se llamará.

Si desea anular este comportamiento, escribe simplemente una acción personalizada, que lleva a cabo supresión en su forma preferida - por ejemplo, llamando Model.delete() para cada uno de los elementos seleccionados .

Para obtener más información sobre la eliminación masiva, consulte la documentación en object deletion.

Mi modelo de administración personalizada se ve así:

from photoblog.models import PhotoBlogEntry 
from django.contrib import admin  

class PhotoBlogEntryAdmin(admin.ModelAdmin): 
    actions=['really_delete_selected'] 

    def get_actions(self, request): 
     actions = super(PhotoBlogEntryAdmin, self).get_actions(request) 
     del actions['delete_selected'] 
     return actions 

    def really_delete_selected(self, request, queryset): 
     for obj in queryset: 
      obj.delete() 

     if queryset.count() == 1: 
      message_bit = "1 photoblog entry was" 
     else: 
      message_bit = "%s photoblog entries were" % queryset.count() 
     self.message_user(request, "%s successfully deleted." % message_bit) 
    really_delete_selected.short_description = "Delete selected entries" 

admin.site.register(PhotoBlogEntry, PhotoBlogEntryAdmin) 
+0

yup, eso es todo, muchas gracias. ¿podría explicar brevemente cómo es su método de administración personalizada? – schneck

+0

@schneck - ¡Claro! –

+0

por cierto - puede haber formas más elegantes de lograrlo, ¡pero funciona! –

29

Sé que esta pregunta es antiguo, pero me acabo de encontrar con esto otra vez y quería añadir que siempre se puede mover el código a un pre_delete o post_delete señal de este modo:

from django.db.models.signals import pre_delete 
from django.dispatch.dispatcher import receiver 

@receiver(pre_delete, sender=MyModel) 
def _mymodel_delete(sender, instance, **kwargs): 
    print "deleting" 

funciona con mayor del administrador eliminar la acción (por lo menos a partir de 1.3.1).

+0

muy resbaladizo. Me pregunto si hay alguna mejora con esto en django 1.4? –

+0

¡tnx a ton! Utilicé post_delete para evitar eliminaciones recursivas. – Babu

5

La acción masiva de las llamadas de administrador queryset.delete().

Puede anular el método .delete() del conjunto de interrogación, , por lo que siempre elimina 1 por 1 de los objetos. Por ejemplo:

en managers.py:

from django.db import models 
from django.db.models.query import QuerySet 

class PhotoQueryMixin(object): 
    """ Methods that appear both in the manager and queryset. """ 
    def delete(self): 
     # Use individual queries to the attachment is removed. 
     for photo in self.all(): 
      photo.delete() 

class PhotoQuerySet(PhotoQueryMixin, QuerySet): 
    pass 

class PhotoManager(PhotoQueryMixin, models.Manager): 
    def get_query_set(self): 
     return PhotoQuerySet(self.model, using=self._db) 

En models.py:

from django.db import models 

class Photo(models.Model): 
    image = models.ImageField(upload_to='images') 

    objects = PhotoManager() 

    def delete(self, *args, **kwargs): 
     # Note this is a simple example. it only handles delete(), 
     # and not replacing images in .save() 
     super(Photo, self).delete(*args, **kwargs) 
     self.image.delete() 
3

El principal problema es que la mayor parte de la administración de Django borrado utiliza SQL, no instancia .delete(), como se indica en otra parte. Para una solución solo de administrador, la siguiente solución preserva el intersitial del administrador de Django "¿realmente quieres eliminar estos?". La solución de vdboor es la más general, sin embargo.

from django.contrib.admin.actions import delete_selected 

class BulkDeleteMixin(object): 
    class SafeDeleteQuerysetWrapper(object): 
     def __init__(self, wrapped_queryset): 
      self.wrapped_queryset = wrapped_queryset 

     def _safe_delete(self): 
      for obj in self.wrapped_queryset: 
       obj.delete() 

     def __getattr__(self, attr): 
      if attr == 'delete': 
       return self._safe_delete 
      else: 
       return getattr(self.wrapped_queryset, attr) 

     def __iter__(self): 
      for obj in self.wrapped_queryset: 
       yield obj 

     def __getitem__(self, index): 
      return self.wrapped_queryset[index] 

     def __len__(self): 
      return len(self.wrapped_queryset) 

    def get_actions(self, request): 
     actions = super(BulkDeleteMixin, self).get_actions(request) 
     actions['delete_selected'] = (BulkDeleteMixin.action_safe_bulk_delete, 'delete_selected', ugettext_lazy("Delete selected %(verbose_name_plural)s")) 
     return actions 

    def action_safe_bulk_delete(self, request, queryset): 
     wrapped_queryset = BulkDeleteMixin.SafeDeleteQuerysetWrapper(queryset) 
     return delete_selected(self, request, wrapped_queryset) 


class SomeAdmin(BulkDeleteMixin, ModelAdmin): 
    ... 
Cuestiones relacionadas