Вызов другого представления в пирамиде

Моя цель: в Pyramid вызвать другой вызываемый вид и вернуть объект Response, не зная никаких подробностей об этом вызываемом представлении.

В моем приложении Pyramid, скажем, у меня есть представление "foo", которое определено с помощью декоратора view_config:

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

Теперь предположим, что я хочу перенаправить «bar» в представление, которое на данный момент делает то же самое, поэтому оно внутренне вызывает foo_view и возвращает свой ответ:

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

...но ждать! Это не работает, поскольку foo_view возвращает не Response, а его рендерер.

Итак, это будет работать:

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

поскольку он будет применять тот же рендерер, что и foo_view. Но это плохо, так как теперь я должен повторить себя, скопировав значение средства визуализации И зная средство визуализации вызываемого представления.

Итак, я буду надеяться, что в Pyramid есть какая-то функция, которая позволяет вызывать другой вызываемый вид и возвращать объект Response, не зная и не заботясь о том, как он был отрендерен:

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

Каким будет some_function_that_renders_a_view_callable?

pyramid.views.render_view появляется для поиска представления по имени; Я не хочу давать названия своим взглядам.

(Примечание. Я пытаюсь избежать возврата HTTPFound, чтобы клиент перенаправлялся на целевой маршрут. Я хочу «внутренне» перенаправить).


person kes    schedule 13.08.2011    source источник


Ответы (6)


Ага. Есть некоторые опасения

  • не возвращает ответ
  • предикаты/рендерер
  • разрешения
  • свойства запроса, связанные со старым запросом

Вот почему вы не должны вызывать представление из представления как функцию, если вы не знаете, что делаете.

Создатели пирамиды сделали отличный инструмент для перенаправления на стороне сервера - http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/subrequest.html

person enomad    schedule 15.06.2013

Вы можете вызвать представление с помощью 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()`

Когда /view_one просматривается в браузере, текст, напечатанный в панели браузера, будет "Это пришло из view_two". Представление view_one использовало pyramid.request.Request.invoke_subrequest() API для получения ответа от другого представления (view_two) в том же приложении при его выполнении. Он сделал это, создав новый запрос с URL-адресом, который, как было известно, будет соответствовать регистрации представления view_two, и передал этот новый запрос pyramid.request.Request.invoke_subrequest(). Был вызван вызываемый объект представления view_two, и он вернул ответ. Затем вызываемый объект представления view_one просто возвращал ответ, полученный от вызываемого объекта представления view_two.

person Anouar Mokhtari    schedule 29.05.2015

Я тоже боролся с этим. У меня есть решение, использующее метод render_to_response, хотя я уверен, что есть "более правильный" способ сделать это. Однако, пока кто-то не опубликует это, вот как я справился с этим:

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)

(Пирамида 1.3)

Это требует использования модуля визуализации, но, объявив этот модуль визуализации в возвращаемых значениях исходного представления, вы можете получить его в другом представлении, не зная, что это такое. Я подозреваю, что необходимость сделать это нелегко найти, потому что есть другие, лучшие методы для выполнения задач, решаемых этим решением.

Другой недостаток заключается в том, что он полагается на прямой импорт вызываемого представления. Было бы неплохо, если бы его можно было искать прямо по маршруту.

person chris    schedule 19.08.2012

Документация по пирамиде здесь указывает, что оставление аргумента ключевого слова name вне view_config приведет к тому, что представление будет зарегистрировано самой функцией (а не строкой):

Такая регистрация... подразумевает, что имя представления будет *my_view*

Итак, в вашем случае вы должны иметь возможность использовать pyramid.view.render_view или pyramid.view.render_view_to_response напрямую, ссылаясь на foo_view:

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

Обновление:

Да, вы правы, передача функции просмотра не работает.

Интересно, но взять код вашего примера и применить route_name к конфигу у меня не сработало. Однако в следующем примере просто присваивая представлению name, задается URL-адрес маршрута и присваивается имя представлению. В этом моде render_view_to_response работает так, как рекламируется. Назовите, ваши представления могут быть не такими, как вы хотите, но эта конфигурация выполняет то же самое, что и ваш пример кода, без дополнительной настройки.

@view_config(name="foo")
def foo_view(request):
    # returning a response here, in lieu of having
    # declared a renderer to delegate to...
    return Response('Where am i? `{0[whereami]}'.format({"whereami" : "foo!"}))

@view_config(name="bar")
def bar_view(request):
    # handles the response if bar_view has a renderer 
    return render_view_to_response(None, request, name='foo')

@view_config(name="baz")
def baz_view(request):
    # presumably this would not work if foo_view was
    # not returning a Response object directly, as it
    # skips over the rendering part. I think you would
    # have to declare a renderer on this view in that case.
    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')
person Mark Gemmill    schedule 13.08.2011
comment
Это приводит к ошибке ValueError: Не удалось преобразовать возвращаемое представление None в объект ответа. Есть ли что-то еще, что мне нужно сделать с целевым представлением, чтобы это сработало? - person kes; 13.08.2011

вы не можете сделать что-то вроде этого:

@view_config(name="baz") def baz_view(request): return HTTPFound(location=self.request.route_path('foo'))

person Mathieu Leduc-Hamel    schedule 16.01.2012
comment
Да и нет — перенаправление HTTP в два раза больше, чем в оба конца, и это может сбить пользователя с толку, если он заметит, что возвращаемая страница находится по другому URL-адресу, чем тот, который он запросил. Последнее становится более важным, когда исходный URL-адрес, о котором мы говорим, является домашней страницей '/'. - person Jerry; 19.03.2012
comment
Это отличный способ сделать перенаправление на представление без использования URL-адреса и, IMO, предпочтительнее внутреннего вызова других представлений. - person Javier; 15.05.2014

Не точное решение, которое вы просили, а решение проблемы, которую вы описываете:

Создайте класс представления, в котором и foo, и bar являются методами. Тогда бар может вызвать self.foo()

К классу можно применить общую конфигурацию view_configuration, такую ​​как имя шаблона, а затем вы можете украсить каждый метод только именем представления.

Короче говоря, следующее должно удовлетворить ваши потребности, если я правильно понимаю проблему.

@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()
person mac01021    schedule 07.02.2013
comment
конечно, это могло не быть вариантом, когда вопрос был задан. Я не уверен, когда вышла пирамида 1.3. - person mac01021; 08.02.2013
comment
Моя цель: в Pyramid вызвать другой вызываемый вид и вернуть объект Response, не зная никаких подробностей об этом вызываемом представлении. Конечно, нужно разместить другой вид. -callable в конкретном классе прямо противоположно незнанию каких-либо подробностей об этом view-callable. - person Piotr Dobrogost; 23.10.2015
comment
Почти уверен, что это не работает. Вот что произошло, когда я попробовал: ValueError: Не удалось преобразовать возвращаемое значение вызываемого метода представления foo_view класса example.views.ExampleViews в объект ответа. Возвращенное значение было {'whereami': 'foo!'}. Возможно, вы забыли определить средство визуализации в конфигурации представления. - person jmercouris; 25.10.2015