2015-02-16 6 views
9

Qual è l'approccio migliore per la gestione dell'eliminazione di un oggetto con qualche convalida prima che l'oggetto venga eliminato? Ad esempio, nella mia configurazione sono disponibili due modelli: Game e Team (che sono ovviamente correlati). Gli utenti dovrebbero essere in grado di eliminare solo squadre che NON sono legate a nessun gioco.Convalida tramite DeleteView prima dell'eliminazione dell'istanza

Ho creato un modulo (senza campi) per l'eliminazione di una squadra ...

class TeamDeleteForm(ModelForm): 
    class Meta: 
     model = Team 
     fields = [] 

    def clean(self): 
     # Check to see if this team is tied to any existing games 
     if self.instance.gameteams_set.exists(): 
      raise ValidationError("This team is tied to 1 or more games") 
     return super().clean() 

ma poi ho capito che la base di classe di visualizzazione DeleteView non ha alcun tipo di metodo form_valid(). Dovrei estendere il FormView generico invece di DeleteView o c'è un approccio migliore che mi manca?

+0

Hai dato un'occhiata alla mia risposta qui sotto? –

+0

@SimonCharette l'ho fatto, grazie. Tuttavia, penso di avere una soluzione migliore utilizzando FormView. Ancora non è l'ideale, e sto aspettando di vedere se qualcun altro ha intenzione di rispondere prima di pubblicare la mia soluzione. – Ben

risposta

8

Penso che l'approccio migliore sostituirà il metodo di eliminazione del modello.Ad esempio:

class Team(models.Model): 
    ... 
    def delete(self, *args, **kwargs): 
     if Game.objects.filter(team__pk= self.pk).exists(): 
      raise Exception('This team is related to a game.') 
     super().delete(*args, **kwargs) 
+4

Come può essere questa la soluzione accettata? Ciò comporterà un errore 500 dal momento che stai sollevando un'eccezione non gestita ... –

+1

D'accordo, penso che la risposta di @Esteban sia la migliore. Risponde alla domanda più direttamente. – jaywhy13

+0

Per un'esperienza utente migliore, è possibile restituire HttpResponseForbidden() anziché sollevare Exception() – acidjunk

6

Per il tuo caso particolare, dovrei semplicemente ignorare l'attributo queryset della vista per escludere Team s con Game s associato.

class TeamDeleteView(DeleteView): 
    queryset = Team.objects.distinct().exclude(games__isnull=False) 

C'è un Django ticket opened to make the DeleteView behave like other form views ma fino the proposed patch si fonde e rilasciati (Non farà in 1.8) dovrete ignorare completamente il metodo della visualizzazione come la seguente delete:

class TeamDeleteView(DeleteView): 
    model = Team 

    def delete(request, *args, **kwargs): 
     self.object = self.get_object() 
     if self.object.gameteams_set.exists(): 
      # Return the appropriate response 
     success_url = self.get_success_url() 
     self.object.delete() 
     return HttpResponseRedirect(success_url) 

Modifica:

Dalla soluzione accettata sembra che si stia tentando di impedire la cancellazione a livello di modello. Tale applicazione dovrebbe essere eseguita utilizzando un gestore PROTECTon_delete.

from django.db import models 

class Team(models.Model): 
    pass 

class Game(models.Model): 
    team = models.ForeignKey(Team, on_delete=models.PROTECT) 

Avrai ancora a che fare con la sollevato ProtectedError nella vista:

from django.db import models 
from django.http.response import HttpResponseForbidden 

class TeamDeleteView(DeleteView): 
    model = Team 

    def delete(request, *args, **kwargs): 
     try: 
      return super(TeamDeleteView, self).delete(
       request, *args, **kwargs 
      ) 
     except models.ProtectedError as e: 
      # Return the appropriate response 
      return HttpResponseForbidden(
       "This team is tied to 1 or more games" 
      ) 

si potrebbe anche utilizzare la proprietà protected_objects di e per visualizzare un messaggio di errore più significativo, proprio come l'amministratore fa .

+0

Cosa succede se vuoi farlo con l'Admin di Django? – Gocht

+0

L'amministratore di Django si occupa automaticamente di questi errori in [1.10+] (https://github.com/django/django/commit/a7c813ba045044ec23bb656ef9a23a0e38e36514) ma è possibile sovrascrivere il metodo 'delete_selected' della sottoclasse' ModelAdmin' in modo simile se è possibile ' t usare questa versione. –

6

Ho usato sia DeleteView che FormView per questo scenario. Entrambi hanno i loro pro e contro.

DeleteView è bello perché basato su SingleObjectMixin e consente di accedere facilmente all'oggetto che si desidera eliminare. Un buon modo per farlo è sollevare un'eccezione in get_object. Questo rende così possibile sollevare un'eccezione sia su get che su post.

def get_object(self, qs): 
    obj = super(FooView, self).get_object(qs) 
    if obj.can_delete(): 
    return obj 
    raise PermissionDenied 

FormView è bello perché è possibile sfruttare form_invalid ei metodi di pulizia, ma poi si devono ancora fare il lavoro per ottenere l'oggetto, messa a punto una sorta di forma (non necessaria in deleteview).

È davvero una questione di come si vuole affrontarlo. Alcune altre domande sono: sollevate un'eccezione su GET o volete mostrare una bella pagina per far sapere all'utente che non possono cancellare l'oggetto. Questo può essere fatto in entrambi i tipi di vista.

Aggiorna la tua domanda se hai più punti da approfondire e aggiornerò la mia risposta.

Problemi correlati