Мобильные шаблоны на основе пользовательского агента в django, обеспечивающие безопасность потоков

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

Затем я создал промежуточное ПО, которое читает этот заголовок и изменяет каталог шаблонов в файле настроек. Сначала казалось, что это работает, но потом я понял, что происходит состояние гонки, поскольку этот метод не является потокобезопасным. (Я должен был подумать об этом заранее).

Поэтому я начал думать о других альтернативах. Одним из решений было перезаписать метод рендеринга django, чтобы включить параметр «dirs» на основе заголовка запроса. Но потом я узнал, что параметр "dirs" устарел. Ниже приведена справочная ссылка https://docs.djangoproject.com/en/1.9/_modules/django/shortcuts/#render Так что даже это не сработает.

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

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

Любые советы о том, как этого достичь, будут полезны.

Вот как организованы мои шаблоны.

App1
   templates
       App1
           index.html
           catalog.html
App2
   templates
       App2
           about.html

И в каталоге проекта (не в папке приложения) есть папка мобильных шаблонов, которая имеет следующую структуру.

mobile-templates
    App1
        index.html
    App2
        about.html

Спасибо, Анураг.


person Anurag    schedule 08.03.2016    source источник
comment
Как насчет изменения объекта request, чтобы добавить имя каталога? Затем render может отобразить файл, присоединив путь к этой request.template_dir переменной.   -  person sudshekhar    schedule 10.03.2016
comment
@SudhanshuShekhar - Даже если я изменю объект запроса, как функция рендеринга автоматически примет имя каталога? Я также проверил исходный код и не обнаружил, что request.template_dir используется функцией рендеринга. Не знаю, может я что-то пропустил, поясните пожалуйста. Если вы хотите предварительно добавить имя каталога в мою функцию рендеринга, то вернуться к веб-версии не получится.   -  person Anurag    schedule 11.03.2016
comment
Я думал о чем-то вроде: render(request, ["app/template/t_mobile.html", "app/template/t.html"], context). Вы можете использовать request.template_dir, чтобы решить, добавлять ли t_mobile.html в качестве опции или нет.   -  person sudshekhar    schedule 11.03.2016
comment
Я уже пробовал это, но это не сработает, пока я не укажу каталог мобильных шаблонов в файле настроек. И как только я это сделаю, порядок шаблонов, которые я указываю в функции рендеринга, больше не имеет значения.   -  person Anurag    schedule 11.03.2016


Ответы (2)


Вот как я бы организовал свои шаблоны:

  1. Создайте два каталога внутри templates dir - mobile и desktop.
  2. Храните шаблоны для мобильных устройств в каталоге mobile, а шаблоны для настольных компьютеров — в каталоге desktop.

Таким образом, вам не придется переименовывать шаблоны.


И вот как я бы их отобразил:

  1. Чтение User-Agent в промежуточном программном обеспечении.

  2. Установите атрибут request с именем template_prefix, значение которого будет либо mobile, либо desktop, в зависимости от User-Agent. Например:

    def process_request(self, request):
        # read user agent and determine if 
        # request is from mobile or desktop
        # ...
        if mobile_user_agent:
            request.template_prefix = 'mobile'
        else:
            request.template_prefix = 'desktop'
    
  3. В своих представлениях используйте request.template_prefix перед именами шаблонов. Например:

    def home(request):
        ...
        template = request.template_prefix + '/home.html'
        return render(request, template)
    

Это отобразит шаблоны из каталогов mobile или desktop в зависимости от значения атрибута template_prefix.


ОБНОВЛЕНИЕ (согласно редактированию вопроса):

Глядя на то, как организованы ваши шаблоны, я бы сделал следующее:

  1. Промежуточное ПО:

    Установите template_prefix только для мобильных запросов.

    def process_request(self, request):
        if mobile_user_agent:
            request.template_prefix = 'mobile-templates'
        else:
            request.template_prefix = '' # set an empty string
    
  2. Взгляды:

    Используйте os.path.join; поймать TemplateDoesNotExist исключение.

    import os.path
    from django.template.loader import get_template
    from django.template.base import TemplateDoesNotExist
    
    def index(request):
        try:
            template = os.path.join(request.template_prefix, 'App1/index.html')
            get_template(template)
        except TemplateDoesNotExist:
            template = 'App1/index.html'
    
        return render(request, template)
    

Я проверил это, и это работает. Но писать блок try...except в каждом представлении кажется излишним. Если я придумаю лучшее решение, я обновлю.

person xyres    schedule 12.03.2016
comment
Это вызовет ошибку для резервного случая, так как мобильных шаблонов будет очень мало по сравнению с настольными. И если мобильный шаблон не найден, он должен вернуться к шаблону рабочего стола. - person Anurag; 12.03.2016
comment
@anurag В этом случае вы можете использовать request.template_prefix только в тех представлениях, где вы хотите отображать разные шаблоны. Если для этого представления нет мобильного шаблона, вручную используйте префикс desktop. Обновил ответ. - person xyres; 12.03.2016
comment
Да, но проблема в том, что я не узнаю, существует шаблон или нет. Поэтому мне придется либо проверять наличие файла, либо изменять функцию рендеринга всякий раз, когда создается мобильный шаблон. Я могу добиться этого, не изменяя функцию рендеринга, используя каталоги параметров, но поскольку она устарела, я ищу правильный способ. Не могу понять, почему это устарело. - person Anurag; 12.03.2016
comment
спасибо @xyres, это должно сработать. Мне нужно протестировать его, так как моя текущая структура шаблона немного отличается. Вернется обратно после проверки. Я также обновил вопрос с моей текущей структурой шаблона. - person Anurag; 12.03.2016
comment
@anurag Начиная с Django 1.8, select_template работает по-другому. Поэтому это не будет работать, как я описал в своем ответе. Я постараюсь обновить. - person xyres; 12.03.2016
comment
Я немного изменил ваше решение, и оно работает для меня без необходимости помещать операторы try, кроме операторов. Я создал customRender, который вызывает функцию рендеринга внутри. В функции рендеринга я изменил путь к шаблону в зависимости от того, мобильный он или нет, и передал список шаблонов в соответствующем порядке в функцию рендеринга. return render(запрос, ['mobile/'+template_name, template_name],контекст) - person Anurag; 12.03.2016
comment
Вы можете использовать эту попытку catch в промежуточном программном обеспечении в process_response для отображения этого ненайденного шаблона. - person Snigdha Batra; 07.06.2017

Кажется, что это невозможно прямо сейчас из коробки. Если вы действительно хотите следовать этой архитектуре, вам придется написать свой собственный загрузчик, а также найти способ передать запрос/индикатор, чтобы он знал, что это мобильный запрос.

Написать загрузчик не так уж сложно (просто посмотрите загрузчик файловой системы Django, если запрос с мобильного устройства, прокрутите все templates_dirs и добавьте к нему правильный суффикс, чтобы вы также включили мобильные каталоги).

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

person sudshekhar    schedule 11.03.2016
comment
Дело не в том, чтобы следовать определенной архитектуре, это скорее постановка проблемы, для которой я ищу правильное решение. Я могу создать загрузчик, но, как вы сказали, передача динамического параметра является проблемой, поскольку методы загрузчика не получают доступа к объекту запроса. docs.djangoproject.com/en/1.9/ref/templates /api/#custom-loaders И изменение имени шаблона перед загрузкой шаблона, а затем его чтение и отмена сделанных ранее изменений кажется еще более запутанным. - person Anurag; 11.03.2016
comment
Похоже на общую проблему, не знаю, как люди ее решают - person Anurag; 11.03.2016