2009-06-18 10 views
5

En ASP.NET MVC, puede utilizar los AcceptVerbs atributo para correlacionar una función de vista con un verbo:Http verb decorator for Django?

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

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

The Django Book sugiere algo como esto:

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}), 
    # ... 
) 

Eso me parece un poco feo: ¿hay un decorador que pueda asociar un verbo HTTP con una vista, ASP.NET MVC-style u otro aceptar? d forma de hacer esto?

Respuesta

11

Hay decoradores incorporados estándar para requerir un método HTTP particular o una lista de métodos permitidos.

Ver el código: http://code.djangoproject.com/browser/django/trunk/django/views/decorators/http.py.

+0

Esta es de hecho la manera de hacerlo. Tenga en cuenta que también hay un generador de decorador que le permite crear decoradores para cualquier combinación de métodos que desee. (y no tengo idea de por qué se votó negativamente por dar la respuesta obvia y correcta a la pregunta ...) –

+3

Creo que esta no es la respuesta correcta, porque require_http_methods() es un filtro, no un despachador. No se puede hacer @require_http_methods ("GET") para una función, @require_http_methods ("POST") para otra (¡con el mismo nombre!), Y dejar que Django elija la adecuada para llamar por método de verbo. – drdaeman

9

respuesta actualizada en 2016: moderna Django tiene todo lo necesario built-in y disponible a través del class-based views. En la forma más cruda, el enfoque canónico es subclasssing django.views.generic.View y la aplicación de métodos de clase que llevan el nombre de los verbos HTTP:

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

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

Internamente, esto funciona de una manera muy similar a mi antiguo código de abajo (que fue escrito antes Django tenía vistas basadas en clases). Hay un método View.dispatch que básicamente busca qué llamar, o devuelve 405 si no puede encontrar nada: getattr(self, request.method.lower(), self.http_method_not_allowed).

Por supuesto, si realiza el procesamiento, la representación de la plantilla o las cosas comunes de CRUD, asegúrese de consultar las subclases View disponibles.


Legacy answer from 2009 a continuación. El código aún funciona en 2016, pero no es una solución SECA, así que no lo use. En 2011 Django obtuvo vistas basadas en clases y hoy en día son la forma estándar de cómo se deben hacer las cosas. Lo mantengo aquí solo con fines históricos. texto de la respuesta de edad sigue:

En una visión particular en el que deben tener el código separado para diferentes métodos HTTP (esta es mi pequeña aplicación WebDAV), que estoy haciendo algo como esto:

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()), 

Agregado/editado: Bueno, he pensado un poco y realmente implementé el enfoque de decorador. No es tan malo como inicialmente pensé.

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'), 

Este post es una wiki de la comunidad, de todos modos, así que no dude que mejorar si te gusta la idea! Y el historial de revisión también contiene algunas aproximaciones un poco diferentes que probé antes de escribir esto ...

+1

Tenga en cuenta, que en la mayoría del trabajo de procesamiento de formularios es probable que en realidad no desea separar GET y POST métodos, ya que son probablemente va para devolver la página, que contiene el formulario con errores en la POST fallida. – drdaeman

+0

Buen trabajo: estaba escribiendo una solución similar cuando vi su respuesta. Sin embargo, has logrado llegar a un resultado más elegante;) –

+0

Gracias. En realidad, hay mucho que mejorar: el código actual ni siquiera intenta conservar los atributos (como __doc__ o __name__) y no es propenso a errores (por ejemplo, no hay ninguna comprobación de firma en absoluto). Estaba pensando en utilizar el módulo de decorador (http://pypi.python.org/pypi/decorator) pero soy demasiado vago;) – drdaeman

3

Puede utilizar View Decorators

A partir de los documentos:

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 
Cuestiones relacionadas