2009-06-18 14 views
5

In ASP.NET MVC, è possibile utilizzare i AcceptVerbs attributo per correlare una funzione di visualizzazione con un verbo:Http decoratore di verbi per Django?

public ActionResult Create() 
{ 
    // do get stuff 
} 

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Create(FormCollection collection) 
{ 
    // do post stuff 
} 

The Django Book suggerisce qualcosa di simile:

def method_splitter(request, *args, **kwargs): 
    get_view = kwargs.pop('GET', None) 
    post_view = kwargs.pop('POST', None) 
    if request.method == 'GET' and get_view is not None: 
     return get_view(request, *args, **kwargs) 
    elif request.method == 'POST' and post_view is not None: 
     return post_view(request, *args, **kwargs) 
    raise Http404 

urls.py:

urlpatterns = patterns('', 
    # ... 
    (r'^somepage/$', views.method_splitter, {'GET': views.some_page_get, 
     'POST': views.some_page_post}), 
    # ... 
) 

Mi sembra un po 'brutto - c'è un decoratore che può associare un verbo HTTP a una vista, ASP.NET in stile MVC o un altro accettabile d modo di fare questo?

risposta

11

Ci sono decoratori incorporati standard per richiedere un particolare metodo HTTP o un elenco di metodi consentiti.

Vedere il codice: http://code.djangoproject.com/browser/django/trunk/django/views/decorators/http.py.

+0

Questo è davvero il modo di farlo. Nota che c'è anche un generatore di decoratore che ti permette di creare decoratori per qualsiasi combinazione di metodi che ti piacciono. (e non ho idea del perché sei stato downvoted per dare la risposta ovvia e corretta alla domanda ...) –

+3

Credo che questa non è la risposta corretta, perché require_http_methods() è un filtro, non dispatcher. Non si può fare @require_http_methods ("GET") per una funzione, @require_http_methods ("POST") per un'altra (con lo stesso nome!), E lasciare che Django scelga la giusta da chiamare per metodo verb. – drdaeman

9

Risposta aggiornata nel 2016: Modern Django ha tutto il necessario integrato e disponibile tramite class-based views. Nella forma più grezza, l'approccio canonico è subclasssing django.views.generic.View e l'attuazione di metodi di classe che prendono il nome i verbi HTTP:

class MyView(View): 
    def get(self, request, *args, **kwargs): 
     # ... 

    def post(self, request, *args, **kwargs): 
     # ... 

Internamente, questo funziona in modo molto simile al mio antico codice qui sotto (che è stato scritto prima Django aveva viste basate sulla classe). C'è un metodo View.dispatch che fondamentalmente cerca cosa chiamare, o restituisce 405 se non riesce a trovare nulla: getattr(self, request.method.lower(), self.http_method_not_allowed).

Ovviamente, se si esegue l'elaborazione dei moduli, il rendering dei modelli o roba CRUD comune, assicurarsi di verificare le sottoclassi disponibili View.


Legacy risposta a partire dal 2009 sotto. Il codice funziona ancora nel 2016, ma non è una soluzione DRY, quindi non utilizzarlo. Nel 2011 Django ha ottenuto visualizzazioni basate su classi e al giorno d'oggi sono il modo standard in cui le cose dovrebbero essere fatte. Sto mantenendo questo qui solo per scopi storici. Vecchio risposta testo segue:

In una particolare visione in cui ho bisogno di avere codice separato per i diversi metodi HTTP (questo è il mio piccolo implementazione WebDAV), sto facendo qualcosa di simile:

class SomeView(object): 
    def method_get(self, request, ...): 
     ... 

    def __call__(self, request, *args, **kwargs): 
     m = getattr(self, 'method_%s' % request.method.lower(), None) 
     if m is not None: 
      return m(request, user, *args, **kwargs) 
     return HttpResponseNotAllowed("405 Method Not Allowed") 

# Then url(r'...', SomeView()), 

Aggiunto/modificato: Bene, ho pensato un po 'e in realtà implementato approccio decoratore. Non è così male come inizialmente pensavo.

def method_not_allowed_view(request, *args, **kwargs): 
    return HttpResponseNotAllowed("405 Method Not Allowed") 

def http_method(*methods): 
    methods = map(lambda m: m.lower(), methods) 
    def __method_wrapper(f): 
     this_module = __import__(__name__) 
     chain = getattr(this_module, f.__name__, method_not_allowed_view) 
     base_view_func = lambda request, *args, **kwargs: \ 
      f(request, *args, **kwargs) if request.method.lower() in methods \ 
             else chain(request, *args, **kwargs) 
     setattr(this_module, f.__name__, base_view_func) 
     return base_view_func 
    return __method_wrapper 

@http_method('get') 
def my_view(request): 
    return HttpResponse("Thank you for GETting.") 

@http_method('post', 'put') 
def my_view(request): 
    return HttpResponse("Thank you for POSTing or PUTting.") 

# url(r'...', 'app.my_view'), 

Questo post è un wiki comunità, in ogni caso, quindi sentitevi liberi di migliorare se vi piace l'idea! E la cronologia delle revisioni contiene anche alcuni approcci un po 'diversi che ho provato prima di scrivere questo ...

+1

nota, che nella maggior parte del lavoro sotto forma di elaborazione che probabilmente in realtà non vuole separare GET e POST metodi, perché si sta probabilmente andando per restituire la pagina, contenente il modulo con errori sul POST non riuscito. – drdaeman

+0

Bel lavoro - stavo scrivendo una soluzione simile quando ho visto la tua risposta. Sei comunque riuscito a ottenere un risultato più elegante;) –

+0

Grazie. In realtà, c'è molto da migliorare: il codice corrente non tenta nemmeno di preservare gli attributi (come __doc__ o __name__) e non è realmente soggetto a errori (ad esempio, non c'è affatto controllo della firma). Stavo pensando di usare il modulo decoratore (http://pypi.python.org/pypi/decorator) ma sono troppo pigro;) – drdaeman

3

È possibile utilizzare View Decorators

Dalla documentazione:

from django.views.decorators.http import require_http_methods 

@require_http_methods(["GET", "POST"]) 
def my_view(request): 
    # I can assume now that only GET or POST requests make it this far 
    # ... 
    pass