Django настройка статических файлов

Обновлено: 06.07.2024

В предыдущих шагах этого руководства рассматривалось создание минимального приложения Django с одной страницей автономного HTML. Современные веб-приложения обычно состоят из многих страниц и используют общие ресурсы, такие как файлы CSS и JavaScript, для обеспечения согласованного стиля и реакции на события.

На этом шаге вы научитесь делать следующее:

  • с помощью шаблонов элементов Visual Studio быстро добавлять новые файлы различных типов с удобным стереотипным кодом (шаг 3.1);
  • настраивать проект Django для обслуживания статических файлов (шаг 3–2);
  • добавлять дополнительные страницы в приложение (шаг 3–3);
  • использовать наследование шаблона для создания заголовка и панели навигации, которая используется на разных страницах (шаг 3–4).

Шаг 3-1. Знакомство с шаблонами элементов

По мере разработки приложения Django обычно добавляется множество дополнительных файлов Python, HTML, CSS и JavaScript. Для каждого типа файла (а также других файлов, таких как web.config, которые могут понадобиться для развертывания) Visual Studio предоставляет удобные шаблоны элементов для начала работы.

Чтобы просмотреть доступные шаблоны, перейдите к обозревателю решений, щелкните правой кнопкой мыши папку, в которой необходимо создать элемент, выберите Добавить > Новый элемент:

Диалоговое окно

Чтобы использовать шаблон, выберите нужный шаблон, укажите имя файла и нажмите кнопку ОК. При добавлении элемента таким образом автоматически добавляется файл в проект Visual Studio и отмечает изменения для системы управления версиями.

Вопрос. Как Visual Studio определяет, какой шаблон элемента предложить?

Ответ. Файл проекта Visual Studio (PYPROJ) содержит идентификатор типа проекта, который помечает его как проект Python. Visual Studio использует этот тип идентификатора для отображения шаблонов элементов, подходящих для типа проекта. Таким образом Visual Studio может предоставлять богатый набор шаблонов элементов для многих типов проектов, который не требуется отсортировывать каждый раз.

Шаг 3–2. Обработка статических файлов из приложения

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

В проекте Django по умолчанию настроено предоставление статических файлов из папки static приложения благодаря таким строкам в файле settings.py проекта Django:

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

В обозревателе решений в проекте Visual Studio щелкните папку HelloDjangoApp правой кнопкой мыши, выберите Добавить > Новая папка и назовите папку static .

Щелкните папку static правой кнопкой мыши и выберите Добавить > Новый элемент. В открывшемся диалоговом окне выберите шаблон Таблица стилей, назовите файл site.css и нажмите кнопку ОК. Файл site.css появится в проекте и откроется в редакторе. Структура папки должна выглядеть похоже на структуру на следующем рисунке:

Структура статического файла, показанная в обозревателе решений

Замените все содержимое site.css следующим кодом и сохраните файл:

Замените содержимое файла templates/HelloDjangoApp/index.html приложения приведенным ниже кодом, который заменяет элемент <strong> , используемый в шаге 2, элементом <span> , ссылающимся на класс стиля message . Использование класса стиля таким образом дает большую гибкость в стилизации элемента. (Если вы не переместили файл index.html во вложенную папку в templates при использовании Visual Studio 2017 15.7 и более ранних версий, ознакомьтесь со сведениями об организации пространства имен шаблонов в шагах 2–4.)

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

Вопрос. Какое назначение у тега ?

Ответ. Строка необходима для ссылки на статические файлы в таких элементах, как <head> и <body> . В примере, показанном в этом разделе, staticfiles ссылается на пользовательский набор тегов шаблона Django, который позволяет использовать синтаксис для ссылки на статические файлы. Без при выполнении приложения отобразится исключение.

Вопрос. Существуют ли соглашения для организации статических файлов?

Ответ. Вы можете добавить другие файлы CSS, JavaScript и HTML в свою папку static любым способом. Типичный способ организации статических файлов — это создание вложенных папок fonts, scripts и content (для таблиц стилей и других файлов). В каждом случае не забудьте включить эти папки в относительный путь к файлу в ссылках .

Вопрос. Можно ли выполнить эту задачу без использования тега ?

Ответ. Да, можете.

Шаг 3-3. Добавление страницы в приложение

Добавление еще одной страницы в приложение означает:

  • добавление функции Python, которая определяет представление;
  • добавление шаблона для исправления страницы;
  • добавление необходимой маршрутизации в файл urls.py проекта Django.

Следующие действия добавляются на странице "Дополнительные сведения" проекта HelloDjangoApp и связывают эту страницу с домашней страницей:

В обозревателе решений щелкните правой кнопкой мыши папку templates/HelloDjangoApp, выберите Добавить > Новый элемент, щелкните шаблон элемента HTML-страница, назовите файл about.html и нажмите кнопку ОК.

Если команда Новый элемент не появляется в меню Добавить, убедитесь, что вы остановили сервер, чтобы служба Visual Studio вышла из режима отладки.

Замените содержимое about.html следующей разметкой (вы замените явную ссылку на домашнюю страницу простой навигационной панелью в шаге 3–4):

Откройте файл views.py приложения и добавьте функцию с именем about , которая использует шаблон:

Откройте файл urls.py проекта Django и добавьте следующую строку в массив urlPatterns :

Откройте файл templates/HelloDjangoApp/index.html и добавьте следующую строку под элементом <body> , чтобы задать ссылку на страницу сведений (опять же, вы замените эту ссылку навигационной панелью в шаге 3–4):

Сохраните все файлы с помощью команды меню Файл > Сохранить все или просто нажмите клавиши CTRL+SHIFT+S. (Технически этот шаг не требуется, так как при запуске проекта в Visual Studio файлы автоматически сохраняются. Однако об этой команде лучше знать.)

Выполните проект, чтобы просмотреть результаты и проверьте перемещение по страницам. Закройте сервер после завершения.

Вопрос. Я пытался использовать index для связи с домашней страницей, но он не работает. Почему?

Ответ. Несмотря на то, что функция, представления во views.py, называется index , шаблоны маршрутизации URL-адресов в файле urls.py проекта Django не содержат регулярных выражений, которые совпадают со строкой "index". Чтобы сопоставить эту строку, необходимо добавить еще одну запись для шаблона ^index$ .

Как показано в следующем разделе, лучше использовать тег в шаблоне страницы, чтобы ссылаться на имя шаблона. В этом случае Django создаст соответствующий URL-адрес. Например, замените <div><a href="home">Home</a></div> в about.html на <div><a href="">Home</a></div> . Использование index работает, потому что первый шаблон URL-адреса в urls.py называется index (из-за аргумента name='index' ). Вы также можете использовать home для обозначения второго шаблона.

Шаг 3-4. Использование наследования шаблона для создания заголовка и навигационной панели

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

Система шаблонов Django предоставляет два способа повторного использования определенных элементов в нескольких шаблонах: включение и наследование.

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

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

В обоих случаях <template_path> относится к папке templates приложения ( ../ или ./ также разрешены).

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

Далее демонстрируется наследование:

В папке templates/HelloDjangoApp приложения создайте HTML-файл (с помощью пункта контекстного меню Добавить > Новый элемент или Добавить > HTML-страница) с именем layout.html и замените его содержимое приведенным ниже кодом. Вы увидите, что этот шаблон содержит блок под названием content, который должен быть заменен для всех ссылающихся страниц:

Добавьте следующие стили в файл static/site.css приложения (в этом пошаговом руководстве мы не пытаемся продемонстрировать гибкость дизайна; эти стили просто позволяют получить интересные результаты):

Измените templates/HelloDjangoApp/index.html для ссылки на базовый шаблон и переопределения блока content. Можно увидеть, что с помощью наследования этот шаблон упрощается:

Измените файл templates/HelloDjangoApp/about.html так, чтобы он тоже ссылался на базовый шаблон и блок content в нем переопределялся:

Запустите сервер для просмотра результатов. Закройте сервер после завершения.

Выполнение приложения, показывающего панель навигации

Так как в приложение были внесены значительные изменения, самое время зафиксировать изменения в системе управления версиями.

Следующие шаги

Tutorial step 4: Use the full Django Web Project template (Руководство (шаг 4). Использование полного шаблона веб-проекта Django)

Веб-сайты обычно должны обслуживать дополнительные файлы, такие как изображения, JavaScript или CSS. В Django эти файлы называются «статическими файлами». Django предоставляет django.contrib.staticfiles помощь в этом управлении.

На этой странице рассказывается, как обслуживать эти статические файлы.

Настройка статических файлов ¶

Убедитесь, что django.contrib.staticfiles это включено в вашу настройку INSTALLED_APPS .

В вашем файле настроек определите STATIC_URL , например:

В своих шаблонах используйте тег шаблона static для создания URL-адреса заданного относительного пути с использованием STATICFILES_STORAGE настроенного хранилища .

Храните свои статические файлы в папке, названной static в вашем приложении. Например, mon_app/static/mon_app/exemple.jpg .

В дополнение к этим шагам настройки вам также потребуется обслуживать статические файлы.

Во время разработки, если вы используете django.contrib.staticfiles , статические файлы автоматически обслуживаются, runserver если для DEBUG него установлено значение True (см. django.contrib.staticfiles.views.serve() ).

Этот метод заведомо неэффективен и, вероятно, небезопасен , поэтому его нельзя использовать в производственной среде .

См. Раздел « Развертывание статических файлов» для получения истинных стратегий службы статических файлов в производственных средах.

В вашем проекте, вероятно, также будут статические элементы, не связанные с конкретным приложением. Помимо использования каталога static/ в ваших приложениях, вы можете определить список каталогов ( STATICFILES_DIRS ) в файле настроек, сообщающий Django, где он может найти другие статические файлы. Например :

См. Документацию STATICFILES_FINDERS по настройке, чтобы узнать, как staticfiles искать файлы.

Пространства имен статических файлов

Мы могли бы проще помещать наши статические файлы напрямую my_app/static (вместо создания подкаталога my_app ), но это было бы плохой идеей. Django выбирает первый статический файл, соответствующий поисковому имени, и если бы у вас был файл с таким же именем в другом приложении, Django не смог бы отличить их друг от друга. Нам нужно указать Django правильный файл, и лучший способ убедиться в этом - использовать пространства имен . То есть, поместив эти статические файлы в другой подкаталог, названный в честь приложения.

В STATICFILES_DIRS , можно указать пространства имен с помощью префиксов .

Сервис статических файлов в разработке ¶

Если вы используете, django.contrib.staticfiles как описано выше, runserver автоматически делает это, если DEBUG установлено значение True . Если django.contrib.staticfiles нет INSTALLED_APPS , вы все равно можете обслуживать статические файлы вручную с помощью представления django.views.static.serve() .

Однако в производстве это недопустимо! Вы можете просмотреть некоторые распространенные стратегии развертывания в разделе «Развертывание статических файлов» .

Например, если параметр STATIC_URL определен как /static/ , вы можете настроить эту службу, добавив в файл следующий фрагмент кода urls.py :

Кроме того, эта служебная функция работает только с самим файлом STATIC_ROOT ; он не выполняет обнаружение статических файлов, как это делает django.contrib.staticfiles .

Сервис файлов, загружаемых пользователями в процессе разработки ¶

Во время разработки вы можете обслуживать файлы, загруженные MEDIA_ROOT пользователями, используя представление django.views.static.serve() .

Однако в производстве это недопустимо! Вы можете просмотреть некоторые распространенные стратегии развертывания в разделе «Развертывание статических файлов» .

Например, если параметр MEDIA_URL определен как /media/ , вы можете настроить эту службу, добавив в файл следующий фрагмент кода urls.py :

Тесты ¶

По этой причине он staticfiles поставляется со своим собственным классом django.contrib.staticfiles.testing.StaticLiveServerTestCase , подклассом предыдущего, который имеет возможность прозрачно обслуживать все статические файлы при запуске этих тестов, что очень похоже на то, что мы мы получаем во время разработки , то есть без необходимости собирать их при первом использовании . DEBUG = True collectstatic

Развертывание ¶

django.contrib.staticfiles предоставляет удобную команду управления для сбора статических файлов в один каталог для упрощения обслуживания.

Задайте настройку STATIC_ROOT в соответствии с каталогом, из которого вы хотите обслуживать файлы, например:

Запустите команду управления collectstatic :

Это скопирует все файлы из ваших статических папок с файлами в каталог STATIC_ROOT .

Используйте любой веб-сервер для обслуживания файлов. Развертывание статических файлов обсуждает некоторые часто используемые стратегии развертывания статических файлов.

Веб-сайты обычно нуждаются в дополнительных файлах, таких как изображения, JavaScript или CSS. В Django мы называем эти файлы «статическими файлами». Django предоставляет django.contrib.staticfiles , чтобы помочь вам управлять ими.

На этой странице описано, как вы можете обслуживать эти статические файлы.

Настройка статических файлов¶

Убедитесь, что django.contrib.staticfiles включено в ваше INSTALLED_APPS .

В файле настроек определите STATIC_URL , например:

В своих шаблонах используйте тег шаблона static для построения URL для заданного относительного пути с помощью настроенного STATICFILES_STORAGE .

Храните ваши статические файлы в папке с именем static в вашем приложении. Например, my_app/static/my_app/example.jpg .

В дополнение к этим шагам по настройке, вам также нужно будет фактически обслуживать статические файлы.

Во время разработки, если вы используете django.contrib.staticfiles , это будет сделано автоматически runserver , когда DEBUG установлен в True (см. django.contrib.staticfiles.views.serve() ).

Этот метод грубо неэффективен и, вероятно, небезопасен, поэтому он не подходит для производства.

Смотрите Развертывание статических файлов для правильной стратегии обслуживания статических файлов в производственных средах.

В вашем проекте, вероятно, также будут статические активы, которые не привязаны к конкретному приложению. В дополнение к использованию директории static/ внутри ваших приложений, вы можете определить список директорий ( STATICFILES_DIRS ) в вашем файле настроек, где Django также будет искать статические файлы. Например:

Подробности о том, как STATICFILES_FINDERS находит ваши файлы, смотрите в документации к параметру staticfiles .

Пространство имен статических файлов

Теперь мы может быть сможем обойтись размещением наших статических файлов непосредственно в my_app/static/ (вместо того, чтобы создавать еще один подкаталог my_app ), но на самом деле это будет плохой идеей. Django будет использовать первый найденный статический файл, имя которого совпадает, и если бы у вас был статический файл с таким же именем в различном приложении, Django не смог бы отличить их друг от друга. Нам нужно иметь возможность указать Django на нужный файл, и лучший способ обеспечить это - разделение имен между ними. То есть, поместить эти статические файлы в другой каталог, названный в честь самого приложения.

Вы можете разместить статические активы в пространстве имен STATICFILES_DIRS , указав prefixes .

Обслуживание статических файлов во время разработки¶

Если вы используете django.contrib.staticfiles , как объяснялось выше, runserver будет делать это автоматически, когда DEBUG установлен в True . Если у вас нет django.contrib.staticfiles в INSTALLED_APPS , вы все равно можете вручную обслуживать статические файлы, используя представление django.views.static.serve() .

Это не подходит для производственного использования! О некоторых распространенных стратегиях развертывания смотрите Развертывание статических файлов .

Например, если ваш STATIC_URL определен как /static/ , вы можете сделать это, добавив следующий фрагмент в ваш urls.py:

Также эта вспомогательная функция обслуживает только фактическую папку STATIC_ROOT ; она не выполняет открытие статических файлов, таких как django.contrib.staticfiles .

Обслуживание файлов, загруженных пользователем во время разработки¶

Во время разработки вы можете обслуживать загруженные пользователем медиафайлы из MEDIA_ROOT , используя представление django.views.static.serve() .

Это не подходит для производственного использования! О некоторых распространенных стратегиях развертывания смотрите Развертывание статических файлов .

Например, если ваш MEDIA_URL определен как /media/ , вы можете сделать это, добавив следующий фрагмент в ваш urls.py:

Тестирование¶

В связи с этим staticfiles поставляет свой собственный django.contrib.staticfiles.testing.StaticLiveServerTestCase , подкласс встроенного, который имеет возможность прозрачно обслуживать все активы во время выполнения этих тестов способом, очень похожим на тот, который мы получаем во время разработки с помощью DEBUG = True , т.е. без необходимости сначала собирать их с помощью collectstatic .

Развертывание¶

django.contrib.staticfiles предоставляет удобную команду управления для сбора статических файлов в одном каталоге, чтобы вы могли легко их обслуживать.

Установите параметр STATIC_ROOT в каталог, из которого вы хотите обслуживать эти файлы, например:

Выполните команду управления collectstatic :

Это скопирует все файлы из ваших статических папок в каталог STATIC_ROOT .

Используйте выбранный вами веб-сервер для обслуживания файлов. Развертывание статических файлов охватывает некоторые общие стратегии развертывания статических файлов.

На предыдущем занятии мы в целом познакомились с шаблонами в Django. Продолжим эту тему и следующий важный шаг – это научиться подключать к шаблонам статические файлы, например, оформление CSS или JavaScript и так далее. Здесь есть свои тонкости. Как мы помним, наше приложение может работать в двух режимах: отладки – на тестовом веб-сервере; эксплуатации – на реальном веб-сервере. Так вот, в режиме отладки статические файлы Django ищет во всех каталогах static приложений и во всех возможных каталогах static внешних модулей (например, админки). То есть, статические файлы при отладке совершенно спокойно можно размещать в подкаталоге static нашего приложения. Но, в режиме эксплуатации реальный веб-сервер будет брать все статические файлы из папки static, расположенной в каталоге всего проекта. Но как появится такая папка со всеми необходимыми файлами? Для этого, при подготовке проекта к эксплуатации, выполняется специальная команда фреймворка Django:

python manage.py collectstatic

которая собирает все статические файлы из разных приложений и модулей и помещает в одну общую папку проекта.


  • STATIC_URL – префикс URL-адреса для статических файлов;
  • STATIC_ROOT – путь к общей статической папке, используемой реальным веб-сервером;
  • STATICFILES_DIRS – список дополнительных (нестандартных) путей к статическим файлам, используемых для сбора и для режима отладки.

Для этого откроем файл coolsite/settings.py и внизу уже видим заданную константу STATIC_URL с префиксом '/static/', который будет добавляться к URL статических файлов. Этот префикс должен соответствовать имени папки, в которой предполагается хранить статические файлы. Далее, определим путь к папке static всего проекта (выделена красным) с помощью константы:

Наконец, при необходимости, можем определить третью константу со списком нестандартных путей, где также могут располагаться статические файлы:

В нашем случае таких путей нет, поэтому список пуст.

Давайте теперь создадим папку static в нашем приложении women и, также как и для шаблонов, укажем в ней вложенный каталог women, чтобы не было конфликтов имен между статическими файлами разных модулей и приложений. (В действительности, Django просто найдет первый подходящий файл и на этом остановится. Чтобы этот файл был тем, что нам нужен, как раз и используется дополнительный каталог, который играет роль некоторого пространства имен.)

В этом последнем подкаталоге уже будем размещать файлы css – для файлов CSS; js – для файлов JavaScript; images – для общих файлов изображений и так далее. Я создам подкаталог css для файла стилей нашего сайта. И в нем размещу файл styles.css, который заготовил заранее, чтобы не отвлекаться на CSS-оформление сайта, а сосредоточится на изучении фреймворка Django. Вы сможете скачать весь этот проект с github и, при необходимости, внимательно его изучить. Также создам подкаталог images и скопирую в него все необходимые изображения для базового оформления сайта.

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

А, затем, для формирования URL к тому или иному статическому файлу, используется конструкция:

Например, для подключения css-файла в базовом шаблоне base.html, следует прописать:

При просмотре страницы увидим следующий URL-адрес:

<link type="text/css" href="/static/women/css/styles.css" rel="stylesheet" />

И, щелкнув по нему, убеждаемся, что он успешно загружается.

Отлично, это мы сделали. Далее, я возьму заготовленные html-файлы для базового шаблона страниц (base.html) и главной страницы (index.html). Для более детального ознакомления вы сможете их скачать по ссылке с github. Также я добавил в БД знаменитых женщин и их биографии. Сделал это с помощью SQLiteStudio. После всех этих изменений главная страница сайта выглядит так:


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

Но я намеренно не стал к нему обращаться, чтобы не усложнять изложение материала по Django.

Итак, на нашем сайте выводится пока только заголовок и контент статей. Нам бы хотелось, чтобы в списке контент выводился не полностью, а фрагментом, скажем, 50 слов или 100 символов. Как это сделать? Для таких целей в шаблонах предусмотрены специальные фильтры, которые позволяют управлять фрагментами данных. Список всех фильтров можно посмотреть на странице русскоязычной документации:

Использовать их предельно просто. Смотрите, нам для правильного отображения страницы нужно, во-первых, проставить теги абзацев при выводе постов. Это можно сделать с помощью фильтра:

Здесь value – это некая переменная шаблона, к которой применяется фильтр linebreaks. Этот фильтр ставит тег перевода строки <br>, если строки в тексте следуют друг за другом и тег абзаца <p>, если между строками имеется пустая строка. Я добавлял текст в БД с пустыми строками, поэтому этот фильтр добавит теги абзацев.

Конкретно, в нашем шаблоне главной страницы index.html этот фильтр запишется так:

Если теперь обновить страницу, то увидим разбивку текста на абзацы. Отлично, это есть. Следующее, что нам нужно – это ограничить размер постов в списке 50-ю словами. За это отвечает фильтр:

И применим его в нашем шаблоне следующим образом:

Смотрите, здесь к контенту применяются сразу два фильтра: первый добавляет теги абзацев, а второй обрезает текст до 50 слов. То есть, мы легко можем применить к переменной множество фильтров для получения искомого результата. Обновим страницу и увидим адекватное отображение списка актрис:


По аналогии используются все остальные фильтры в шаблонах фреймворка Django.

В заключение я отмечу одну важную особенность отображения информации на страницах шаблонов. Например, если в текст статьи поместить какой-либо HTML-тег, например, написать вначале в поле content «<h1>Анджелина Джоли</h1>», то при выводе мы увидим, что тег h1 был экранирован, то есть, заменен спецсимволами. Это намеренное поведение Django с целью защиты сайта от возможных хакерских атак. Если же нам все-таки нужно вывести статью без экранирования тегов, то можно использовать фильтр:

Например, чтобы отключить экранирование, в шаблоне index.html можем указать, чтобы поле content выводилось как есть со всеми тегами:

Вот так используются различные фильтры в Django.

Видео по теме



























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

Читайте также: