urls tutorial template examples python django python-2.7 django-views django-generic-views

python - tutorial - django urls path



¿Cómo funciona la clase View de django? (2)

La implementación subyacente de estas vistas implica un Python bastante avanzado, por lo que si eres un principiante relativo, no es sorprendente si encuentras que este código es confuso.

  1. Lo principal que debes entender es qué hace el decorador de @classmethod en la definición de as_view() . Esto significa que este método no es un método normal, que se llama en una instancia de la clase (y toma la instancia como el parámetro self ), sino un método de clase, que se llama en la clase en sí (y toma la clase como los cls parámetro). Algunos lenguajes se refieren a eso como un método estático, aunque en Python ese es un tercer tipo de método en el que no necesitamos entrar aquí.

  2. Esto se debe a cómo se define la vista en el urlconf. WelcomeView.as_view() correctamente WelcomeView.as_view() : lo que hace es llamar al as_view la as_view as_view en el momento en que se importa el urlconf .

  3. Como sabemos por el punto 1, cls es la clase de vista en sí misma. Como es normal con una clase, cuando la llamas, obtienes un objeto. Entonces, como dices, lo que estamos haciendo aquí es crear instancias de la clase, y luego asignar esa instancia a una variable llamada self , como si estuviéramos dentro de un método de esa instancia. El punto aquí es que, como dije anteriormente, se llama a as_view en el momento de la importación, y devuelve una función - view - que a su vez es llamada por el despachador de URL cuando un navegador solicita esa URL. Entonces, dentro de esa función, construimos y llamamos al resto de la clase que conforma la vista basada en la clase. En cuanto a por qué es necesario, mira a continuación.

  4. El método __init__ se ocupa de configurar cada miembro de initargs en un atributo de instancia, donde puede acceder a él en su código de vista a través de la sintaxis habitual de self.whatever .

Entonces, ¿por qué es todo esto necesario?

Las vistas basadas en clases vienen con un gran potencial, que es que cualquier clase instanciada directamente en el URLconf (o en cualquier otro lugar a nivel de módulo) persistirá durante toda la vida del proceso. Y la forma en que comúnmente Django se implementa, a través de WSGI, generalmente significa que un proceso puede durar muchas solicitudes. Y si tiene algo que persiste en varias solicitudes, tiene el potencial para algunos errores de seguridad de subprocesos realmente desagradables: si establece algo para un atributo de instancia en una solicitud, por ejemplo, será visible en solicitudes posteriores.

Por lo tanto, este código no solo garantiza que cada solicitud obtenga una nueva instancia, sino que también hace que sea realmente difícil romper el aislamiento de la solicitud mediante la construcción dinámica de la instancia cada vez dentro de la función de vista.

Estoy buceando en las vistas genéricas de Django, descubriendo cómo devuelven un objeto HttpResponse simple, como lo haría una simple función de vista.
Escribí un proyecto simple para probar y agregué algunos comandos de registro a la Vista básica clasificada definida en el archivo django / views / generic / base.py, para poder rastrear lo que sucede debajo del capó.

Tengo algunas preguntas que surgieron durante mi investigación.
He estado tratando de mantener esta publicación corta, sin embargo, para una comprensión completa, me pareció esencial incluir los fragmentos de código y los registros.
Estaré muy agradecido con cualquiera que se tome el tiempo de dar algunos comentarios útiles, posiblemente respondiendo algunas de mis preguntas.


urls.py

from django.conf.urls import patterns, url from views import WelcomeView urlpatterns = patterns('''', url(r''^welcome/(?P<name>/w+)/$'', WelcomeView.as_view()), )


views.py

from django.http import HttpResponse from django.views.generic import View class WelcomeView(View): def get(self, request, name): return HttpResponse(''What is up, {0}?''.format(name))


django / views / generic / base.py

class View(object): """ Intentionally simple parent class for all views. Only implements dispatch-by-method and simple sanity checking. """ http_method_names = [''get'', ''post'', ''put'', ''delete'', ''head'', ''options'', ''trace''] def __init__(self, **kwargs): #####logging logging.error(''*** View.__init__ is started with kwargs: {0} ***''.format( repr(kwargs))) """ Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things. """ # Go through keyword arguments, and either save their values to our # instance, or raise an error. for key, value in kwargs.iteritems(): setattr(self, key, value) #####logging logging.error(''*** View.__init__ reached its end. No return value. ***'') @classonlymethod def as_view(cls, **initkwargs): #####logging logging.error(''*** View.as_view is started with initkwargs: {0} ***''.format( repr(initkwargs))) """ Main entry point for a request-response process. """ # sanitize keyword arguments for key in initkwargs: if key in cls.http_method_names: raise TypeError(u"You tried to pass in the %s method name as a " u"keyword argument to %s(). Don''t do that." % (key, cls.__name__)) if not hasattr(cls, key): raise TypeError(u"%s() received an invalid keyword %r" % ( cls.__name__, key)) def view(request, *args, **kwargs): #####logging logging.error(''*** View.as_view.view is called with args: {0};/ and kwargs: {1} ***''.format( repr(args), repr(kwargs))) self = cls(**initkwargs) if hasattr(self, ''get'') and not hasattr(self, ''head''): self.head = self.get #####logging logging.error(''*** View.as_view.view reached its end./ Now calls dispatch() and returns the return value.'') return self.dispatch(request, *args, **kwargs) # take name and docstring from class update_wrapper(view, cls, updated=()) # and possible attributes set by decorators # like csrf_exempt from dispatch update_wrapper(view, cls.dispatch, assigned=()) #####logging logging.error(''*** View.as_view reached its end. Now returns view. ***'') return view def dispatch(self, request, *args, **kwargs): # Try to dispatch to the right method; if a method doesn''t exist, # defer to the error handler. Also defer to the error handler if the # request method isn''t on the approved list. #####logging logging.error(''*** View.dispatch called, with args: {0};/ and kwargs: {1} ***''.format( repr(args), repr(kwargs))) if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed self.request = request self.args = args self.kwargs = kwargs #####logging logging.error(''*** View.dispatch reached its end./ Now calls handler and returns the return value. ***'') return handler(request, *args, **kwargs) def http_method_not_allowed(self, request, *args, **kwargs): allowed_methods = [m for m in self.http_method_names if hasattr(self, m)] logger.warning(''Method Not Allowed (%s): %s'', request.method, request.path, extra={ ''status_code'': 405, ''request'': self.request } ) return http.HttpResponseNotAllowed(allowed_methods)


Registros de algunas solicitudes de prueba

Django version 1.4.5, using settings ''try1.settings'' Development server is running at http://127.0.0.1:8000/ Quit the server with CONTROL-C. ERROR:root:*** View.as_view is started with initkwargs: {} *** ERROR:root:*** View.as_view reached its end. Now returns view. *** ERROR:root:*** View.as_view.view is called with args: (); and kwargs: {''name'': u''Dude''} *** ERROR:root:*** View.__init__ is started with kwargs: {} *** ERROR:root:*** View.__init__ reached its end. No return value. *** ERROR:root:*** View.as_view.view reached its end. Now calls dispatch() and returns the return value. ERROR:root:*** View.dispatch called, with args: (); and kwargs: {''name'': u''Dude''} *** ERROR:root:*** View.dispatch reached its end. Now calls handler and returns the return value. *** [24/Feb/2013 12:43:19] "GET /welcome/Dude/ HTTP/1.1" 200 17 ERROR:root:*** View.as_view.view is called with args: (); and kwargs: {''name'': u''Dude''} *** ERROR:root:*** View.__init__ is started with kwargs: {} *** ERROR:root:*** View.__init__ reached its end. No return value. *** ERROR:root:*** View.as_view.view reached its end. Now calls dispatch() and returns the return value. ERROR:root:*** View.dispatch called, with args: (); and kwargs: {''name'': u''Dude''} *** ERROR:root:*** View.dispatch reached its end. Now calls handler and returns the return value. *** [24/Feb/2013 12:43:32] "GET /welcome/Dude/ HTTP/1.1" 200 17 [24/Feb/2013 12:44:43] "GET /welcome/ HTTP/1.1" 404 1939 ERROR:root:*** View.as_view.view is called with args: (); and kwargs: {''name'': u''Bro''} *** ERROR:root:*** View.__init__ is started with kwargs: {} *** ERROR:root:*** View.__init__ reached its end. No return value. *** ERROR:root:*** View.as_view.view reached its end. Now calls dispatch() and returns the return value. ERROR:root:*** View.dispatch called, with args: (); and kwargs: {''name'': u''Bro''} *** ERROR:root:*** View.dispatch reached its end. Now calls handler and returns the return value. *** [24/Feb/2013 12:44:59] "GET /welcome/Bro/ HTTP/1.1" 200 16


Después de todo, mis preguntas

1.
Según los registros, se llama a as_view antes de View.init.
¿Significa que llama a un método de Vista incluso antes de crear una instancia de Vista?

2.
¿Por qué no se llama a as_view () después de que se ejecuta para la primera llamada?
Todavía no soy un experto en importación, compilación y uso de memoria de Python,
pero tengo la sensación de que juegan un papel aquí.

3.
En la definición de view (), ¿qué hace el siguiente fragmento?

self = cls(**initkwargs)

Según los registros, activa View.init.
¿Es que crea una nueva instancia de View con initkwargs y la asigna a la instancia en uso (self)?
Si es así, ¿por qué es necesario?

4.
¿Cómo podemos hacer uso de initkwargs (argumentos de as_view)?


1. Primero as_view () es un método de clase . Este es un método que puede invocarse en una clase en lugar de una instancia de una clase. Y en este caso, puede ver que se está llamando a View , que es una clase en lugar de una instancia.

2. se as_view() cuando se carga el módulo url.conf - devuelve la view() función view() . Es esta función la que se as_view cada vez que se solicita una vista, as_view no es necesario volver a llamar a as_view .

3. En el ámbito de la función view() , la variable cls es la clase View (por ejemplo, DetailView , ListView o cualquier elemento secundario de View que llame a la función). Refiriéndose al primer argumento de un método de clase como cls trata de una especificación de estilo de codificación de PEP8 . Es similar a la forma en que nos referimos al primer argumento del método am instance como self. Asi que

self = cls(**initkwargs)

es básicamente lo mismo que

self = View(**initkwargs) o self = DetailView(**initkwargs)

(dependiendo de qué clase hereda esta función).

Esto es, como dices, instanciando una nueva instancia de la clase. Hasta este punto, un objeto View aún no se ha instanciado.

4. Por último, los initkwargs se usan cuando se crea una instancia de la clase. Es tan simple como agregar cada clave, par de valores como atributos del nuevo objeto de vista -

for key, value in kwargs.iteritems(): setattr(self, key, value)