Как переписать URL для структуры папок

У меня есть приложение Django, и у меня есть следующие варианты использования:

  1. Местное развитие. Поскольку мое приложение в основном простое, я предпочитаю обычный manage.py runserver подход. Поэтому я ожидаю, что мое приложение будет работать для этого случая.
  2. Производственная среда. У нас есть один веб-сервер и хост-инструменты под разными URL-адресами, например https://myserver.com/tool1, https://myserver.com/tool2 и т. д. Поэтому я хотел бы разместить свое приложение в этой структуре.
  3. (Теоретически) Помимо варианта использования 2, я могу захотеть разместить приложение в собственном домене, например https://tool.myserver.com

Но когда я попытался сделать это, у меня возникла проблема со статическими файлами (вздох!), потому что если у меня STATIC_URL в качестве относительного пути STATIC_URL='static/', то это не работает для «вложенных» страниц (т.е. если я нахожусь на myserver.com/tool1/page, то статические URL-адрес будет отображаться на myserver.com/tool1/page/static, что неверно). С другой стороны, если я использую абсолютный путь STATIC_URL='/static/', то он вообще не работает для случая 2, потому что приложение Django ничего не знает о /tool1 части URL, где оно находится.

Я могу использовать два разных варианта для STATIC_URL в зависимости от среды и жесткого кода STATIC_URL='/tool1/static/', но тогда один и тот же код не будет работать для случая 3...

Как мне поступить в этой ситуации?

УПД

На самом деле я понял, что это больше похоже на общий вопрос nginx + backend, чем на вопрос Django. Потому что в конце дня веб-страница, созданная бэкэндом, скорее всего, будет иметь src = "/static/...." (если я не добавлю некоторые хаки в бэкэнд, чтобы вставить префикс /tool1). И мне интересно, как с этим обычно обращаются? Есть способ заменить фактический HTML-контент в nginx, но это сильно повлияет на производительность...

UPD2

Похоже, многие люди неправильно поняли мой вопрос, думая, что единственная проблема заключается в статических файлах. Однако это был всего лишь пример, потому что, как правильно заметил Иван, со ссылками та же проблема. Обычно на странице у меня есть ссылки вида a href = "/category/post?id=1". И, очевидно, когда сайт открывается как my.domain/tool1, это разрешается как my.domain/category..., что неверно (я хочу, чтобы он указывал на my.domain/tool1/category....)

Как вы решили это для всех других страниц / URL-адресов?

Ivan Starostin 11.05.2019 09:36

@Иван, что ты имеешь в виду?

The Godfather 11.05.2019 12:19

У вас есть гиперссылки в вашем веб-приложении? Как вы ссылаетесь на одну страницу с другой?

Ivan Starostin 11.05.2019 12:25

Поскольку мое приложение простое, у меня нет многоуровневых иерархических ссылок, у меня есть запрос на ту же страницу (например, href = "#", которая отлично работает). Но да, вы правы, такая же проблема у меня будет и со ссылками, не только со статическими

The Godfather 11.05.2019 12:54

где STATIC_URL='static/' установлен? Можете ли вы показать свой файл конфигурации или соответствующие части?

Bman70 09.07.2019 22:51

Неважно, это очень специфично для Django, мои серверы nginx не используют static_url. Этот вопрос может помочь: stackoverflow.com/questions/7307549/…

Bman70 09.07.2019 23:01
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
6
489
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Я рекомендую размещать ваши статические файлы в службе хранения, такой как AWS S3, Google Cloud Storage и т. д. Таким образом, он всегда доступен для любого URL-адреса, как у вас (вложенный, поддомен, что угодно)

Пример настройки (может потребоваться еще несколько шагов):

  • Измените свой settings.py:

    STATIC_URL = 'https://storage.googleapis.com/your_bucket/static/'

    STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")

    STATICFILES_DIRS = (os.path.join(BASE_DIR, 'static'),)

  • Перед развертыванием приложения запустите manage.py collectstatic --noinput

  • Скопируйте файлы из папки staticfiles в службу хранилища и убедитесь, что они общедоступны.
  • Разверните свое приложение

P.S. Я считаю, что whitenoise действительно помогает с размерами статических файлов и упрощает развертывание.

Использование облака здесь не помогает, потому что проблема не только в статических файлах (как указано в комментарии), но и в ссылках вообще. Также на каждом CDN есть примечание «не используйте CDN в качестве основного хранилища, используйте его только как зеркало», поэтому мне все равно нужно где-то хранить свои файлы (в том числе для локальной разработки, когда я изменяю статические)

The Godfather 09.07.2019 22:37

Решение работает для локальной (файлы загружаются из локальной папки), среды разработки и производственной среды. Преимущество службы «Хранение в хранилище» заключается в том, что она поддерживает несколько регионов (если хотите), повышает скорость загрузки веб-страницы и снижает нагрузку на ваш сервер приложений. Что касается других ссылок, я не понимаю, почему они не работают. Вы можете объяснить?

Yarh 10.07.2019 08:39

см. комментарий Ивана к самому вопросу. Поскольку у меня есть ссылки `a href='/my/folder/page?id=1', и это неправильно разрешается, когда приложение находится в папке

The Godfather 10.07.2019 10:41

Обработка приложения STATIC_URL from django может привести к накладным расходам. Поскольку у вас есть несколько подкаталогов, почему бы не обслуживать статические файлы только из одного из них? Нет необходимости сопоставлять подкаталог при обслуживании статических файлов.

Допустим, если вы установите STATIC_URL следующим образом:

STATIC_URL = "/tool1/static/"

Затем вы можете просто настроить NGINX следующим образом:

server {
    listen 80

    server_name *.myserver.com;

    location /tool1/static/ {
        root /path/to/STATIC_ROOT;
    }

    location / {
        proxy_pass http://localhost:8000;  # <-- No trailing slash to make it work with gunicorn
    }

    location /tool2/ {
        proxy_pass http://localhost:8000;
        proxy_set_header SCRIPT_NAME /tool2;  # <-- For serving in sub path
    }

    location /tool1/ {
        proxy_pass http://localhost:8000;
        proxy_set_header SCRIPT_NAME /tool1; # <-- For serving in sub path
    }

}

И в настройках обновите STATIC_URL на /tool1/static/. Также убедитесь, что ваш STATIC_ROOT указывает на правильный путь, по которому статические файлы будут находиться на сервере. Наконец, перед развертыванием статических файлов вам необходимо запустить collectstatic command, чтобы поместить статические файлы в каталог STATIC_ROOT, а затем перезапустить сервер NGINX.

Обновлять: не рекомендуется использовать жестко заданный URL. Вместо этого в шаблоне вы должны использовать тег {% URL '<url_name> '%} или reverse() в коде Python. Таким образом, django сам разрешит URL-адреса. Когда он находится в /tool1 месте, URL-адреса будут разрешены как /tool1/url_name/, а /tool2/url_name, когда вы находитесь в местоположении /tool2 (на основе SCRIPT_NAME).

Я правильно понял, что вы предлагаете хардкодить tool1 один раз в Django и два раза в Nginx? В чем преимущество этого вместо того, чтобы явно помещать его один раз на стороне nginx, а затем динамически использовать SCRIPT_NAME на резервной стороне? Кроме того, это также не помогает для ссылок на страницах, похожих на a href = "/main/page?id=1" — это неправильно разрешается, когда приложение находится в папке.

The Godfather 10.07.2019 10:45

При вызове URL-адресов вы не должны жестко кодировать его. вместо этого используйте тег {% URL%}, поэтому django решит его сам. Кроме того, как вы собираетесь динамически определять SCRIPT_NAME на стороне сервера? В django есть одна переменная с именем FORCE_SCRIPT_NAME, но она не динамическая. Но вам это не нужно, так как вы можете указать его в заголовке SCRIPT_NAME явно в конфигурации NGINX.

ruddra 10.07.2019 13:41

Из документа url: «Возвращает ссылку на абсолютный путь (URL-адрес без имени домена)». Так что он вернет мне абсолютно тот же URL href = "/my/page"

The Godfather 10.07.2019 14:53

Возможно нет. Из-за SCRIPT_NAME он должен настроить URL-адрес, например «/tool1/url/», если он находится в /tool1.

ruddra 10.07.2019 17:11

Звучит как недокументированная магия, вау. Но спасибо, что указали на это, Django действительно может автоматически обрабатывать заголовок SCRIPT_NAME, если он правильно перенаправлен/установлен. Я узнал что-то новое сегодня :)

The Godfather 12.07.2019 17:15
Ответ принят как подходящий

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

Всегда используйте один канонический адрес (лично предпочтительно)

Я должен спросить себя: «Почему вы хотите, чтобы ваш инструмент был доступен как в https://myserver.com/tool1, так и в https://tool1.myserver.com?». И на самом деле у меня нет причин, почему он должен быть доступен отдельно в то же время. Так что просто выберите «канонический» адрес и настройте правильные перенаправления:

Если домен канонический:

    server_name myserver.com
    location /tool1/ {
            rewrite ^/tool1/?(.*)$ https://tool1.myserver.com/$1 permanent;
    }

Если папка каноническая:

    server_name tool1.myserver.com
    location / {
            rewrite ^ https://myserver.com/tool1$request_uri? permanent;
    }

Затем на стороне сервера используйте любую опцию один STATIC_URL, соответствующую каноническому адресу (например, /static для поддомена или /tool1/static для папки)

Дополнительные заголовки или параметры URL

Если есть причина (скажите мне, почему!) не использовать перенаправления и использовать оба адреса, то, как упоминал ruddra, можно добавить дополнительные заголовки, а затем использовать их на стороне сервера для создания немного разных страниц:

location /tool1/ {
    <proxy_pass something>
    proxy_set_header SCRIPT_NAME /tool1;
}

Тот же подход можно использовать, если вы выбираете папку в качестве канонического адреса, тогда вы можете избежать жесткого кодирования tool1 в бэкэнде. Тогда, возможно, вы можете использовать либо собственный заголовок, либо дополнительный параметр URL, например ?from=domain[1], который можно обрабатывать в бэкэнде. Здесь у меня нет готового решения, так как для меня был предпочтительнее случай поддомена.

Допустим, у вас есть 2 разных домена abc.com/sales/ и xyz.com/e-commerce/, и вы хотите использовать одно и то же приложение для обоих доменов, тогда подход Additional headers or URL params полезен. :)

ruddra 11.07.2019 10:14

Другие вопросы по теме