Подтвердить что ты не робот

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

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

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

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

Теперь скажите, что я хочу направить "бар" на представление, которое делает то же самое на данный момент, поэтому он внутренне вызывает 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. Но это плохо, так как теперь я должен повторить себя, скопировав значение renderer и должен знать рендерер вызываемого вида.

Итак, я надеюсь, что в 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, чтобы заставить клиента перенаправлять на целевой маршрут, я пытаюсь избежать. Я хочу "внутренне" перенаправить).

4b9b3361

Ответ 1

Угу. Есть некоторые проблемы

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

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

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

Ответ 2

Я тоже боролся с этим. У меня есть решение, используя метод 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)

(Pyramid 1.3)

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

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

Ответ 3

Документация Pyramid здесь означает, что оставить аргумент ключевого слова 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)

Update:

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

Это интересно, но взяв ваш пример кода и применив route_name к настройке не работал у меня. Однако в следующем примере, просто давая представление a 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')

Ответ 4

Вы можете вызвать представление с помощью 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использовал API pyramid.request.Request.invoke_subrequest() для получения ответ от другого представления (view_two) в том же приложении когда он выполняется. Он сделал это, построив новый запрос, который URL, который, как он знал, будет соответствовать регистрации просмотра view_two и передал этот новый запрос pyramid.request.Request.invoke_subrequest(). Представление view_twoвызываемый вызываемый, и он ответил на ответ. Представление view_oneзатем просто возвращает ответ, полученный от view_two просмотреть вызываемый.

Ответ 5

Вы не можете сделать что-то подобное:

@view_config (name= "Баз" ) def baz_view (запрос):   return HTTPFound (location = self.request.route_path ('foo'))

Ответ 6

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

Создайте класс представления, из которых как 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()