16 votes

Appeler une autre vue dans Pyramid

Mon objectif : Dans Pyramid, appeler un autre appel de vue, et obtenir un objet Response sans connaître aucun détail sur cet appel de vue.

Dans mon application Pyramid, disons que j'ai une vue "foo" qui est définie en utilisant un décorateur view_config :

@view_config(route_name="foo",
             renderer="foo.jinja2")
def foo_view(request):
    return {"oùsuisje" : "foo!"}

Disons maintenant que je veux router "bar" vers une vue qui fait la même chose pour le moment, donc elle appelle foo_view en interne et renvoie sa Response :

@view_config(route_name="bar")
def bar_view(request):
   return foo_view(request)

... mais attendez ! Cela ne fonctionne pas, car foo_view ne renvoie pas de Response, son rendu le fait.

Donc, cela fonctionnera :

@view_config(route_name="bar",
             renderer="foo.jinja2")
def bar_view(request):
    return foo_view(request)

car il appliquera le même rendu que foo_view l'a fait. Mais c'est mauvais, car maintenant je dois me répéter en copiant la valeur du rendu ET en ayant à connaître le rendu de la vue appelée.

Donc, j'espère qu'il y a une fonction disponible dans Pyramid qui permet d'appeler un autre appel de vue et d'obtenir un objet Response sans savoir ou se soucier de la façon dont il a été rendu :

@view_config(route_name="bar")
def bar_view(request):
    response = some_function_that_renders_a_view_callable(foo_view, request)
    return response

Quelle serait some_function_that_renders_a_view_callable ?

pyramid.views.render_view semble rechercher une vue par nom ; je ne veux pas donner de noms à mes vues.

(Remarque : Retourner HTTPFound pour amener le client à rediriger vers l'itinéraire cible est ce que j'essaie d'éviter. Je veux une redirection "interne").

10voto

enomad Points 753

Oui. Il y a certaines préoccupations

  • ne renvoie pas de réponse
  • prédicats/renderers
  • autorisations
  • propriétés de requête associées à l'ancienne requête

C'est pourquoi vous ne devriez pas appeler une vue à partir d'une vue en tant que fonction, sauf si vous savez ce que vous faites

Les créateurs de Pyramid ont créé un outil génial pour les redirections côté serveur - http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/subrequest.html

5voto

Vous pouvez invoquer une vue en utilisant request.invoke_subrequest:

from wsgiref.simple_server import make_server

from pyramid.config import Configurator

from pyramid.request import Request

def view_one(request):

    subreq = Request.blank('/view_two')
    response = request.invoke_subrequest(subreq)
    return response

def view_two(request):

    request.response.body = 'This came from view_two'
    return request.response

if __name__ == '__main__':

    config = Configurator()
    config.add_route('one', '/view_one')
    config.add_route('two', '/view_two')
    config.add_view(view_one, route_name='one')
    config.add_view(view_two, route_name='two')
    app = config.make_wsgi_app()
    server = make_server('0.0.0.0', 8080, app)
    server.serve_forever()`

Lorsque /view_one est visité dans un navigateur, le texte affiché dans le volet du navigateur sera "This came from view_two". La vue view_one a utilisé l'API pyramid.request.Request.invoke_subrequest() pour obtenir une réponse d'une autre vue (view_two) dans la même application lors de son exécution. Cela a été fait en construisant une nouvelle requête avec une URL qui correspondait à l'enregistrement de la vue view_two, puis en passant cette nouvelle requête à pyramid.request.Request.invoke_subrequest(). La vue callable view_two a été invoquée, et a renvoyé une réponse. La vue callable view_one a ensuite simplement renvoyé la réponse obtenue de la vue callable view_two.

4voto

chris Points 1225

J'ai également eu du mal avec ça. J'ai une solution en utilisant la méthode render_to_response, bien que je sois sûr qu'il y ait une "manière plus correcte" de le faire. Jusqu'à ce que quelqu'un le post, voici comment j'ai géré cela:

from pyramid.renderers import render_to_response

@view_config(route_name="foo", renderer="foo.mak")
def foo_view(request):
    return {'stuff':'things', '_renderer':'foo.mak')

def bar_view(request):
    values = foo_view(request)
    renderer = values['_renderer']
    return render_to_response(renderer,values)

(Pyramid 1.3)

Cela nécessite l'utilisation d'un rendu, mais en déclarant ce rendu dans les valeurs de retour de la vue originale, vous pouvez le récupérer dans une autre vue sans savoir ce que c'est. Je soupçonne que la nécessité de le faire n'est pas facilement trouvable car il existe d'autres méthodes meilleures pour accomplir les tâches résolues par cette solution.

Un autre inconvénient est qu'il repose sur l'importation directe de l'appel de vue. Ce serait bien s'il pouvait être recherché directement par l'itinéraire.

3voto

Mark Gemmill Points 2851

La documentation de Pyramid ici indique que si vous omettez l'argument mot-clé name de view_config, la vue sera enregistrée par la fonction elle-même (plutôt qu'une chaîne) :

Un tel enregistrement... implique que le nom de la vue sera *my_view*

Donc, dans votre cas, vous devriez pouvoir utiliser pyramid.view.render_view ou pyramid.view.render_view_to_response en référençant directement foo_view :

@view_config(route_name="bar")
def bar_view(request):
    return pyramid.views.render_view_to_response(None, request, name=foo_view)

Mise à jour :

Oui, vous avez raison, passer la fonction de la vue ne fonctionne pas.

C'est intéressant, mais en prenant votre exemple de code et en appliquant le route_name à la configuration n'a pas fonctionné pour moi. Cependant, l'exemple suivant, en donnant simplement à la vue un name définit l'url de la route et donne un nom à la vue. De cette manière, render_view_to_response fonctionne comme annoncé. Nommer, vos vues ne sont peut-être pas ce que vous voulez, mais cette configuration accomplit la même chose que votre exemple de code sans configuration supplémentaire.

@view_config(name="foo")
def foo_view(request):
    # retourne une réponse ici, au lieu d'avoir
    # déclaré un renderer à déléguer...
    return Response('Où suis-je ? `{0[whereami]}'.format({"whereami" : "foo!"}))

@view_config(name="bar")
def bar_view(request):
    # gère la réponse si bar_view a un renderer
    return render_view_to_response(None, request, name='foo')

@view_config(name="baz")
def baz_view(request):
    # apparemment cela ne fonctionnerait pas si foo_view ne retournait pas
    # directement un objet Response, car cela
    # sauterait la partie rendue. Je pense que vous devriez
    # déclarer un renderer sur cette vue dans ce cas.
    return foo_view(request)

if __name__ == '__main__':
    config = Configurator()
    config.scan()
    app = config.make_wsgi_app()
    serve(app, host='127.0.0.1', port='5000')

1 votes

Cela entraîne une "ValueError : Impossible de convertir la valeur de retour de la vue "None" en un objet de réponse." Y a-t-il autre chose que je dois faire à la vue cible pour que cela fonctionne ?

2voto

mac01021 Points 59

Pas la solution précise que vous demandez, mais une solution au problème que vous décrivez:

Créez une classe de vue, dont foo et bar sont des méthodes. Ensuite, bar peut appeler self.foo()

La configuration de vue commune, telle que le nom du modèle, peut être appliquée à la classe, puis vous pouvez décorer chaque méthode avec juste le nom de la vue.

En bref, ce qui suit devrait répondre à vos besoins, si je comprends correctement le problème.

@view_defaults(renderer="foo.jinja2")
class WhereaboutsAreFoo(object):

    @view_config(route-name="foo")
    def foo_view(self):
        return {"whereami" : "foo!"}

    @view_config(route-name="bar")
    def bar_view(self):
        return self.foo_view()

0 votes

Accordé, cela n'a peut-être pas été une option lorsque la question a été posée. Je ne suis pas sûr quand la pyramide 1.3 est sortie.

0 votes

Mon objectif : Dans Pyramid, appeler un autre callable de vue, et obtenir un objet Response sans connaître aucun détail sur ce callable de vue. Certainement devoir placer un autre callable de vue dans une classe spécifique va à l'encontre du fait de ne pas connaître aucun détail sur ce callable de vue.

0 votes

Je suis presque certain que cela ne fonctionne pas. Voici ce qui s'est passé lorsque j'ai essayé : ValueError: Impossible de convertir la valeur de retour de la méthode callable de la vue foo_view de la classe example.views.ExampleViews en un objet de réponse. La valeur retournée était {'whereami': 'foo!'}. Vous avez peut-être oublié de définir un moteur de rendu dans la configuration de la vue.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X