Передача сеанса из представления шаблона в запросы Python API-вызов

python django

1285 просмотра

3 ответа

176001 Репутация автора

Я хочу сделать несколько внутренних вызовов REST API из моего Django TemplateView, используя библиотеку запросов. Теперь я хочу также передать сессию из представления шаблона в вызов API. Каков рекомендуемый способ сделать это, имея в виду производительность.

Прямо сейчас я извлекаю cookieиз текущего requestобъекта в представлении шаблона и передаю это requests.get()или requests.post()вызываю. Но проблема в том, что я должен был бы передать requestобъект моему клиенту API, который мне не нужен.

Это текущая оболочка, которую я использую для маршрутизации моих запросов:

def wrap_internal_api_call(request, requests_api, uri, data=None, params=None, cookies=None, is_json=False, files=None):
    headers = {'referer': request.META.get('HTTP_REFERER')}
    logger.debug('Request API: %s calling URL: %s', requests_api, uri)
    logger.debug('Referer header sent with requests: %s', headers['referer'])
    if cookies:
        csrf_token = cookies.get('csrftoken', None)
    else:
        csrf_token = request.COOKIES.get('csrftoken', None)

    if csrf_token:
        headers['X-CSRFToken'] = csrf_token
    if data:
        if is_json:
            return requests_api(uri, json=data, params=params, cookies=cookies if cookies else request.COOKIES, headers=headers)
        elif not files:
            return requests_api(uri, data=data, params=params, cookies=cookies if cookies else request.COOKIES, headers=headers)
        else:
            return requests_api(uri, data=data, files=files, params=params, cookies=cookies if cookies else request.COOKIES,
                                headers=headers)
    else:
        return requests_api(uri, params=params, cookies=cookies if cookies else request.COOKIES, headers=headers)

По сути, я хочу избавиться от этого requestпараметра (1-й параметр), потому что для его вызова я должен продолжать передавать requestобъект из TemplateViews во внутренние службы. Кроме того, как я могу сохранить постоянное соединение между несколькими вызовами?

Автор: Rohit Jain Источник Размещён: 18.07.2016 05:28

Ответы (3)


1 плюс

2524 Репутация автора

Если вы хотите , чтобы избежать прохождения requestк wrap_internal_api_call, все , что вам нужно сделать , это сделать немного больше работы на конце TemplateView , где вы называете апи обертку. Обратите внимание, что ваша оригинальная оболочка делает много cookies if cookies else request.COOKIES. Вы можете учесть это на вызывающем сайте. Перепишите свою обертку API следующим образом:

def wrap_internal_api_call(referer, requests_api, uri, data=None, params=None, cookies, is_json=False, files=None):
    headers = {'referer': referer}
    logger.debug('Request API: %s calling URL: %s', requests_api, uri)
    logger.debug('Referer header sent with requests: %s', referer)
    csrf_token = cookies.get('csrftoken', None)

    if csrf_token:
        headers['X-CSRFToken'] = csrf_token
    if data:
        if is_json:
            return requests_api(uri, json=data, params=params, cookies=cookies, headers=headers)
        elif not files:
            return requests_api(uri, data=data, params=params, cookies=cookies, headers=headers)
        else:
            return requests_api(uri, data=data, files=files, params=params, cookies=cookies, headers=headers)
    else:
        return requests_api(uri, params=params, cookies=cookies, headers=headers)

Теперь на месте вызова вместо

wrap_internal_api_call(request, requests_api, uri, data, params, cookies, is_json, files)

делать:

cookies_param = cookies or request.COOKIES
referer_param = request.META.get['HTTP_REFERER']
wrap_internal_api_call(referer_param, requests_api, uri, data, params, cookies_param, is_json, files)

Теперь вы больше не передаете requestобъект оболочке. Это экономит немного времени, потому что вы не тестируете cookiesснова и снова, но в остальном это не влияет на производительность. Фактически, вы можете добиться такого же небольшого прироста производительности, просто выполнив cookies or request.COOKIESодин раз внутри обертки API.

Сеть всегда является самым узким местом в любом приложении. Поэтому, если эти внутренние API-интерфейсы находятся на том же компьютере, что и TemplateView, лучшим вариантом для повышения производительности является избегание вызова API.

Автор: Julian Размещён: 29.07.2016 01:34

4 плюса

43203 Репутация автора

Решение

REST vs вызывая вид напрямую

Хотя веб-приложение может сделать вызов API REST самому себе. Это не то, для чего предназначен REST. Обратите внимание на следующее: https://docs.djangoproject.com/ja/1.9/topics/http/middleware/

Жизненный цикл ответа на запрос Django

Как вы можете видеть цикл запроса / ответа django имеет довольно много накладных расходов. Добавьте к этому накладные расходы веб-сервера и контейнера wsgi. На стороне клиента у вас есть накладные расходы, связанные с библиотекой запросов, но подождите секунду, клиент также окажется тем же веб-приложением, поэтому он также станет частью накладных расходов веб-приложения. И есть проблема постоянства (к которой я скоро вернусь).

И последнее, но не менее важное: если у вас настроена циклическая перестановка DNS, ваш запрос может быть передан по сети, прежде чем вернуться на тот же сервер. Есть лучший способ, чтобы вызвать представление напрямую.

Вызвать другое представление без вызова API остальных очень просто.

 other_app.other_view(request, **kwargs)

Это обсуждалось несколько раз здесь по ссылкам, таким как представление на основе классов вызовов Django из другого представления на основе классов, и могу ли я вызвать представление из другого представления? поэтому я не буду уточнять.

Постоянные запросы

Постоянные http-запросы (речь идет не о django.http.request.HttpRequest, а о python-запросах ) управляются с помощью объектов сеанса (опять же, чтобы не путать их с сеансами django). Избежать путаницы действительно сложно:

Объект Session позволяет сохранять определенные параметры в запросах. Он также сохраняет файлы cookie во всех запросах, сделанных из экземпляра Session, и будет использовать пул соединений urllib3. Поэтому, если вы делаете несколько запросов к одному и тому же хосту, базовое TCP-соединение будет использоваться повторно, что может привести к значительному увеличению производительности.

Разные попадания в ваше представление django, вероятно, будут от разных пользователей, поэтому вы не хотите, чтобы один и тот же файл cookie повторно использовался для внутреннего вызова REST. Другая проблема заключается в том, что объект сеанса python не может быть сохранен между двумя различными попаданиями в представление django. Сокеты, как правило, не могут быть сериализованы, это требование для их размещения в memcached или redis.

Если вы все еще хотите сохранить с внутренним REST

Я думаю, что ответ @julian показывает, как избежать передачи экземпляра запроса django в качестве параметра.

Автор: e4c5 Размещён: 29.07.2016 12:02

-1 плюса

1364 Репутация автора

По сути, я хочу избавиться от этого параметра запроса (1-й параметр), потому что затем, чтобы вызвать его, я должен продолжать передавать объект запроса из TemplateViews во внутренние службы.

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

request_decorators.py

REQUEST = None


def request_extractor(func):

    def extractor(cls, request, *args, **kwargs):
        global REQUEST
        REQUEST = request # this part registers request arg to global
        return func(cls, request, *args, **kwargs) 

    return extractor


def request_injector(func):

    def injector(*args, **kwargs):
        global REQUEST
        request = REQUEST
        if len(args) > 0 and callable(args[0]): # to make it work with class methods
            return func(args[0], request, args[1:], **kwargs) # class method
        return func(request, *args, **kwargs) # function

    return injector

extract_request_middleware.py

См. Django docs для информации о настройке промежуточного программного обеспечения.

from request_decorators import request_extractor

class ExtractRequest:

    @request_extractor
    def process_request(self, request):
        return None

internal_function.py

from request_decorators import request_injector

@request_injector
def internal_function(request):
    return request

your_view.py

from internal_function import internal_function

def view_with_request(request):
    return internal_function() # here we don't need to pass in the request arg.

def run_test():

    request = "a request!"
    ExtractRequest().process_request(request)
    response = view_with_request(request)
    return response


if __name__ == '__main__':

    assert run_test() == "a request!"
Автор: Mattew Whitt Размещён: 29.07.2016 04:20
Вопросы из категории :
32x32