2010-11-20 18 views
11

He estado trabajando con Django desde hace un tiempo (actualmente en la versión 1.2), pero recientemente comencé a trabajar en una aplicación que necesita admitir varias instancias. Por ejemplo, el archivo de proyecto urls.py incluirá dos veces, en dos espacios de nombres diferentes, así:Invertir URL con espacios de nombres en Django: varias instancias de la misma aplicación

urlpatterns = patterns('', 
    (r'^instance1/', include('myapp.urls', namespace='instance1')), 
    (r'^instance2/', include('myapp.urls', namespace='instance2')), 
) 

que iba a lo largo de bien, hasta que me di cuenta que tenía que averiguar qué hacer con todas las llamadas internas a reverse() (o la plantilla llama al filtro {% url %}). Por ejemplo, digamos que estoy haciendo algo como lo siguiente en uno de mis puntos de vista:

return HttpResponseRedirect(reverse('view_name')) 

o algo así en una de mis plantillas:

<a href="{% url view_name %}">link text</a> 

... view_name donde es el nombre de un patrón de URL contenido en myapp.urls. Dado que estoy usando espacios de nombres, esto generará un error: no hay una vista llamada view_name. Por el contrario, tengo que decir instance1:view_name o instance2:view_name. Pero hacer esto dinámicamente me está golpeando.

he hecho un poco mirando y parece que el argumento current_app, se pasa a cualquiera Context o RequestContext, fue diseñado para ayudar con esto, pero no está claro en absoluto cómo pasar de forma dinámica el nombre de la aplicación derecho a current_app. Entonces, ¿cuál es la forma correcta de decirle a Django qué espacio de nombres usar?

EDITAR: Mi caso de uso es utilizar una única instalación de la aplicación varias veces. Es decir, solo existe en el disco una vez, pero se incluye varias veces en la raíz del proyecto urls.py (cada vez bajo un espacio de nombres diferente, como en mi ejemplo anterior). Teniendo esto en cuenta, ¿hay alguna forma de hacer un seguimiento del espacio de nombres desde el que se llama a una vista/plantilla, y hacer uso de reverse() o {% url %} dentro del mismo espacio de nombres? Sé que Django 1.3 proporcionará algunas características adicionales que podrían ayudar con esto (es decir, el new and improved resolve()), pero seguramente hay una buena manera de hacer esto ahora ...

+1

Mi respuesta a http://stackoverflow.com/questions/2030225/how-to-get-current-app-for-using-with-reverse-in-multi-deployable-reusable-djang/13249060#13249060 se aplica aquí también. – stefanfoulis

Respuesta

3

No es una muy buena solución, pero ya que utiliza el mismo texto para el espacio de nombres y la parte inicial de la Ruta URL, puede extraer ese elemento del request.path (request.path.split('/')[1]) y configurarlo como current_app en el contexto de la solicitud, o simplemente usarlo como espacio de nombres en las vistas.

http://docs.djangoproject.com/en/dev/topics/http/urls/#url-namespaces punto 2.

que podría hacer que, por ejemplo, en un procesador de contexto (si desea usar el espacio de nombres en una plantilla).

Para las vistas que podría escribir un decorador que alimenta su función de una kwarg extra "espacio de nombres" y utilizarlo como:

@feed_namespace 
def view1(request, *args, **kwargs): 
    ns = kwargs['namespace'] 

o simplemente escribir una función reverse_namespaced con un parámetro adicional (la solicitud) donde la función obtiene el espacio de nombres de, y lo usa en lugar de invertir.

Por supuesto, si usted hace esto siempre se tendrá que utilizar una solicitud de surco/espacio de nombres para esta aplicación

+0

Gracias, la primera idea (usar parte de la ruta para obtener el espacio de nombres) es lo que Terminé haciendo. Si necesito hacer esto de nuevo antes de que salga 1.3, tus otras ideas podrían ser muy útiles. – mjjohnson

2

Hay una página de doc sobre la inversión de URL con espacios de nombres.

http://docs.djangoproject.com/en/dev/topics/http/urls/#topics-http-reversing-url-namespaces

De cualquier reverse('instance1:myapp.urls.some_view') o reverse('instance1:view_name') debería funcionar, o ambos :) - Nunca he probado esto por mí mismo.

+4

Esos funcionarían, es cierto, pero eso sería difícil de codificar para que la aplicación funcione solo con valores específicos para el espacio de nombres. Quiero que a la aplicación no le importe el espacio de nombre asignado en el archivo urls.py principal del usuario: debe usar automáticamente el de "derecha", ajustándolo según el espacio de nombre desde el que se invocó la vista (o en el que se muestra la plantilla) . – mjjohnson

2

La variable current_app es algo que tienes que fijarte en algo que te guste.

Personalmente recomendaría configurarlo como __name__.rsplit('.', 1)[0] para obtener spam en spam/views.py.

Pero puede definir que sea lo que quiera, siempre y cuando el nombre de su aplicación sea coherente con lo que defina en su archivo de URL.

+0

Hm, tal vez mi mención de current_app es una pista falsa. Suponiendo que estoy usando la misma aplicación (el mismo archivo de views.py, etc.), pero importando el mismo archivo de la aplicación/urls.py dos veces (dos diferentes incluyen en el archivo urls.py del proyecto, en dos espacios de nombres, como en mi ejemplo anterior), ¿hay alguna forma de obtener automáticamente mis llamadas 'reverse()' y '{% url%}' para que se queden con el mismo espacio de nombres desde el que se llaman? – mjjohnson

+0

@peppergrower: no tan lejos como sé. He optado por agregar el espacio de nombres a las delcaraciones 'reverse' y' {% url%} '. Guarda un pequeño problema. Sería trivial para envolver 'render_to_response' y' reverse' con una versión que incluye a usted 'current_app' embargo. Algo como esto podría ser suficiente: 'reverse = lambda * a, ** kw: original_reverse (current_app = YOUR_CURRENT_APP, * a, ** kw)' – Wolph

1

Si usted no está demasiado atado a espacios de nombres, en su lugar podría escribir su urls.py sea como:

urlpatterns = patterns('', 
    (r'^(P<instance>/)', 'controllers.route_me'), 
) 

Esto hará una llamada a los controladores.función route_me y pasarlo la solicitud, además de la cadena 'instancia', que se podría manejar de esta manera:

# (in controllers.py) 
def route_me(request, instance): 
    # you now have complete control, and do what you need to do with the 'instance' and 'request' vars 
    pass 
4

Muchas cosas han cambiado desde que la cuestión se publicó, pero para los futuros empleados de Google (como yo) que podría ser útil para señalar que request tiene ahora el espacio de nombres (por lo menos desde 1,7 como se muestra en this example.

por lo que entendí que debemos ser capaces de pasar simplemente current_app argumento posicional a reverse/redirect pero no pude conseguir que funcione de manera Terminé creando un método de ayuda para este propósito:

def __redirect(request, viewname, *args, **kwargs): 
    to = viewname 

    if callable(to): 
     to = viewname.__module__ + '.' + viewname.__name__ 
    if request.resolver_match.namespace: 
     to = '%s:%s' % (request.resolver_match.namespace, to) 

    return redirect(
     to, 
     *args, 
     **kwargs 
    ) 

Estoy usando redirect aquí, pero todos los argumentos se transmiten a reverse, por lo que es lo mismo.

Cuestiones relacionadas