From 980fe00a74a9ad013b945755415ace2e5429c3c2 Mon Sep 17 00:00:00 2001 From: Alexey Pyltsyn Date: Wed, 27 Oct 2021 02:31:24 +0300 Subject: [RU] Remove notranslate (#2874) --- .../server-side/django/authentication/index.html | 66 +++++++++--------- .../learn/server-side/django/deployment/index.html | 78 +++++++++++----------- .../django/development_environment/index.html | 44 ++++++------ files/ru/learn/server-side/django/forms/index.html | 38 +++++------ .../server-side/django/introduction/index.html | 10 +-- .../ru/learn/server-side/django/models/index.html | 36 +++++----- .../development_environment/index.html | 46 ++++++------- .../locallibrary_base_template/index.html | 4 +- .../server-side/express_nodejs/forms/index.html | 16 ++--- .../express_nodejs/introduction/index.html | 54 +++++++-------- .../express_nodejs/skeleton_website/index.html | 60 ++++++++--------- .../first_steps/client-server_overview/index.html | 12 ++-- .../first_steps/web_frameworks/index.html | 12 ++-- .../first_steps/website_security/index.html | 6 +- 14 files changed, 241 insertions(+), 241 deletions(-) (limited to 'files/ru/learn/server-side') diff --git a/files/ru/learn/server-side/django/authentication/index.html b/files/ru/learn/server-side/django/authentication/index.html index a4baafeae5..c8508e97b7 100644 --- a/files/ru/learn/server-side/django/authentication/index.html +++ b/files/ru/learn/server-side/django/authentication/index.html @@ -61,7 +61,7 @@ original_slug: Learn/Server-side/Django/Аутентификация

Соответствующие настройки сделаны в параметрах INSTALLED_APPS и MIDDLEWARE файла проекта (locallibrary/locallibrary/settings.py), как показано ниже:

-
INSTALLED_APPS = [
+
INSTALLED_APPS = [
     ...
     'django.contrib.auth',  # Фреймворк аутентификации и моделей по умолчанию.
     'django.contrib.contenttypes',  # Django контент-типовая система (даёт разрешения, связанные с моделями).
@@ -82,7 +82,7 @@ MIDDLEWARE = [
 

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

-
from django.contrib.auth.models import User
+
from django.contrib.auth.models import User
 
 # Создайте пользователя и сохраните его в базе данных
 user = User.objects.create_user('myusername', 'myemail@crazymail.com', 'mypassword')
@@ -144,7 +144,7 @@ user.save()
 
 

Добавьте следующее в нижней части проекта urls.py файл (locallibrary/locallibrary/urls.py) файл:

-
#Add Django site authentication urls (for login, logout, password management)
+
#Add Django site authentication urls (for login, logout, password management)
 urlpatterns += [
     path('accounts/', include('django.contrib.auth.urls')),
 ]
@@ -157,7 +157,7 @@ urlpatterns += [
 
-
accounts/ login/ [name='login']
+
accounts/ login/ [name='login']
 accounts/ logout/ [name='logout']
 accounts/ password_change/ [name='password_change']
 accounts/ password_change/done/ [name='password_change_done']
@@ -169,7 +169,7 @@ accounts/ reset/done/ [name='password_reset_complete']

Теперь попробуйте перейти к URL-адресу входа (http://127.0.0.1:8000/accounts/login/). Это приведёт к сбою снова, но с ошибкой, сообщающей вам, что нам не хватает требуемого шаблона (registration / login.html) в пути поиска шаблона. Вы увидите следующие строки, перечисленные в жёлтом разделе вверху:

-
Exception Type:    TemplateDoesNotExist
+
Exception Type:    TemplateDoesNotExist
 Exception Value:    registration/login.html

Следующий шаг - создать каталог регистрации в пути поиска, а затем добавить файл login.html.

@@ -191,7 +191,7 @@ Exception Value: registration/login.html

Чтобы сделать эти директории видимыми для загрузчика шаблонов   (т. е. помещать этот каталог в путь поиска шаблона) откройте настройки проекта (/locallibrary/locallibrary/settings.py), и обновите в секции TEMPLATES строку 'DIRS' как показано.

-
TEMPLATES = [
+
TEMPLATES = [
     {
         ...
         'DIRS': [os.path.join(BASE_DIR, 'templates')],
@@ -207,7 +207,7 @@ Exception Value:    registration/login.html

Создайте новый HTML файл, названный /locallibrary/templates/registration/login.html. дайте ему следующее содержание:

-
{% extends "base_generic.html" %}
+
{% extends "base_generic.html" %}
 
 {% block content %}
 
@@ -258,7 +258,7 @@ Exception Value:    registration/login.html

Откройте настройки проекта (/locallibrary/locallibrary/settings.py) и добавьте текст ниже. Теперь, когда вы входите в систему, вы по умолчанию должны перенаправляться на домашнюю страницу сайта.

-
# Redirect to home URL after login (Default redirects to /accounts/profile/)
+
# Redirect to home URL after login (Default redirects to /accounts/profile/)
 LOGIN_REDIRECT_URL = '/'
 
@@ -268,7 +268,7 @@ LOGIN_REDIRECT_URL = '/'
Создайте и откройте /locallibrary/templates/registration/logged_out.html. Скопируйте текст ниже:

-
{% extends "base_generic.html" %}
+
{% extends "base_generic.html" %}
 
 {% block content %}
 <p>Logged out!</p>
@@ -290,7 +290,7 @@ LOGIN_REDIRECT_URL = '/'
 
 

Это форма, используемая для получения адреса электронной почты пользователя (для отправки пароля для сброса пароля). Создайте /locallibrary/templates/registration/password_reset_form.html и дайте ему следующее содержание:

-
{% extends "base_generic.html" %}
+
{% extends "base_generic.html" %}
 {% block content %}
 
 <form action="" method="post">{% csrf_token %}
@@ -306,7 +306,7 @@ LOGIN_REDIRECT_URL = '/'
 
 

Эта форма отображается после того, как ваш адрес электронной почты будет собран. Создайте /locallibrary/templates/registration/password_reset_done.html, и дайте ему следующее содержание:

-
{% extends "base_generic.html" %}
+
{% extends "base_generic.html" %}
 {% block content %}
 <p>We've emailed you instructions for setting your password. If they haven't arrived in a few minutes, check your spam folder.</p>
 {% endblock %}
@@ -316,7 +316,7 @@ LOGIN_REDIRECT_URL = '/'
 
 

Этот шаблон предоставляет текст электронной почты HTML, содержащий ссылку на сброс, которую мы отправим пользователям. Создайте /locallibrary/templates/registration/password_reset_email.html и дайте ему следующее содержание:

-
Someone asked for password reset for email \{{ email }}. Follow the link below:
+
Someone asked for password reset for email \{{ email }}. Follow the link below:
 \{{ protocol}}://\{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
 
@@ -324,7 +324,7 @@ LOGIN_REDIRECT_URL = '/'

На этой странице вы вводите новый пароль после нажатия ссылки в электронном письме с возвратом пароля. Создайте /locallibrary/templates/registration/password_reset_confirm.html и дайте ему следующее содержание:

-
{% extends "base_generic.html" %}
+
{% extends "base_generic.html" %}
 
 {% block content %}
 
@@ -361,7 +361,7 @@ LOGIN_REDIRECT_URL = '/'
 
 

Это последний шаблон сброса пароля, который отображается, чтобы уведомить вас о завершении сброса пароля. Создайте /locallibrary/templates/registration/password_reset_complete.html и дайте ему следующее содержание:

-
{% extends "base_generic.html" %}
+
{% extends "base_generic.html" %}
 {% block content %}
 
 <h1>The password has been changed!</h1>
@@ -383,7 +383,7 @@ LOGIN_REDIRECT_URL = '/'
 

Примечание: Система сброса пароля требует, чтобы ваш сайт поддерживал электронную почту, что выходит за рамки этой статьи, поэтому эта часть ещё не будет работать. Чтобы разрешить тестирование, поместите следующую строку в конец файла settings.py. Это регистрирует любые письма, отправленные на консоль (чтобы вы могли скопировать ссылку на сброс пароля с консоли).

-
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
+
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
 

Для получения дополнительной информации см. Отправка email (Django docs).

@@ -401,7 +401,7 @@ LOGIN_REDIRECT_URL = '/'

Откройте базовый шаблон (/locallibrary/catalog/templates/base_generic.html) и скопируйте следующий текст в sidebar блок непосредственно перед тегом шаблона endblock.

-
  <ul class="sidebar-nav">
+
  <ul class="sidebar-nav">
 
     ...
 
@@ -425,7 +425,7 @@ LOGIN_REDIRECT_URL = '/'
 
 

Если вы используете функциональные представления, самым простым способом ограничить доступ к вашим функциям является применение login_required декоратор к вашей функции просмотра, как показано ниже. Если пользователь вошёл в систему, ваш код просмотра будет выполняться как обычно. Если пользователь не вошёл в систему, это перенаправит URL-адрес входа, определённый в настройках проекта. (settings.LOGIN_URL), передав текущий абсолютный путь в качестве next параметра URL. Если пользователю удастся войти в систему, они будут возвращены на эту страницу, но на этот раз аутентифицированы.

-
from django.contrib.auth.decorators import login_required
+
from django.contrib.auth.decorators import login_required
 
 @login_required
 def my_view(request):
@@ -437,14 +437,14 @@ def my_view(request):
 
 

Аналогичным образом, самый простой способ ограничить доступ к зарегистрированным пользователям в ваших представлениях на основе классов - это производные от LoginRequiredMixin. Вы должны объявить этот mixin сначала в списке суперкласса, перед классом основного представления.

-
from django.contrib.auth.mixins import LoginRequiredMixin
+
from django.contrib.auth.mixins import LoginRequiredMixin
 
 class MyView(LoginRequiredMixin, View):
     ...

Это имеет такое же поведение при переадресации, что и  login_required декоратор. Вы также можете указать альтернативное местоположение для перенаправления пользователя, если он не аутентифицирован (login_url), и имя параметра URL вместо "next" , чтобы вставить текущий абсолютный путь (redirect_field_name).

-
class MyView(LoginRequiredMixin, View):
+
class MyView(LoginRequiredMixin, View):
     login_url = '/login/'
     redirect_field_name = 'redirect_to'
 
@@ -463,21 +463,21 @@ class MyView(LoginRequiredMixin, View):

Откройте catalog/models.py, и импортируйте модель User из django.contrib.auth.models (добавьте это чуть ниже предыдущей строки импорта в верхней части файла, так User доступен для последующего кода, что позволяет использовать его):

-
from django.contrib.auth.models import User
+
from django.contrib.auth.models import User
 

Затем добавьте поле borrower в модель BookInstance:

-
borrower = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
+
borrower = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
 

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

-
from datetime import date
+
from datetime import date

Теперь добавьте следующее определение свойства внутри класса BookInstance:

-
@property
+
@property
 def is_overdue(self):
     if self.due_back and date.today() > self.due_back:
         return True
@@ -489,7 +489,7 @@ def is_overdue(self):
 
 

Теперь, когда мы обновили наши модели, нам нужно будет внести новые изменения в проект, а затем применить эти миграции:

-
python3 manage.py makemigrations
+
python3 manage.py makemigrations
 python3 manage.py migrate
 
@@ -497,7 +497,7 @@ python3 manage.py migrate

Теперь откройте каталог catalog/admin.py, и добавьте поле borrower в класс BookInstanceAdmin , как в list_display , так и в полях fieldsets , как показано ниже. Это сделает поле видимым в разделе Admin, так что мы можем при необходимости назначить User в BookInstance.

-
@admin.register(BookInstance)
+
@admin.register(BookInstance)
 class BookInstanceAdmin(admin.ModelAdmin):
     list_display = ('book', 'status', 'borrower', 'due_back', 'id')
     list_filter = ('status', 'due_back')
@@ -525,7 +525,7 @@ class BookInstanceAdmin(admin.ModelAdmin):
 
 

Добавьте следующее в catalog/views.py:

-
from django.contrib.auth.mixins import LoginRequiredMixin
+
from django.contrib.auth.mixins import LoginRequiredMixin
 
 class LoanedBooksByUserListView(LoginRequiredMixin,generic.ListView):
     """
@@ -544,7 +544,7 @@ class LoanedBooksByUserListView(LoginRequiredMixin,generic.ListView):
 
 

Теперь откройте /catalog/urls.py и добавьте url() , указывая на приведённое выше представление (вы можете просто скопировать текст ниже в конец файла).

-
urlpatterns += [
+
urlpatterns += [
     url(r'^mybooks/$', views.LoanedBooksByUserListView.as_view(), name='my-borrowed'),
 ]
@@ -552,7 +552,7 @@ class LoanedBooksByUserListView(LoginRequiredMixin,generic.ListView):

Теперь все, что нам нужно сделать для этой страницы, - это добавить шаблон. Сначала создайте файл шаблона /catalog/templates/catalog/bookinstance_list_borrowed_user.html и дайте ему следующее содержание:

-
{% extends "base_generic.html" %}
+
{% extends "base_generic.html" %}
 
 {% block content %}
     <h1>Borrowed books</h1>
@@ -582,7 +582,7 @@ class LoanedBooksByUserListView(LoginRequiredMixin,generic.ListView):
 
 

Откройте базовый шаблон (/locallibrary/catalog/templates/base_generic.html) и добавьте выделенную строку из sidebar, как показано на рисунке.

-
 <ul class="sidebar-nav">
+
 <ul class="sidebar-nav">
    {% if user.is_authenticated %}
    <li>User: \{{ user.get_username }}</li>
    <li><a href="{% url 'my-borrowed' %}">My Borrowed</a></li>
@@ -607,7 +607,7 @@ class LoanedBooksByUserListView(LoginRequiredMixin,generic.ListView):
 
 

Определение разрешений выполняется в разделе моделей "class Meta" , используется permissions поле. Вы можете указать столько разрешений, сколько необходимо в кортеже, причём каждое разрешение определяется во вложенном кортеже, содержащем имя разрешения и отображаемое значение разрешения. Например, мы можем определить разрешение, позволяющее пользователю отметить, что книга была возвращена, как показано здесь:

-
class BookInstance(models.Model):
+
class BookInstance(models.Model):
     ...
     class Meta:
         ...
@@ -621,7 +621,7 @@ class LoanedBooksByUserListView(LoginRequiredMixin,generic.ListView):
 
 

Разрешения текущего пользователя хранятся в переменной шаблона, называемой  \{{ perms }}. Вы можете проверить, имеет ли текущий пользователь определённое разрешение, используя конкретное имя переменной в соответствующем приложении «Django» - например, \{{ perms.catalog.can_mark_returned }} будет True если у пользователя есть это разрешение, а False - в противном случае. Обычно мы проверяем разрешение с использованием шаблона {% if %}, как показано в:

-
{% if perms.catalog.can_mark_returned %}
+
{% if perms.catalog.can_mark_returned %}
     <!-- We can mark a BookInstance as returned. -->
     <!-- Perhaps add code to link to a "book return" view here. -->
 {% endif %}
@@ -633,7 +633,7 @@ class LoanedBooksByUserListView(LoginRequiredMixin,generic.ListView):
 
 

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

-
from django.contrib.auth.decorators import permission_required
+
from django.contrib.auth.decorators import permission_required
 
 @permission_required('catalog.can_mark_returned')
 @permission_required('catalog.can_edit')
@@ -642,7 +642,7 @@ def my_view(request):
 
 

Требуется разрешение mixin для представлений на основе классов.

-
from django.contrib.auth.mixins import PermissionRequiredMixin
+
from django.contrib.auth.mixins import PermissionRequiredMixin
 
 class MyView(PermissionRequiredMixin, View):
     permission_required = 'catalog.can_mark_returned'
diff --git a/files/ru/learn/server-side/django/deployment/index.html b/files/ru/learn/server-side/django/deployment/index.html
index e3d7b75700..08cdb491fe 100644
--- a/files/ru/learn/server-side/django/deployment/index.html
+++ b/files/ru/learn/server-side/django/deployment/index.html
@@ -115,7 +115,7 @@ original_slug: Learn/Server-side/Django/Разворачивание
 
  • DEBUG. При развёртывании сайта должен быть установлен в False (DEBUG = False). Тем самым, прекратится вывод  важной отладочной информации.
  • SECRET_KEY. Это большое случайное число, применяемое для защиты от CRSF. Важно, чтобы ключ, используемый в продакшене, не указывался в исходном коде, и/или не запрашивался с другого сервера. Django рекомендует размещать значение ключа либо в переменной окружения, или в файле с доступом только на чтение. -
    # Чтение SECRET_KEY из переменной окружения
    +  
    # Чтение SECRET_KEY из переменной окружения
     import os
     SECRET_KEY = os.environ['SECRET_KEY']
     
    @@ -131,7 +131,7 @@ with open('/etc/secret_key.txt') as f:
     
     

    Откройте /locallibrary/settings.py, закомментируйте исходное значение SECRET_KEY и добавьте новые строки, как указано ниже жирным. В течении разработки, никаких переменных окружения определено не было, таким образом будут использоваться значения по умолчанию (не имеет значения какой ключ вы используете в процессе разработки, поскольку при развёртывании проекта вы будете использовать другой).

    -
    # SECURITY WARNING: keep the secret key used in production secret!
    +
    # SECURITY WARNING: keep the secret key used in production secret!
     # SECRET_KEY = 'cg#p$g+j9tax!#a3cup@1$8obt2_+&k3q+pmu)5%asj6yjpkag'
     import os
     SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'cg#p$g+j9tax!#a3cup@1$8obt2_+&k3q+pmu)5%asj6yjpkag')
    @@ -139,7 +139,7 @@ with open('/etc/secret_key.txt') as f:
     
     

    Затем закомментируйте строку с настройкой DEBUG, а затем, добавьте новую, указанную ниже.

    -
    # SECURITY WARNING: don't run with debug turned on in production!
    +
    # SECURITY WARNING: don't run with debug turned on in production!
     # DEBUG = True
     DEBUG = bool( os.environ.get('DJANGO_DEBUG', True) )
     
    @@ -152,7 +152,7 @@ with open('/etc/secret_key.txt') as f:

    Весь перечень настроек для разворачивания вашего сайта находится по ссылке Deployment checklist (Django docs). Кроме того, вы можете получить список настроек, выполнив в терминале команду:

    -
    python3 manage.py check --deploy
    +
    python3 manage.py check --deploy
     

    Пример: Установка LocalLibrary на Heroku

    @@ -247,11 +247,11 @@ with open('/etc/secret_key.txt') as f:
    1. Установите git себе на компьютер (Вы можете найти версию для своей платформы здесь).
    2. Откройте командную строку (или терминал) и выполните в нём следующую команду, используя ссылку, которую вы получили с github: -
      git clone https://github.com/<your_git_user_id>/django_local_library.git
      +  
      git clone https://github.com/<your_git_user_id>/django_local_library.git
       
      Это создаст подпапку (с содержанием вашего репозитория и именем вашего репозитория) внутри папки, в которой выполнялась команда.
    3. Перейдите в эту папку: -
      cd django_local_library.git
      +
      cd django_local_library.git
    @@ -260,7 +260,7 @@ with open('/etc/secret_key.txt') as f:
    1. Скопируйте ваше приложение в папку репозитория (все файлы с таким же уровнем, как у manage.py, БЕЗ папки проекта, в которой эти файлы находятся).
    2. Откройте файл с расширением .gitignore в текстовом редакторе, вставьте в самый его конец строки, приведённые ниже, а затем сохраните (этот файл "говорит" о файлах, которые не должны быть  загружены в git по умолчанию). -
      # Text backup files
      +  
      # Text backup files
       *.bak
       
       #Database
      @@ -269,10 +269,10 @@ with open('/etc/secret_key.txt') as f:
        
    3. Откройте командную строку или терминал и используйте add команду с флагом -A. Эта команда сохранит изменения в репозиторий:

      -
      git add -A
      +
      git add -A
    4. Используйте команду status,  что бы убедиться, что все файлы, которые вы собираетесь добавить верны (вы хотите включить исходные файлы, а не бинарные файлы, временные файлы и т. д.). В консоль выведется что то вроде этого: -
      > git status
      +  
      > git status
       On branch master
       Your branch is up-to-date with 'origin/master'.
       Changes to be committed:
      @@ -286,10 +286,10 @@ Changes to be committed:
               new file:   templates/registration/password_reset_form.html
    5. Теперь, зафиксируйте файлы в локальном репозитории: -
      git commit -m "First version of application moved into github"
      +
      git commit -m "First version of application moved into github"
    6. Синхронизируете свой локальный репозиторий с сайтом Github: -
      git push origin master
      +
      git push origin master
    @@ -309,7 +309,7 @@ Changes to be committed:

     Создайте файл с именем Procfile (без расширения) в корне нашего GitHub репозитории объявить типы процессов и точки входа приложения. Скопируйте в него следующий текст:

    -
    web: gunicorn locallibrary.wsgi --log-file -
    +
    web: gunicorn locallibrary.wsgi --log-file -

    «web:» сообщает Heroku, что это веб динамический и может быть отправлен HTTP-трафик. Процесс, который начнётся в этом динамически, - это gunicorn, который является популярным сервером веб-приложений, который рекомендует Heroku. Мы запускаем Gunicorn, используя конфигурационную информацию в модуле locallibrary.wsgi (созданный с помощью нашего скелета приложения: /locallibrary/wsgi.py).

    @@ -321,7 +321,7 @@ Changes to be committed:

    Установка Gunicorn локально в командной строке используя пакетный менеджер pip (которые мы установили когда настраивали среду разработки):

    -
    pip3 install gunicorn
    +
    pip3 install gunicorn
     

    Настройка Базы Данных

    @@ -336,14 +336,14 @@ Changes to be committed:

    Установите dj-database-url локально, чтобы он стал частью наших требований к настройке Heroku на удалённом сервере:

    -
    $ pip3 install dj-database-url
    +
    $ pip3 install dj-database-url
     
    settings.py

    Откройте /locallibrary/settings.py и скопируйте следующую конфигурацию в нижнюю часть файла:

    -
    # Heroku: Update database configuration from $DATABASE_URL.
    +
    # Heroku: Update database configuration from $DATABASE_URL.
     import dj_database_url
     db_from_env = dj_database_url.config(conn_max_age=500)
     DATABASES['default'].update(db_from_env)
    @@ -363,7 +363,7 @@ DATABASES['default'].update(db_from_env)

    Django будет использовать нашу базу данных SQLite локально по умолчанию, поскольку переменная среды DATABASE_URL не задана в нашей локальной среде. Если вы хотите полностью перейти на Postgres и использовать нашу бесплатную базу данных Heroku для разработки и производства, то вы можете. Например, чтобы установить psycopg2 и его зависимости локально в системе на базе Linux, вы должны использовать следующие команды bash / terminal:

    -
    sudo apt-get install python-pip python-dev libpq-dev postgresql postgresql-contrib
    +
    sudo apt-get install python-pip python-dev libpq-dev postgresql postgresql-contrib
     pip3 install psycopg2
     
    @@ -392,7 +392,7 @@ pip3 install psycopg2

    Откройте /locallibrary/settings.py и скопируйте следующую конфигурацию в нижнюю часть файла. BASE_DIR уже должен быть определён в вашем файле (STATIC_URL, возможно, уже был определён в файле, когда он был создан. В то время как это не причинит вреда, вы также можете удалить дублируемую предыдущую ссылку).

    -
    # Static files (CSS, JavaScript, Images)
    +
    # Static files (CSS, JavaScript, Images)
     # https://docs.djangoproject.com/en/1.10/howto/static-files/
     
     # The absolute path to the directory where collectstatic will collect static files for deployment.
    @@ -419,14 +419,14 @@ STATIC_URL = '/static/'
     
     

    Установите WhiteNoise локально, используя следующую команду:

    -
    $ pip3 install whitenoise
    +
    $ pip3 install whitenoise
     
    settings.py

    Чтобы установить WhiteNoise в приложение Django, откройте /locallibrary/settings.py, найдите параметр MIDDLEWARE и добавьте WhiteNoiseMiddleware в верхней части списка, чуть ниже SecurityMiddleware:

    -
    MIDDLEWARE = [
    +
    MIDDLEWARE = [
         'django.middleware.security.SecurityMiddleware',
         'whitenoise.middleware.WhiteNoiseMiddleware',
         'django.contrib.sessions.middleware.SessionMiddleware',
    @@ -440,7 +440,7 @@ STATIC_URL = '/static/'
     
     

    При желании вы можете уменьшить размер статических файлов при их обслуживании (это более эффективно). Просто добавьте следующее в конец /locallibrary/settings.py:

    -
    # Simplified static file serving.
    +
    # Simplified static file serving.
     # https://warehouse.python.org/project/whitenoise/
     STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
     
    @@ -449,11 +449,11 @@ STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

    Требования Python вашего веб-приложения должны храниться в файле requirements.txt в корневом каталоге вашего репозитория. После этого Heroku автоматически установит их при восстановлении вашей среды. Вы можете создать этот файл с помощью pip в командной строке (запустите в корне repo):

    -
    pip3 freeze > requirements.txt
    +
    pip3 freeze > requirements.txt

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

    -
    dj-database-url==0.4.1
    +
    dj-database-url==0.4.1
     Django==1.10.2
     gunicorn==19.6.0
     psycopg2==2.6.2
    @@ -468,7 +468,7 @@ whitenoise==3.2.2
     
     

    Файл runtime.txt, если определён, говорит Heroku, какой язык программирования использовать. Создайте файл в корне репо и добавьте следующий текст:

    -
    python-3.5.2
    +
    python-3.5.2

    Примечание: Heroku поддерживает только небольшое количество Python runtimes. (на момент написания статьи, в том числе и выше). Heroku будет использовать поддерживаемую среду выполнения независимо от значения, указанного в этом файле.

    @@ -478,13 +478,13 @@ whitenoise==3.2.2

    Далее мы сохраним все наши изменения в Github. В терминале (whist внутри нашего репозитория) введите следующие команды:

    -
    git add -A
    +
    git add -A
     git commit -m "Added files and changes required for deployment to heroku"
     git push origin master

    Прежде чем продолжить, дайте возможность проверить сайт снова локально и убедиться, что это не повлияло ни на одно из наших изменений выше. Запустите веб-сервер разработки как обычно, а затем проверьте, работает ли сайт, как вы ожидаете в своём браузере.

    -
    python3 manage.py runserver
    +
    python3 manage.py runserver

    Теперь мы должны быть готовы начать развёртывание LocalLibrary на Heroku.

    @@ -506,14 +506,14 @@ git push origin master

    После установки клиента вам будут доступны команды. Например, чтобы получить справку о клиенте:

    -
    heroku help
    +
    heroku help
     

    Создание и загрузка веб-сайта

    Чтобы создать приложение, мы запускаем команду «create» в корневом каталоге нашего репозитория. Это создаёт git remote («указатель на удалённый репозиторий»), названный heroku в нашей локальной среде git.

    -
    heroku create
    +
    heroku create

    Примечание: вы можете назвать удалённый, если хотите, указав значение после «create». Если вы этого не сделаете, вы получите случайное имя. Имя используется в URL-адресе по умолчанию.

    @@ -521,19 +521,19 @@ git push origin master

    Затем мы можем подтолкнуть наше приложение в репозиторий heroku как показано ниже. Это позволит загрузить приложение, упаковать его в dyno, запустить collectstatic, и запустить сам сайт.

    -
    git push heroku master
    +
    git push heroku master

    Если нам повезёт, приложение «заработает» на сайте, но оно не будет работать должным образом, потому что мы не настроили таблицы базы данных для использования нашим приложением. Для этого нам нужно использовать команду  heroku run и запустить "one off dyno" для выполнения операции переноса. Введите в терминал следующую команду:

    -
    heroku run python manage.py migrate
    +
    heroku run python manage.py migrate

    Мы также должны будем иметь возможность добавлять книги и авторов, поэтому давайте также создадим суперпользователя, снова используя одноразовый динамический режим:

    -
    heroku run python manage.py createsuperuser
    +
    heroku run python manage.py createsuperuser

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

    -
    heroku open
    +
    heroku open

    Создайте несколько книг на сайте администратора и проверьте, работает ли сайт, как вы ожидаете.

    @@ -541,7 +541,7 @@ git push origin master

    Вы можете проверить дополнения в своём приложении, используя heroku addons команду. Это будет список всех аддонов, их ценовая категория и состояние.

    -
    >heroku addons
    +
    >heroku addons
     
     Add-on                                     Plan       Price  State
     ─────────────────────────────────────────  ─────────  ─────  ───────
    @@ -550,7 +550,7 @@ heroku-postgresql (postgresql-flat-26536)  hobby-dev  free   created
     
     

    Здесь мы видим, что у нас есть только одна надстройка, база данных postgres SQL. Это бесплатно и автоматически создаётся при создании приложения. Вы можете открыть веб-страницу, чтобы более подробно изучить надстройку базы данных (или любое другое дополнение), используя следующую команду:

    -
    heroku addons:open heroku-postgresql
    +
    heroku addons:open heroku-postgresql
     

    Другие команды позволяют создавать, уничтожать, обновлять и понижать аддоны (используя аналогичный синтаксис для открытия). Для получения дополнительной информации см.  Managing Add-ons (Heroku docs).

    @@ -559,7 +559,7 @@ heroku-postgresql (postgresql-flat-26536) hobby-dev free created

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

    -
    >heroku config
    +
    >heroku config
     
     === locallibrary Config Vars
     DATABASE_URL: postgres://uzfnbcyxidzgrl:j2jkUFDF6OGGqxkgg7Hk3ilbZI@ec2-54-243-201-144.compute-1.amazonaws.com:5432/dbftm4qgh3kda3
    @@ -572,7 +572,7 @@ DATABASE_URL: postgres://uzfnbcyxidzgrl:j2jkUFDF6OGGqxkgg7Hk3ilbZI@ec2-54-243-20

    Мы устанавливаем  DJANGO_SECRET_KEY используя команду config:set (как показано ниже). Не забудьте использовать свой секретный ключ!

    -
    >heroku config:set DJANGO_SECRET_KEY=eu09(ilk6@4sfdofb=b_2ht@vad*$ehh9-)3u_83+y%(+phh&=
    +
    >heroku config:set DJANGO_SECRET_KEY=eu09(ilk6@4sfdofb=b_2ht@vad*$ehh9-)3u_83+y%(+phh&=
     
     Setting DJANGO_SECRET_KEY and restarting locallibrary... done, v7
     DJANGO_SECRET_KEY: eu09(ilk6@4sfdofb=b_2ht@vad*$ehh9-)3u_83+y%(+phh
    @@ -580,20 +580,20 @@ DJANGO_SECRET_KEY: eu09(ilk6@4sfdofb=b_2ht@vad*$ehh9-)3u_83+y%(+phh
     
     

    Аналогично мы устанавливаем  DJANGO_DEBUG:

    -
    >heroku config:set DJANGO_DEBUG=''
    +
    >heroku config:set DJANGO_DEBUG=''
     
     Setting DJANGO_DEBUG and restarting locallibrary... done, v8

    Если вы посетите веб-сайт сейчас, вы получите ошибку "Bad request" , потому что в  ALLOWED_HOSTS надо внести параметры, если у вас DEBUG=False (в качестве меры безопасности). Откройте /locallibrary/settings.py и измените ALLOWED_HOSTS для включения вашего базового URL-адреса приложения (например, 'locallibrary1234.herokuapp.com') URL, который вы обычно используете на локальном сервере разработки.

    -
    ALLOWED_HOSTS = ['<your app URL without the https:// prefix>.herokuapp.com','127.0.0.1']
    +
    ALLOWED_HOSTS = ['<your app URL without the https:// prefix>.herokuapp.com','127.0.0.1']
     # For example:
     # ALLOWED_HOSTS = ['fathomless-scrubland-30645.herokuapp.com','127.0.0.1']
     

    Затем сохраните настройки и передайте их в репозиторий Github и в Heroku:

    -
    git add -A
    +
    git add -A
     git commit -m 'Update ALLOWED_HOSTS with site and development server URL'
     git push origin master
     git push heroku master
    @@ -606,7 +606,7 @@ git push heroku master

    Клиент Heroku предоставляет несколько инструментов для отладки:

    -
    heroku logs  # Show current logs
    +
    heroku logs  # Show current logs
     heroku logs --tail # Show current logs and keep updating with any new results
     heroku config:set DEBUG_COLLECTSTATIC=1 # Add additional logging for collectstatic (this tool is run automatically during a build)
     heroku ps   #Display dyno status
    diff --git a/files/ru/learn/server-side/django/development_environment/index.html b/files/ru/learn/server-side/django/development_environment/index.html
    index 199f16badb..5265e95819 100644
    --- a/files/ru/learn/server-side/django/development_environment/index.html
    +++ b/files/ru/learn/server-side/django/development_environment/index.html
    @@ -115,19 +115,19 @@ translation_of: Learn/Server-side/Django/development_environment
     
     

    Ubuntu Linux включает в себя Python 3 по умолчанию. Вы можете удостовериться в этом, выполнив следующую команду в терминале:

    -
    python3 -V
    +
    python3 -V
      Python 3.5.2

    Однако, инструмент Python Package Index, при помощи которого вам нужно будет установить пакеты для  Python 3 (включая Django), по умолчанию не установлен. Вы можете установить pip3 через терминал bash при помощи:

    -
    sudo apt-get install python3-pip
    +
    sudo apt-get install python3-pip
     

    Mac OS X

    Mac OS X "El Capitan" не включает Python 3. Вы можете удостовериться в этом, выполнив следующую команду в терминале:

    -
    python3 -V
    +
    python3 -V
      -bash: python3: command not found

    Вы можете легко установить Python 3 (вместе с инструментом pip3) с python.org:

    @@ -144,13 +144,13 @@ translation_of: Learn/Server-side/Django/development_environment

    Удостовериться в успешной установке вы можете проверкой на наличие  Python 3, как показано ниже:

    -
    python3 -V
    +
    python3 -V
      Python 3.5.20
     

    Подобным образом вы можете проверить установку pip3, отобразив список доступных пакетов:

    -
    pip3 list
    +
    pip3 list

    Windows 10

    @@ -168,13 +168,13 @@ translation_of: Learn/Server-side/Django/development_environment

    После этого вы сможете подтвердить успешную установку Python путём выполнения следующего текста в командной строке:

    -
    py -3 -V
    +
    py -3 -V
      Python 3.5.2
     

    Установщик Windows включает в себя pip3 (менеджер пакетов Python) по умолчанию. Вы можете отобразить список установленных пакетов, как показано далее:

    -
    pip list
    +
    pip list
     
    @@ -193,12 +193,12 @@ translation_of: Learn/Server-side/Django/development_environment

    Установите инструмент при помощи pip3:

    -
    sudo pip3 install virtualenvwrapper
    +
    sudo pip3 install virtualenvwrapper
     

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

    -
    export WORKON_HOME=$HOME/.virtualenvs
    +
    export WORKON_HOME=$HOME/.virtualenvs
     export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
     export VIRTUALENVWRAPPER_VIRTUALENV_ARGS=' -p /usr/bin/python3 '
     export PROJECT_HOME=$HOME/Devel
    @@ -207,12 +207,12 @@ source /usr/local/bin/virtualenvwrapper.sh
     
     

    Затем перезагрузите файл загрузки, выполнив в терминале следующую команду:

    -
    source ~/.bashrc
    +
    source ~/.bashrc
     

    В этот момент вы должны увидеть запуск группы скриптов, как показано ниже:

    -
    virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/premkproject
    +
    virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/premkproject
     virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/postmkproject
     ...
     virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/preactivate
    @@ -228,12 +228,12 @@ virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/get_env_detail
     
     

    Установите инструмент при помощи pip3:

    -
    sudo pip3 install virtualenvwrapper
    +
    sudo pip3 install virtualenvwrapper
     

    Затем добавьте следующие строки в конец вашего файла загрузки программной оболочки:

    -
    export WORKON_HOME=$HOME/.virtualenvs
    +
    export WORKON_HOME=$HOME/.virtualenvs
     export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
     export PROJECT_HOME=$HOME/Devel
     source /usr/local/bin/virtualenvwrapper.sh
    @@ -252,7 +252,7 @@ source /usr/local/bin/virtualenvwrapper.sh
     
     
     
    -
    cd ~ # Navigate to my home directory
    +
    cd ~ # Navigate to my home directory
     ls -la #List the content of the directory. You should see .bash_profile
     nano .bash_profile # Open the file in the nano text editor, within the terminal
     # Scroll to the end of the file, and copy in the lines above
    @@ -262,7 +262,7 @@ nano .bash_profile # Open the file in the nano text editor, within the terminal
     
     

    После этого перезагрузите файл загрузки путём выполнения следующей команды в терминале:

    -
    source ~/.bash_profile
    +
    source ~/.bash_profile
     

    В этот момент вы должны увидеть запуск группы скриптов (те же скрипты, что и в случае установки на Ubuntu).

    @@ -273,7 +273,7 @@ nano .bash_profile # Open the file in the nano text editor, within the terminal

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

    -
    pip3 install virtualenvwrapper-win
    +
    pip3 install virtualenvwrapper-win

    Теперь вы можете создать новую виртуальную среду при помощи команды mkvirtualen.

    @@ -283,7 +283,7 @@ nano .bash_profile # Open the file in the nano text editor, within the terminal

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

    -
    $ mkvirtualenv my_django_environment
    +
    $ mkvirtualenv my_django_environment
     Running virtualenv with interpreter /usr/bin/python3 ...
     virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/t_env7/bin/get_env_details
     (my_django_environment) ubuntu@ubuntu:~$
    @@ -309,12 +309,12 @@ virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/t_env7/bin/get

    После создания виртуальной среды и вызова workon для входа в неё вы можете использовать pip3 для установки Django. 

    -
    pip3 install django
    +
    pip3 install django
     

    Вы можете проверить установку Django, выполнив следующую команду (она просто проверяет, что Python может найти модуль Django):

    -
    # Linux/Mac OS X
    +
    # Linux/Mac OS X
     python3 -m django --version
      1.10.10
     
    @@ -335,18 +335,18 @@ py -3 -m django --version
     
     

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

    -
    mkdir django_test
    +
    mkdir django_test
     cd django_test
     

    Затем вы можете создать шаблон сайта "mytestsite" при помощи инструмента django-admin. После создания сайта вы можете перейти в папку, где найдёте основной скрипт для управления проектами с именем manage.py.

    -
    django-admin startproject mytestsite
    +
    django-admin startproject mytestsite
     cd mytestsite

    Мы можем запустить веб-сервер разработки из этой папки при помощи manage.py и команды runserver, как показано ниже.

    -
    $ python3 manage.py runserver 
    +
    $ python3 manage.py runserver 
     Performing system checks...
     
     System check identified no issues (0 silenced).
    diff --git a/files/ru/learn/server-side/django/forms/index.html b/files/ru/learn/server-side/django/forms/index.html
    index 6652e56297..9b2c9a5ed3 100644
    --- a/files/ru/learn/server-side/django/forms/index.html
    +++ b/files/ru/learn/server-side/django/forms/index.html
    @@ -50,7 +50,7 @@ translation_of: Learn/Server-side/Django/Forms
     
     

    Форма описывается на языке HTML как набор элементов, расположенных внутри парных тэгов <form>...</form>. Любая форма содержит как минимум одно поле-тэг input типа type="submit".

    -
    <form action="/team_name_url/" method="post">
    +
    <form action="/team_name_url/" method="post">
         <label for="team_name">Enter name: </label>
         <input id="team_name" type="text" name="name_field" value="Default name for team.">
         <input type="submit" value="OK">
    @@ -129,7 +129,7 @@ translation_of: Learn/Server-side/Django/Forms
     
     

    Для того, чтобы создать класс с возможностями базового класса Form мы должны импортировать библиотеку forms, наследовать наш класс от класса Form, а затем объявить поля формы. Таким образом, самый простой класс формы в нашем случае будет иметь вид, показанный ниже:

    -
    from django import forms
    +
    from django import forms
     
     class RenewBookForm(forms.Form):
         renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).")
    @@ -160,7 +160,7 @@ class RenewBookForm(forms.Form):
     
     

    Django предоставляет несколько мест где вы можете осуществить валидацию ваших данных. Простейшим способом проверки значения одиночного поля является переопределение методаclean_<fieldname>() (здесь, <fieldname> это имя поля, которое вы хотите проверить). Например, мы хотим проверить, что введённое значение renewal_date находится между текущей датой и  4 неделями в будущем. Для этого мы создаём метод clean_renewal_date(), как показано ниже:

    -
    from django import forms
    +
    from django import forms
     
     from django.core.exceptions import ValidationError
     from django.utils.translation import ugettext_lazy as _
    @@ -201,7 +201,7 @@ class RenewBookForm(forms.Form):
     
     

    Перед созданием отображения давайте добавим соответствующую конфигурацию URL-адреса для страницы обновления книг. Скопируйте следующий фрагмент в нижнюю часть файла locallibrary/catalog/urls.py.

    -
    urlpatterns += [
    +
    urlpatterns += [
         url(r'^book/(?P<pk>[-\w]+)/renew/$', views.renew_book_librarian, name='renew-book-librarian'),
     ]
    @@ -219,7 +219,7 @@ class RenewBookForm(forms.Form):

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

    -
    from django.shortcuts import get_object_or_404
    +
    from django.shortcuts import get_object_or_404
     from django.http import HttpResponseRedirect
     from django.urls import reverse
     import datetime
    @@ -263,7 +263,7 @@ def renew_book_librarian(request, pk):
     
     

    В отображении аргумент pk мы используем в функцииget_object_or_404() для получения текущего объекта типа BookInstance (если его не существует, то функция, а следом и наше отображение прервут своё выполнение, а на странице пользователя отобразится сообщение об ошибке: "объект не найден"). Если запрос вызова отображения не является POST-запросом, то мы переходим к условному блоку else, в котором мы создаём форму по умолчанию и передаём ей начальное значенияinitial для поля renewal_date (выделено жирным ниже, - 3 недели, начиная с текущей даты). 

    -
        book_inst = get_object_or_404(BookInstance, pk=pk)
    +
        book_inst = get_object_or_404(BookInstance, pk=pk)
     
         # Если это GET (или другой метод), тогда создаём форму по умолчанию
         else:
    @@ -276,7 +276,7 @@ def renew_book_librarian(request, pk):
     
     

    Если все таки у нас POST-запрос, тогда мы создаём объект с  именем form и заполняем его данными, полученными из запроса. Данный процесс называется связыванием (или, биндингом, от англ. "binding") и позволяет нам провести валидацию данных. Далее осуществляется валидация формы, при этом проверяются все поля формы — для этого используются как код обобщённого класса, так и пользовательских функций, в частности нашей функции проверки введённых дат clean_renewal_date()

    -
        book_inst = get_object_or_404(BookInstance, pk=pk)
    +
        book_inst = get_object_or_404(BookInstance, pk=pk)
     
         # Если данный запрос типа POST, тогда
         if request.method == 'POST':
    @@ -309,7 +309,7 @@ def renew_book_librarian(request, pk):
     
     

    Окончательный вид отображения показан ниже. Пожалуйста, скопируйте данный текст в нижнюю часть файла locallibrary/catalog/views.py.

    -
    from django.contrib.auth.decorators import permission_required
    +
    from django.contrib.auth.decorators import permission_required
     
     from django.shortcuts import get_object_or_404
     from django.http import HttpResponseRedirect
    @@ -352,7 +352,7 @@ def renew_book_librarian(request, pk):
     
     

    Создайте шаблон, на который ссылается наше отображение (/catalog/templates/catalog/book_renew_librarian.html) и скопируйте в него код, указанный ниже:

    -
    {% extends "base_generic.html" %}
    +
    {% extends "base_generic.html" %}
     {% block content %}
     
         <h1>Renew: \{{bookinst.book.title}}</h1>
    @@ -379,7 +379,7 @@ def renew_book_librarian(request, pk):
     
     

    Все что осталось, это указать переменную \{{form}}, которую мы передали в шаблон в словаре контекста. Возможно это вас не удивит, но таким образом мы предоставим возможность форме отрендерить свои поля с их метками, виджетами и дополнительными текстами, и в результате мы получим следующее:

    -
    <tr>
    +
    <tr>
       <th><label for="id_renewal_date">Renewal date:</label></th>
       <td>
         <input id="id_renewal_date" name="renewal_date" type="text" value="2016-11-08" required />
    @@ -395,7 +395,7 @@ def renew_book_librarian(request, pk):
     
     

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

    -
    <tr>
    +
    <tr>
       <th><label for="id_renewal_date">Renewal date:</label></th>
        <td>
           <ul class="errorlist">
    @@ -427,7 +427,7 @@ def renew_book_librarian(request, pk):
     
     

    Если вы выполнили задание в Django руководство часть 8: Аутентификация и разрешение доступа, то у вас должна быть страница со списком всех книг в наличии библиотеки и данный список (страница) должен быть доступен только её сотрудникам. На данной странице в каждом пункте (для каждой книги) мы можем добавить ссылку на нашу новую страницу обновления книги.

    -
    {% if perms.catalog.can_mark_returned %}- <a href="{% url 'renew-book-librarian' bookinst.id %}">Renew</a>  {% endif %}
    +
    {% if perms.catalog.can_mark_returned %}- <a href="{% url 'renew-book-librarian' bookinst.id %}">Renew</a>  {% endif %}

    Примечание: Помните что, для того чтобы перейти на страницу обновления книги, ваш тестовый логин должен иметь разрешение доступа типа "catalog.can_mark_returned"(возможно надо воспользоваться вашим аккаунтом для суперпользователя).

    @@ -457,7 +457,7 @@ def renew_book_librarian(request, pk):

    Базовая реализация ModelForm содержит тоже поле как и ваш предыдущий класс формы  RenewBookForm, что и показано ниже. Все что вам необходимо сделать, - внутри вашего нового класса добавить класс Meta и связать его с моделью model (BookInstance), а затем перечислить поля модели в поле fields которые должны быть включены в форму (вы можете включить все поля при помощи fields = '__all__', или можно воспользоваться полем exclude (вместо fields), чтобы определить поля модели, которые не нужно включать).

    -
    from django.forms import ModelForm
    +
    from django.forms import ModelForm
     from .models import BookInstance
     
     class RenewBookModelForm(ModelForm):
    @@ -472,7 +472,7 @@ class RenewBookModelForm(ModelForm):
     
     

    Оставшаяся часть информации касается объявления полей модели (то есть, текстовых меток, виджетов, текстов, сообщений об ошибках). Если они недостаточно "правильные", то тогда мы можем переопределить их в нашем классе Meta при помощи словаря, содержащего поле, которое надо изменить и его новое значение. Например, в нашей форме мы могли бы поменять текст метки для поля "Renewal date" (вместо того, чтобы оставить текст по умолчанию: Due date), а кроме того мы хотим написать другой вспомогательный текст. Класс Meta, представленный ниже, показывает вам, как переопределить данные поля. Кроме того, при необходимости, вы можете установить значения для виджетов widgets и сообщений об ошибках error_messages.

    -
    class Meta:
    +
    class Meta:
         model = BookInstance
         fields = ['due_back',]
         labels = { 'due_back': _('Renewal date'), }
    @@ -481,7 +481,7 @@ class RenewBookModelForm(ModelForm):
     
     

    Чтобы добавить валидацию, вы можете использовать тот же способ как и для класса Form — вы определяете функцию с именем  clean_field_name() из которой выбрасываете исключение ValidationError, если это необходимо. Единственным отличием от нашей оригинальной формы будет являться то, что поле модели имеет имя due_back, а не "renewal_date".

    -
    from django.forms import ModelForm
    +
    from django.forms import ModelForm
     from .models import BookInstance
     
     class RenewBookModelForm(ModelForm):
    @@ -522,7 +522,7 @@ class RenewBookModelForm(ModelForm):
     
     

    Откройте файл отображений (locallibrary/catalog/views.py) и добавьте следующий код в его нижнюю часть:

    -
    from django.views.generic.edit import CreateView, UpdateView, DeleteView
    +
    from django.views.generic.edit import CreateView, UpdateView, DeleteView
     from django.urls import reverse_lazy
     from .models import Author
     
    @@ -551,7 +551,7 @@ class AuthorDelete(DeleteView):
     
     

    Создайте файл шаблона locallibrary/catalog/templates/catalog/author_form.html  и скопируйте в него следующий текст.

    -
    {% extends "base_generic.html" %}
    +
    {% extends "base_generic.html" %}
     
     {% block content %}
     
    @@ -569,7 +569,7 @@ class AuthorDelete(DeleteView):
     
     

    Отображения "удалить" ожидает "найти" шаблон с именем формата model_name_confirm_delete.html (и снова, вы можете изменить суффикс при помощи поля отображенияtemplate_name_suffix). Создайте файл шаблона locallibrary/catalog/templates/catalog/author_confirm_delete.html и скопируйте в него текст, указанный ниже.

    -
    {% extends "base_generic.html" %}
    +
    {% extends "base_generic.html" %}
     
     {% block content %}
     
    @@ -589,7 +589,7 @@ class AuthorDelete(DeleteView):
     
     

    Откройте файл конфигураций URL-адресов (locallibrary/catalog/urls.py) и добавьте в его нижнюю часть следующие настройки:

    -
    urlpatterns += [
    +
    urlpatterns += [
         url(r'^author/create/$', views.AuthorCreate.as_view(), name='author_create'),
         url(r'^author/(?P<pk>\d+)/update/$', views.AuthorUpdate.as_view(), name='author_update'),
         url(r'^author/(?P<pk>\d+)/delete/$', views.AuthorDelete.as_view(), name='author_delete'),
    diff --git a/files/ru/learn/server-side/django/introduction/index.html b/files/ru/learn/server-side/django/introduction/index.html
    index 34fcd1da21..b94853398f 100644
    --- a/files/ru/learn/server-side/django/introduction/index.html
    +++ b/files/ru/learn/server-side/django/introduction/index.html
    @@ -112,7 +112,7 @@ original_slug: Learn/Server-side/Django/Введение
     
     

    Сопоставитель URL-адресов обычно содержится в файле urls.py. В примере ниже сопоставитель (urlpatterns) определяет список сопоставлений междумаршрутами (определёнными URL-шаблонами) и соответствующими функциями отображения (view). Если получен HTTP-запрос, который имеет URL-адрес, соответствующий определённому шаблону, то затем будет вызвана связанная функция отображения (view) и передана в запрос.

    -
    urlpatterns = [
    +
    urlpatterns = [
         path('admin/', admin.site.urls),
         path('book/<int:id>/', views.book_detail, name='book_detail'),
         path('catalog/', include('catalog.urls')),
    @@ -131,7 +131,7 @@ original_slug: Learn/Server-side/Django/Введение
     
     

    В приведённом ниже примере показана минимальная функция представления index(), которая могла быть вызвана нашим сопоставителем URL-адресов в предыдущем разделе. Как и все функции отображения (view), она получает объект HttpRequest в качестве параметра (request) и возвращает объект HttpResponse. В этом случае мы ничего не делаем с запросом, и наш ответ просто возвращает жёстко запрограммированную строку. Мы покажем вам запрос, который делает что-то более интересное в следующем разделе.

    -
    ## filename: views.py (Django view functions)
    +
    ## filename: views.py (Django view functions)
     
     from django.http import HttpResponse
     
    @@ -162,7 +162,7 @@ def index(request):
     
     

    В приведённом ниже фрагменте кода показана очень простая модель Django для объекта Team. Класс Team наследуется от класса models.Model. Он определяет имя команды и командный уровень в качестве полей символов и задаёт максимальное количество символов, которые могут быть сохранены для каждой записи. Team_level может быть одним из нескольких значений, поэтому мы определяем его как поле выбора и предоставляем сопоставление между отображаемыми вариантами и хранимыми данными вместе со значением по умолчанию.

    -
    # filename: models.py
    +
    # filename: models.py
     
     from django.db import models
     
    @@ -194,7 +194,7 @@ class Team(models.Model):
     
     

    Фрагмент кода показывает функцию view (обработчик ресурсов) для отображения всех команд U09. Выделенная жирным строка показывает, как мы можем использовать модель API-запросов для того, чтобы отфильтровать все записи, где поле team_level в точности содержит текст 'U09' (обратите внимание, как эти критерии передаются функции filter() в качестве аргумента с именем поля и типом соответствия, разделённым двойным подчёркиванием: team_level__exact). 

    -
    ## filename: views.py
    +
    ## filename: views.py
     
     from django.shortcuts import render
     from .models import Team
    @@ -216,7 +216,7 @@ def index(request):
     
     

    Фрагмент кода показывает, как может выглядеть HTML-шаблон, вызванный функцией  render() из предыдущего раздела. Этот шаблон был написан с предположением, что во время отрисовки он будет иметь доступ к переменной списка, названной youngest_teams (содержащейся в контекстной переменной внутри функции render() выше). Внутри скелета HTML мы имеем выражение, которое сначала проверяет, существует ли переменная youngest_teams, а затем повторяет её в цикле for. При каждом повторе шаблон отображает значение team_name каждой команды в элементе {{htmlelement("li")}}.

    -
    ## filename: best/templates/best/index.html
    +
    ## filename: best/templates/best/index.html
     
     <!DOCTYPE html>
     <html lang="en">
    diff --git a/files/ru/learn/server-side/django/models/index.html b/files/ru/learn/server-side/django/models/index.html
    index 8e05924833..84f8b261d2 100644
    --- a/files/ru/learn/server-side/django/models/index.html
    +++ b/files/ru/learn/server-side/django/models/index.html
    @@ -63,7 +63,7 @@ translation_of: Learn/Server-side/Django/Models
     
     

    Модели обычно определяются в приложении models.py. Они реализуются как подклассы django.db.models.Model, и могут включать поля, методы и метаданные. В приведённом ниже фрагменте кода показана «типичная» модель, названная MyModelName:

    -
    from django.db import models
    +
    from django.db import models
     
     class MyModelName(models.Model):
         """
    @@ -97,7 +97,7 @@ class MyModelName(models.Model):
     
     

    Модель может иметь произвольное количество полей любого типа - каждый представляет столбец данных, который мы хотим сохранить в одной из наших таблиц базы данных. Каждая запись (строка) базы данных будет состоять из одного значения каждого поля. Давайте рассмотрим приведённый выше пример:

    -
    my_field_name = models.CharField(max_length=20, help_text="Enter field documentation")
    +
    my_field_name = models.CharField(max_length=20, help_text="Enter field documentation")

    Наш вышеприведённый пример имеет одно поле, называемое my_field_name, типа models.CharField — что означает, что это поле будет содержать строки буквенно-цифровых символов. Типы полей назначаются с использованием определённых классов, которые определяют тип записи, которая используется для хранения данных в базе данных, а также критерии проверки, которые должны использоваться, когда значения получены из формы HTML (то есть, что составляет действительное значение). Типы полей также могут принимать аргументы, которые дополнительно определяют, как поле хранится или может использоваться. В этом случае мы даём нашему полю два аргумента:

    @@ -148,7 +148,7 @@ class MyModelName(models.Model):

    Вы можете объявить метаданные на уровне модели для своей модели, объявив класс Meta, как показано на рисунке.

    -
    class Meta:
    +
    class Meta:
         ordering = ["-my_field_name"]
         ...
    @@ -156,13 +156,13 @@ class MyModelName(models.Model):

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

    -
    ordering = ["title", "-pubdate"]
    +
    ordering = ["title", "-pubdate"]

    Книги будут отсортированы по алфавиту по названию, от A-Z, а затем по дате публикации внутри каждого названия, от самого нового до самого старого.

    Другим распространённым атрибутом является verbose_name, подробное имя для класса в единственной и множественной форме:

    -
    verbose_name = "BetterName"
    +
    verbose_name = "BetterName"

    Другие полезные атрибуты позволяют создавать и применять новые «разрешения доступа» для модели (разрешения по умолчанию применяются автоматически), разрешить упорядочение на основе другого поля или объявить, что класс является «абстрактным» (базовый класс, для которого вы не можете создавать записи, и вместо этого будет создан для создания других моделей). Многие другие параметры метаданных управляют тем, какая база данных должна использоваться для модели и как хранятся данные (это действительно полезно, если вам нужно сопоставить модель с существующей базой данных). Полный список опций метаданных доступен здесь: Model metadata options (Django документация).

    @@ -170,12 +170,12 @@ class MyModelName(models.Model):

    Модель также может иметь методы. Минимально в каждой модели вы должны определить стандартный метод класса для Python __str __ (), чтобы вернуть удобочитаемую строку для каждого объекта. Эта строка используется для представления отдельных записей на сайте администрирования (и в любом другом месте, где вам нужно обратиться к экземпляру модели). Часто это возвращает поле названия или имени из модели.

    -
    def __str__(self):
    +
    def __str__(self):
         return self.field_name

    Другим распространённым методом включения в модели Django является get_absolute_url (), который возвращает URL-адрес для отображения отдельных записей модели на веб-сайте (если вы определяете этот метод, тогда Django автоматически добавит кнопку «Просмотр на сайте» на экранах редактирования записей модели на сайте администратора). Типичный шаблон для get_absolute_url () показан ниже.

    -
    def get_absolute_url(self):
    +
    def get_absolute_url(self):
         """
         Returns the url to access a particular instance of the model.
         """
    @@ -198,7 +198,7 @@ class MyModelName(models.Model):
     
     

    Чтобы создать запись, вы можете определить экземпляр модели, а затем вызвать метод save ().

    -
    # Create a new record using the model's constructor.
    +
    # Create a new record using the model's constructor.
     a_record = MyModelName(my_field_name="Instance #1")
     
     # Save the object into the database.
    @@ -211,7 +211,7 @@ a_record.save()
     
     

    Вы можете получить доступ к полям в этой новой записи с использованием синтаксиса точек и изменить значения. Вы должны вызвать save (), чтобы сохранить изменённые значения в базе данных.

    -
    # Access model field values using Python attributes.
    +
    # Access model field values using Python attributes.
     print(a_record.id) #should return 1 for the first record.
     print(a_record.my_field_name) # should print 'Instance #1'
     
    @@ -229,12 +229,12 @@ a_record.save()

    Мы можем получить все записи для модели как объект QuerySet,  используя objects.all(). QuerySet - это итерируемый объект, означающий, что он содержит несколько объектов, которые мы можем перебирать / прокручивать.

    -
    all_books = Book.objects.all()
    +
    all_books = Book.objects.all()
     

    Метод filter() Django позволяет отфильтровать возвращаемый QuerySet для соответствия указанному текстовому или числовому полю по конкретным критериям. Например, чтобы отфильтровать книги, содержащие  слово «wild» («дикие») в заголовке, а затем подсчитать их, мы могли бы сделать следующее.

    -
    wild_books = Book.objects.filter(title__contains='wild')
    +
    wild_books = Book.objects.filter(title__contains='wild')
     number_wild_books = Book.objects.filter(title__contains='wild').count()
     
    @@ -242,7 +242,7 @@ number_wild_books = Book.objects.filter(title__contains='wild').count()

    В некоторых случаях вам нужно будет фильтровать поле, которое определяет отношение «один ко многим» к другой модели (например, ForeignKey). В этом случае вы можете «индексировать» поля в связанной модели с дополнительными двойными подчёркиваниями. Так, например, чтобы фильтровать книги с определённым жанровым рисунком, вам нужно будет указывать имя в поле жанра, как показано ниже:

    -
    books_containing_genre = Book.objects.filter(genre__name__icontains='fiction')  # Will match on: Fiction, Science fiction, non-fiction etc.
    +
    books_containing_genre = Book.objects.filter(genre__name__icontains='fiction')  # Will match on: Fiction, Science fiction, non-fiction etc.
     
    @@ -255,7 +255,7 @@ number_wild_books = Book.objects.filter(title__contains='wild').count()

    В этом разделе мы начнём определять модели для библиотеки. Откройте models.py (в / locallibrary / catalog /). Шаблон в верхней части страницы импортирует модуль моделей, который содержит базовый класс модели models.Model, от которого наследуются наши модели.

    -
    from django.db import models
    +
    from django.db import models
     
     # Create your models here.
    @@ -263,7 +263,7 @@ number_wild_books = Book.objects.filter(title__contains='wild').count()

    Скопируйте приведённый ниже код модели Genre и вставьте его в нижнюю часть вашего файла models.py. Эта модель используется для хранения информации о категории книг - например, будь то художественная или документальная, роман или военно-историческая и т. д. Как уже упоминалось выше, мы создали жанр как модель, а не как свободный текст или список выбора, чтобы возможные значения могли управляться через базу данных, а не были закодированными.

    -
    class Genre(models.Model):
    +
    class Genre(models.Model):
         """
         Model representing a book genre (e.g. Science Fiction, Non Fiction).
         """
    @@ -281,7 +281,7 @@ number_wild_books = Book.objects.filter(title__contains='wild').count()
     
     

    Скопируйте модель книги ниже и снова вставьте её в нижнюю часть файла. Модель книги представляет всю информацию о доступной книге в общем смысле, но не конкретный физический «экземпляр» или «копию» для временного использования. Модель использует CharField для представления названия книги и isbn (обратите внимание, как isbn указывает свой ярлык как «ISBN», используя первый неименованный параметр, поскольку в противном случае ярлык по умолчанию был бы «Isbn»). Модель использует TextField для summary, потому что этот текст, возможно, должен быть очень длинным.

    -
    from django.urls import reverse #Used to generate URLs by reversing the URL patterns
    +
    from django.urls import reverse #Used to generate URLs by reversing the URL patterns
     
     class Book(models.Model):
         """
    @@ -326,7 +326,7 @@ class Book(models.Model):
      
  • CharField, для представления данных (конкретного выпуска) о книге.
-
import uuid # Required for unique book instances
+
import uuid # Required for unique book instances
 
 class BookInstance(models.Model):
     """
@@ -382,7 +382,7 @@ class BookInstance(models.Model):
 
 

Теперь все поля/методы должны быть знакомы. Модель определяет автора как имя, фамилию, дату рождения и (необязательную) дату смерти. Он указывает, что по умолчанию __str __ () возвращает имя в фамилии, порядковый номер первого имени. Метод get_absolute_url () отменяет сопоставление URL-адреса автора с целью получения URL-адреса для отображения отдельного автора.

-
class Author(models.Model):
+
class Author(models.Model):
     """
     Model representing an author.
     """
@@ -409,7 +409,7 @@ class BookInstance(models.Model):
 
 

Теперь все ваши модели созданы. Теперь переустановите миграцию базы данных, чтобы добавить их в свою базу данных.

-
python3 manage.py makemigrations
+
python3 manage.py makemigrations
 python3 manage.py migrate

Языковая модель - вызов

diff --git a/files/ru/learn/server-side/express_nodejs/development_environment/index.html b/files/ru/learn/server-side/express_nodejs/development_environment/index.html index c0c188f731..626090c6a0 100644 --- a/files/ru/learn/server-side/express_nodejs/development_environment/index.html +++ b/files/ru/learn/server-side/express_nodejs/development_environment/index.html @@ -85,7 +85,7 @@ translation_of: Learn/Server-side/Express_Nodejs/development_environment

Самый простой способ установить последнюю версию LTS Node 6.x - это использовать package manager чтобы получить его из репозитория бинарных дистрибутивов Ubuntu. Это можно сделать очень просто, выполнив следующие две команды на вашем терминале:

-
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
+
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
 sudo apt-get install -y nodejs
 
@@ -100,12 +100,12 @@ sudo apt-get install -y nodejs

Самый простой способ проверить, установлен ли этот узел, - это запустить команду «версия» в своём терминале / командной строке и проверить, что возвращается строка версии:

-
>node -v
+
>node -v
 v8.11.3

Менеджер пакетов Nodejs NPM также должен быть установлен и может быть протестирован таким же образом:

-
>npm -v
+
>npm -v
 5.8.0

В качестве немного более захватывающего теста давайте создадим очень простой сервер «чистого узла», который просто печатает «Hello World» в браузере, когда вы посещаете правильный URL в вашем браузере:

@@ -113,7 +113,7 @@ v8.11.3
  1. Скопируйте следующий текст в файл с именем hellonode.js. Здесь используются чистые функции Node (ничего из Express) и некоторый синтаксис ES6: -
    //Загрузка модуля HTTP
    +  
    //Загрузка модуля HTTP
     const http = require("http");
     const hostname = '127.0.0.1';
     const port = 3000;
    @@ -141,7 +141,7 @@ server.listen(port, hostname, () => {
       
  • Запустите сервер, перейдя в тот же каталог, что и ваш файл hellonode.js в командной строке, и вызвав узел вместе с именем скрипта, например так: -
    >node hellonode.js
    +  
    >node hellonode.js
     Server running at http://127.0.0.1:3000/
     
  • @@ -169,15 +169,15 @@ Server running at http://127.0.0.1:3000/
    1. Сначала создайте каталог для вашего нового приложения и перейдите в него: -
      mkdir myapp
      +  
      mkdir myapp
       cd myapp
    2. Используйте команду npm init для создания файла package.json для вашего приложения. Эта команда запрашивает у вас несколько вещей, включая имя и версию вашего приложения, а также имя исходного файла точки входа (по умолчанию это index.js). Сейчас просто примите значения по умолчанию: -
      npm init
      +
      npm init

      Если вы отобразите файл package.json (cat package.json), вы увидите принятые по умолчанию значения, заканчивающиеся лицензией.

      -
      {
      +  
      {
         "name": "myapp",
         "version": "1.0.0",
         "description": "",
      @@ -192,11 +192,11 @@ cd myapp
    3. Теперь установите Express в каталог myapp и сохраните его в списке зависимостей вашего файла package.json
    4. -
      npm install express --save
      +
      npm install express --save

      Раздел зависимостей вашего package.json теперь появится в конце файла package.json и будет содержать Express.

      -
      {
      +  
      {
         "name": "myapp",
         "version": "1.0.0",
         "description": "",
      @@ -213,7 +213,7 @@ cd myapp
    5. Для использования библиотеки вы вызываете функцию require (), как показано ниже в вашем файле index.js. -
      const express = require('express')
      +  
      const express = require('express')
       const app = express();
       
       app.get('/', (req, res) => {
      @@ -230,7 +230,7 @@ app.listen(8000, () => {
          Создайте файл с именем index.js в корне каталога приложения «myapp» и передайте ему содержимое, показанное выше.

    6. Вы можете запустить сервер, вызвав узел с помощью скрипта в командной строке: -
      >node index.js
      +  
      >node index.js
       Example app listening on port 8000
       
    7. @@ -241,11 +241,11 @@ Example app listening on port 8000

      Если зависимость используется только во время разработки, вы должны вместо этого сохранить её как «зависимость разработки» (чтобы пользователям вашего пакета не приходилось устанавливать её в производстве). Например, чтобы использовать популярный инструмент JavaScript Linting eslint, вы должны вызвать NPM, как показано ниже:

      -
      npm install eslint --save-dev
      +
      npm install eslint --save-dev

      Следующая запись будет добавлена в package.json вашего приложения:

      -
        "devDependencies": {
      +
        "devDependencies": {
           "eslint": "^4.12.1"
         }
       
      @@ -264,7 +264,7 @@ Example app listening on port 8000

      Например, чтобы определить скрипт для запуска зависимости разработки eslint, которую мы указали в предыдущем разделе, мы могли бы добавить следующий блок скрипта в наш файл package.json (при условии, что наш источник приложения находится в папке / src / js):

      -
      "scripts": {
      +
      "scripts": {
         ...
         "lint": "eslint src/js"
         ...
      @@ -275,7 +275,7 @@ Example app listening on port 8000
       
       

      Затем мы сможем запустить eslint с помощью NPM, вызвав:

      -
      npm run-script lint
      +
      npm run-script lint
       # OR (using the alias)
       npm run lint
       
      @@ -286,16 +286,16 @@ npm run lint

      Инструмент Express Application Generator создаёт «скелет» приложения Express. Установите генератор, используя NPM, как показано (флаг -g устанавливает инструмент глобально, чтобы вы могли вызывать его из любого места):

      -
      npm install express-generator -g
      +
      npm install express-generator -g

      Чтобы создать приложение Express с именем «helloworld» с настройками по умолчанию, перейдите туда, где вы хотите его создать, и запустите приложение, как показано ниже:

      -
      express helloworld
      +
      express helloworld

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

      -
      express --help
      +
      express --help
       
      @@ -304,7 +304,7 @@ npm run lint

      Новое приложение будет иметь файл package.json в своём корневом каталоге. Вы можете открыть это, чтобы увидеть, какие зависимости установлены, включая Express и библиотеку шаблонов Jade:

      -
      {
      +
      {
         "name": "helloworld",
         "version": "0.0.0",
         "private": true,
      @@ -325,13 +325,13 @@ npm run lint
       
       

      Установите все зависимости для приложения helloworld, используя NPM, как показано ниже:

      -
      cd helloworld
      +
      cd helloworld
       npm install
       

      Затем запустите приложение (команды немного отличаются для Windows и Linux / macOS), как показано ниже:

      -
      # Run the helloworld on Windows with Command Prompt
      +
      # Run the helloworld on Windows with Command Prompt
       SET DEBUG=helloworld:* & npm start
       
       # Run the helloworld on Windows with PowerShell
      @@ -343,7 +343,7 @@ DEBUG=helloworld:* npm start
       
       

      Команда DEBUG создаёт полезное ведение журнала, что приводит к выводу, подобному показанному ниже.

      -
      >SET DEBUG=helloworld:* & npm start
      +
      >SET DEBUG=helloworld:* & npm start
       
       > helloworld@0.0.0 start D:\Github\expresstests\helloworld
       > node ./bin/www
      diff --git a/files/ru/learn/server-side/express_nodejs/displaying_data/locallibrary_base_template/index.html b/files/ru/learn/server-side/express_nodejs/displaying_data/locallibrary_base_template/index.html
      index c8b39b2872..830c895bc8 100644
      --- a/files/ru/learn/server-side/express_nodejs/displaying_data/locallibrary_base_template/index.html
      +++ b/files/ru/learn/server-side/express_nodejs/displaying_data/locallibrary_base_template/index.html
      @@ -7,7 +7,7 @@ translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/LocalLibrary_ba
       
       

      Откройте файл /views/layout.pug и замените его содержимое следующим.

      -
      doctype html
      +
      doctype html
       html(lang='en')
         head
           title= title
      @@ -53,7 +53,7 @@ html(lang='en')
       
       

      Базовый шаблон также ссылается на локальный файл стилей (style.css), что обеспечивает дополнительное управление внешним видом. Откройте /public/stylesheets/style.css и замените его содержимое таким текстом:

      -
      .sidebar-nav {
      +
      .sidebar-nav {
           margin-top: 20px;
           padding: 0;
           list-style: none;
      diff --git a/files/ru/learn/server-side/express_nodejs/forms/index.html b/files/ru/learn/server-side/express_nodejs/forms/index.html
      index 1a6208f065..910f6a53ed 100644
      --- a/files/ru/learn/server-side/express_nodejs/forms/index.html
      +++ b/files/ru/learn/server-side/express_nodejs/forms/index.html
      @@ -46,7 +46,7 @@ translation_of: Learn/Server-side/Express_Nodejs/forms
       
       

      Определённые в  HTML формы собираются внутри тэга <form>...</form>, содержащего хотя ы один элемент input с type="submit".

      -
      <form action="/team_name_url/" method="post">
      +
      <form action="/team_name_url/" method="post">
           <label for="team_name">Enter name: </label>
           <input id="team_name" type="text" name="name_field" value="Default name for team.">
           <input type="submit" value="OK">
      @@ -108,7 +108,7 @@ translation_of: Learn/Server-side/Express_Nodejs/forms
       
       

      Установите модуль, выполнив следующую команду в корне проекта

      -
      npm install express-validator
      +
      npm install express-validator
       

      Использование express-validator

      @@ -119,7 +119,7 @@ translation_of: Learn/Server-side/Express_Nodejs/forms

      Для того, чтобы использовать валидатор в наших контроллерах, мы должны требовать функции, которые мы хотим использовать из модулей 'express-validator/check' и 'express-validator/filter', как показано ниже:

      -
      const { body,validationResult } = require('express-validator/check');
      +
      const { body,validationResult } = require('express-validator/check');
       const { sanitizeBody } = require('express-validator/filter');
       
      @@ -130,12 +130,12 @@ const { sanitizeBody } = require('express-validator/filter');
      • body(fields[, message]): Задаёт набор полей в теле запроса (параметр POST) для проверки, а также необязательное сообщение об ошибке, которое может отображаться в случае сбоя тестов. Критерии проверки последовательно связаны с методом body(). Например, первая проверка ниже проверяет, что поле" имя "не пустое и задаёт сообщение об ошибке" пустое имя", если оно не пустое. Второй тест проверяет, что поле age является допустимой датой, и с помощью optional() указывает, что пустые и пустые строки не пройдут проверку. -
        body('name', 'Empty name').isLength({ min: 1 }),
        +  
        body('name', 'Empty name').isLength({ min: 1 }),
         body('age', 'Invalid age').optional({ checkFalsy: true }).isISO8601(),
         
        Можно также последовательно подключить различные валидаторы и добавить сообщения, отображаемые при выполнении предыдущих валидаторов.
      • -
        body('name').isLength({ min: 1 }).trim().withMessage('Name empty.')
        +  
        body('name').isLength({ min: 1 }).trim().withMessage('Name empty.')
             .isAlpha().withMessage('Name must be alphabet letters.'),
         
        @@ -144,11 +144,11 @@ body('age', 'Invalid age').optional({ checkFalsy: true }).isISO8601(),
    8. sanitizeBody(fields): Задаёт поле тела для очистки. затем операции очистки последовательно соединяются с этим методом. Например, операция очистки escape(), описанная ниже, удаляет символы HTML из переменной name, которые могут использоваться в атаках сценариев между сайтами JavaScript. -
      sanitizeBody('name').trim().escape(),
      +  
      sanitizeBody('name').trim().escape(),
       sanitizeBody('date').toDate(),
    9. validationResult(req): Запускает проверку, делая ошибки доступными в виде объекта результата проверки. Это вызывается в отдельном обратном вызове, как показано ниже: -
      (req, res, next) => {
      +  
      (req, res, next) => {
           // Extract the validation errors from a request.
           const errors = validationResult(req);
       
      @@ -193,7 +193,7 @@ sanitizeBody('date').toDate(),

      Мы уже создали маршруты для всех страниц создания нашей модели в  /routes/catalog.js (in a previous tutorial).  Например, жанровые маршруты показаны ниже:

      -
      // GET request for creating a Genre. NOTE This must come before route that displays Genre (uses id).
      +
      // GET request for creating a Genre. NOTE This must come before route that displays Genre (uses id).
       router.get('/genre/create', genre_controller.genre_create_get);
       
       // POST request for creating Genre.
      diff --git a/files/ru/learn/server-side/express_nodejs/introduction/index.html b/files/ru/learn/server-side/express_nodejs/introduction/index.html
      index 881a3c70d6..cbcafaa64a 100644
      --- a/files/ru/learn/server-side/express_nodejs/introduction/index.html
      +++ b/files/ru/learn/server-side/express_nodejs/introduction/index.html
      @@ -46,13 +46,13 @@ translation_of: Learn/Server-side/Express_Nodejs/Introduction
        
    10. Создайте папку, куда вы хотите сохранить программу, к примеру test-node и перейдите в неё с помощью следующей команды:
    -
    cd test-node
    +
    cd test-node
    1. Используя любимый текстовый редактор, создайте файл hello.js и вставьте в него код:
    -
    // Загружаем HTTP модуль
    +
    // Загружаем HTTP модуль
     const http = require("http");
     
     const hostname = "127.0.0.1";
    @@ -79,7 +79,7 @@ server.listen(port, hostname, () => {
      
  • Вернитесь в терминал и выполните следующую команду:
  • -
    node hello.js
    +
    node hello.js

    В итоге, перейдите по ссылке http://localhost:8000 в вашем браузере; вы должны увидеть текст "Hello World" в верху слева на чистой странице.

    @@ -144,7 +144,7 @@ server.listen(port, hostname, () => {

    Совет: Если у вас уже установлены Node и Express (или если вы устанавливаете их, как показано в следующей статье), вы можете сохранить этот код в файле с именем app.js и запустить его в командной строке, вызвав узел app.js. отражения).

    -
    var express = require('express');
    +
    var express = require('express');
     var app = express();
     
     app.get('/', function(req, res) {
    @@ -168,7 +168,7 @@ app.listen(3000, function() {
      
    Приведённый ниже код показывает, как мы импортируем модуль по имени, используя в качестве примера платформу Express. Сначала мы вызываем функцию require (), определяя имя модуля в виде строки («express») и вызывая возвращённый объект для создания приложения Express. Затем мы можем получить доступ к свойствам и функциям объекта приложения.

    -
    var express = require('express');
    +
    var express = require('express');
     var app = express();
     
    @@ -180,13 +180,13 @@ var app = express();

    Чтобы сделать объекты доступными вне модуля, вам просто нужно назначить их объекту экспорта. Например, модуль square.js ниже представляет собой файл, который экспортирует методы area () и perimeter ():

    -
    exports.area = function(width) { return width * width; };
    +
    exports.area = function(width) { return width * width; };
     exports.perimeter = function(width) { return 4 * width; };
     

    Мы можем импортировать этот модуль, используя require (), а затем вызвать экспортированные методы, как показано:

    -
    var square = require('./square'); // Here we require() the name of the file without the (optional) .js file extension
    +
    var square = require('./square'); // Here we require() the name of the file without the (optional) .js file extension
     console.log('The area of a square with a width of 4 is ' + square.area(4));
    @@ -195,7 +195,7 @@ console.log('The area of a square with a width of 4 is ' + square.area(4));
    Если вы хотите экспортировать полный объект в одном назначении, а не создавать его по одному свойству за раз, назначьте его для module.exports, как показано ниже (вы также можете сделать это, чтобы сделать корень объекта экспорта конструктором или другой функцией) :

    -
    module.exports = {
    +
    module.exports = {
       area: function(width) {
         return width * width;
       },
    @@ -212,13 +212,13 @@ console.log('The area of a square with a width of 4 is ' + square.area(4));
    Код JavaScript часто использует асинхронные, а не синхронные API для операций, выполнение которых может занять некоторое время. Синхронный API - это тот, в котором каждая операция должна завершиться до начала следующей операции. Например, следующие функции журнала являются синхронными и выводят текст на консоль по порядку (первый, второй).

    -
    console.log('First');
    +
    console.log('First');
     console.log('Second');
     

    В отличие от этого, асинхронный API - это тот, в котором API начнёт операцию и сразу же вернётся (до завершения операции). После завершения операции API будет использовать некоторый механизм для выполнения дополнительных операций. Например, приведённый ниже код выведет «Second, First», потому что хотя метод setTimeout () вызывается первым и возвращается немедленно, операция не завершается в течение нескольких секунд.

    -
    setTimeout(function() {
    +
    setTimeout(function() {
        console.log('First');
        }, 3000);
     console.log('Second');
    @@ -240,7 +240,7 @@ console.log('Second');
     
     

    В нашем примере Hello World Express (см. Выше) мы определили функцию обработчика маршрута (колбэка) для HTTP-запросов GET к корню сайта ('/').

    -
    app.get('/', function(req, res) {
    +
    app.get('/', function(req, res) {
       res.send('Hello World!');
     });
     
    @@ -255,7 +255,7 @@ console.log('Second');

    Существует специальный метод маршрутизации app.all (), который будет вызываться в ответ на любой метод HTTP. Это используется для загрузки функций промежуточного программного обеспечения по определённому пути для всех методов запроса. В следующем примере (из документации Express) показан обработчик, который будет выполняться для запросов к / secret независимо от используемого глагола HTTP (при условии, что он поддерживается модулем http).

    -
    app.all('/secret', function(req, res, next) {
    +
    app.all('/secret', function(req, res, next) {
       console.log('Accessing the secret section ...');
       next(); // pass control to the next handler
     });
    @@ -264,7 +264,7 @@ console.log('Second');
    Часто полезно группировать обработчики маршрутов для определённой части сайта и получать к ним доступ с помощью общего префикса маршрута (например, сайт с вики может иметь все связанные с вики маршруты в одном файле и иметь к ним доступ с префиксом маршрута из / вики /). В Express это достигается с помощью объекта express.Router. Например, мы можем создать наш вики-маршрут в модуле с именем wiki.js, а затем экспортировать объект Router, как показано ниже:

    -
    // wiki.js - Wiki route module
    +
    // wiki.js - Wiki route module
     
     var express = require('express');
     var router = express.Router();
    @@ -288,7 +288,7 @@ module.exports = router;
     
     

    Чтобы использовать маршрутизатор в нашем главном файле приложения, нам потребуется () модуль route (wiki.js), а затем вызовите use () в приложении Express, чтобы добавить маршрутизатор в путь обработки промежуточного программного обеспечения. Эти два маршрута будут доступны из / wiki / и / wiki / about /.

    -
    var wiki = require('./wiki.js');
    +
    var wiki = require('./wiki.js');
     // ...
     app.use('/wiki', wiki);
    @@ -306,12 +306,12 @@ app.use('/wiki', wiki);

    Для использования стороннего промежуточного программного обеспечения сначала необходимо установить его в своё приложение с помощью NPM. Например, чтобы установить промежуточное программное обеспечение средства регистрации HTTP-запросов morgan, вы должны сделать следующее:

    -
    $ npm install morgan
    +
    $ npm install morgan
     

    Затем вы можете вызвать use () для объекта приложения Express, чтобы добавить промежуточное программное обеспечение в стек:

    -
    var express = require('express');
    +
    var express = require('express');
     var logger = require('morgan');
     var app = express();
     app.use(logger('dev'));
    @@ -327,7 +327,7 @@ var app = express();
      
    В приведённом ниже примере показано, как можно добавить функцию промежуточного программного обеспечения, используя оба метода, а также с / без маршрута.

    -
    var express = require('express');
    +
    var express = require('express');
     var app = express();
     
     // An example middleware function
    @@ -357,12 +357,12 @@ app.listen(3000);

    Вы можете использовать промежуточное программное обеспечение express.static для обслуживания статических файлов, включая ваши изображения, CSS и JavaScript (static () - единственная функция промежуточного программного обеспечения, которая фактически является частью Express). Например, вы должны использовать строку ниже для обслуживания изображений, файлов CSS и файлов JavaScript из каталога с именем public на том же уровне, где вы вызываете узел:

    -
    app.use(express.static('public'));
    +
    app.use(express.static('public'));
     

    Любые файлы в публичном каталоге обслуживаются путём добавления их имени файла (относительно базового «публичного» каталога) к базовому URL. Так, например:

    -
    http://localhost:3000/images/dog.jpg
    +
    http://localhost:3000/images/dog.jpg
     http://localhost:3000/css/style.css
     http://localhost:3000/js/app.js
     http://localhost:3000/about.html
    @@ -370,18 +370,18 @@ http://localhost:3000/about.html
     
     

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

    -
    app.use(express.static('public'));
    +
    app.use(express.static('public'));
     app.use(express.static('media'));
     

    Вы также можете создать виртуальный префикс для ваших статических URL-адресов, вместо добавления файлов к базовому URL-адресу. Например, здесь мы указываем путь монтирования, чтобы файлы загружались с префиксом "/ media":

    -
    app.use('/media', express.static('public'));
    +
    app.use('/media', express.static('public'));
     

    Теперь вы можете загружать файлы, находящиеся в публичном каталоге, из префикса / media path.

    -
    http://localhost:3000/media/images/dog.jpg
    +
    http://localhost:3000/media/images/dog.jpg
     http://localhost:3000/media/video/cat.mp4
     http://localhost:3000/media/cry.mp3
     
    @@ -392,7 +392,7 @@ http://localhost:3000/media/cry.mp3

    Ошибки обрабатываются одной или несколькими специальными функциями промежуточного программного обеспечения, которые имеют четыре аргумента вместо обычных трёх: (err, req, res, next). Например:

    -
    app.use(function(err, req, res, next) {
    +
    app.use(function(err, req, res, next) {
       console.error(err.stack);
       res.status(500).send('Something broke!');
     });
    @@ -418,12 +418,12 @@ http://localhost:3000/media/cry.mp3
      
    Чтобы использовать их, вы должны сначала установить драйвер базы данных, используя NPM. Например, чтобы установить драйвер для популярной NoSQL MongoDB, вы должны использовать команду:

    -
    $ npm install mongodb
    +
    $ npm install mongodb
     

    Сама база данных может быть установлена локально или на облачном сервере. В вашем экспресс-коде вам требуется драйвер, подключиться к базе данных, а затем выполнить операции создания, чтения, обновления и удаления (CRUD). Пример ниже (из документации Express) показывает, как вы можете найти записи «млекопитающих», используя MongoDB.

    -
    var MongoClient = require('mongodb').MongoClient;
    +
    var MongoClient = require('mongodb').MongoClient;
     
     MongoClient.connect('mongodb://localhost:27017/animals', function(err, db) {
       if (err) throw err;
    @@ -445,7 +445,7 @@ MongoClient.connect('mongodb://localhost:27017/animals', function(err, db) {
      
    В своём коде настроек приложения вы задаёте механизм шаблонов для использования и место, где Express должен искать шаблоны, используя настройки «views» и «engine», как показано ниже (вам также нужно будет установить пакет, содержащий вашу библиотеку шаблонов). !)

    -
    var express = require('express');
    +
    var express = require('express');
     var app = express();
     
     // Set directory to contain the templates ('views')
    @@ -457,7 +457,7 @@ app.set('view engine', 'some_template_engine_name');
     
     

    Внешний вид шаблона будет зависеть от того, какой движок вы используете. Предполагая, что у вас есть файл шаблона с именем «index. <Template_extension>», который содержит заполнители для переменных данных с именами «title» и «message», вы должны вызвать Response.render () в функции обработчика маршрута для создания и отправки ответа HTML. :

    -
    app.get('/', function(req, res) {
    +
    app.get('/', function(req, res) {
       res.render('index', { title: 'About dogs', message: 'Dogs rock!' });
     });
    diff --git a/files/ru/learn/server-side/express_nodejs/skeleton_website/index.html b/files/ru/learn/server-side/express_nodejs/skeleton_website/index.html index f1c993e54d..90ec704439 100644 --- a/files/ru/learn/server-side/express_nodejs/skeleton_website/index.html +++ b/files/ru/learn/server-side/express_nodejs/skeleton_website/index.html @@ -36,12 +36,12 @@ translation_of: Learn/Server-side/Express_Nodejs/skeleton_website

    Вы уже должны были установить express-generator, читая статью установка среды разработки Node. Напомним, что генератор установлен с помощью менеджера пакетов NPM, при выполнении команды:

    -
    npm install express-generator -g
    +
    npm install express-generator -g
     

    Express-generator имеет ряд параметров, которые можно увидеть, выполнив команду express --help (или express -h):

    -
    > express --help
    +
    > express --help
     
       Usage: express [options] [dir]
     
    @@ -63,7 +63,7 @@ translation_of: Learn/Server-side/Express_Nodejs/skeleton_website
     
     

    Команда express создаст проект в текущем каталоге с использованием (устаревшего) движка представления Jade и обычного CSS. Если указать express name, проект будет создан в подкаталоге name текущего каталога. 

    -
    express
    +
    express

    Можно выбрать движок представления (шаблон), используя --view; параметр --css позволяет выбрать движок для создания CSS.

    @@ -127,12 +127,12 @@ translation_of: Learn/Server-side/Express_Nodejs/skeleton_website

    Выберем место для нового проекта — каталог express-locallibrary-tutorial - и выполним команду:

    -
    express express-locallibrary-tutorial --view=pug
    +
    express express-locallibrary-tutorial --view=pug
     

    Будет создан каталог express-locallibrary-tutorial и выведен список созданных внутри каталога проектных файлов.

    -
       create : express-locallibrary-tutorial
    +
       create : express-locallibrary-tutorial
        create : express-locallibrary-tutorial/package.json
        create : express-locallibrary-tutorial/app.js
        create : express-locallibrary-tutorial/public/images
    @@ -169,16 +169,16 @@ translation_of: Learn/Server-side/Express_Nodejs/skeleton_website
     
    1. Прежде всего установим зависимости (команда install запросит все пакеты зависимостей, указанные в файле package.json). -
      cd express-locallibrary-tutorial
      +  
      cd express-locallibrary-tutorial
       npm install
    2. Затем запустим приложение.
      • В Windows используйте команду: -
        SET DEBUG=express-locallibrary-tutorial:* & npm start
        +
        SET DEBUG=express-locallibrary-tutorial:* & npm start
      • В Mac OS X или Linux используйте команду: -
        DEBUG=express-locallibrary-tutorial:* npm start
        +    
        DEBUG=express-locallibrary-tutorial:* npm start
         
      @@ -193,7 +193,7 @@ npm install

    Примечание: Можно также запустить приложение командой npm start. Переменная DEBUG, указанная в примере, включает логирование в консоль для дальнейшей отладки. Так, при посещении страницы веб-приложения, вы увидите похожий вывод в консоль:

    -
    >SET DEBUG=express-locallibrary-tutorial:* & npm start
    +
    >SET DEBUG=express-locallibrary-tutorial:* & npm start
     
     > express-locallibrary-tutorial@0.0.0 start D:\express-locallibrary-tutorial
     > node ./bin/www
    @@ -212,7 +212,7 @@ GET /favicon.ico 404 34.134 ms - 1335

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

    -
    npm install --save-dev nodemon
    +
    npm install --save-dev nodemon
    @@ -220,20 +220,20 @@ GET /favicon.ico 404 34.134 ms - 1335
    -
    npm install -g nodemon
    +
    npm install -g nodemon

    В файле package.json проекта появится  новый раздел с этой зависимостью (на вашей машине номер версии nodemon может быть другим) :

    -
      "devDependencies": {
    +
      "devDependencies": {
         "nodemon": "^1.11.0"
       }
     

    Поскольку nodemon не установлен глобально, его нельзя запустить из командной строки (пока мы не добавим его в путь), но его можно вызвать из сценария NPM, так как NPM знает все об установленных пакетах.  Раздел  scripts в файле package.json исходно будет содержать одну строку, которая начинается с  "start". Обновите его, поставив запятую в конце строки, и добавьте строку "devstart",  показанную ниже:

    -
      "scripts": {
    +
      "scripts": {
         "start": "node ./bin/www",
         "devstart": "nodemon ./bin/www"
       },
    @@ -245,13 +245,13 @@ GET /favicon.ico 404 34.134 ms - 1335
  • В Windows:
  • -
    SET DEBUG=express-locallibrary-tutorial:* & npm run devstart
    +
    SET DEBUG=express-locallibrary-tutorial:* & npm run devstart
    • Для macOS или Linux:
    -
    DEBUG=express-locallibrary-tutorial:* npm run devstart
    +
    DEBUG=express-locallibrary-tutorial:* npm run devstart

    Примечание: Сейчас после изменения любого файла проекта сервер будет перезапускаться (или можно самостоятельно перезапустить его, введя rs в командной строке). Вам всё равно придётся обновить страницу в браузере .

    @@ -267,7 +267,7 @@ GET /favicon.ico 404 34.134 ms - 1335

    После установки зависимостей проект имеет такую структуру файлов (файлы - это элементы без префикса"/"). Файл package.json определяет имя файла с приложением, сценарии запуска, зависимости и др.  Сценарий запуска задаёт точку входа приложения, у нас -- файл JavaScript /bin/www. Этот файл настраивает некоторые обработчики ошибок приложения, а затем загружает  app.js для выполнения остальной работы. Пути приложения хранятся в отдельных модулях каталога routes/.  Шаблоны хранятся в каталоге /views.

    -
    /express-locallibrary-tutorial
    +
    /express-locallibrary-tutorial
         app.js
         /bin
             www
    @@ -295,7 +295,7 @@ GET /favicon.ico 404 34.134 ms - 1335

    Файл package.json указывает зависимости приложения и содержит другие данные:

    -
    {
    +
    {
       "name": "express-locallibrary-tutorial",
       "version": "0.0.0",
       "private": true,
    @@ -329,7 +329,7 @@ GET /favicon.ico 404 34.134 ms - 1335

    Раздел "scripts" определяет скрипт" start", выполняемый при запуске сервера командой npm start.  Можно видеть, что самом деле выполняется команда node ./bin/www.  Кроме того, определяется script "devstart", который вызывается командой npm run devstart. Запускается тот же файл ./bin/www ,но командой nodemon вместо node.

    -
    "scripts": {
    +
    "scripts": {
         "start": "node ./bin/www",
         "devstart": "nodemon ./bin/www"
       },
    @@ -338,7 +338,7 @@ GET /favicon.ico 404 34.134 ms - 1335

    Файл /bin/www – это входная точка приложения. Сначала в файле создаётся объект основного приложения, расположенного в app.js — выполняется app=require(./app).

    -
    #!/usr/bin/env node
    +
    #!/usr/bin/env node
     
     /**
      * Module dependencies.
    @@ -357,7 +357,7 @@ GET /favicon.ico 404 34.134 ms - 1335

    Этот файл создаёт объект приложения  express (с именемapp, по соглашению), настраивает приложение и промежуточное ПО, а затем экспортирует приложение из модуля. В приведённом ниже коде показаны только те части файла, которые создают и экспортируют объект приложения:

    -
    var express = require('express');
    +
    var express = require('express');
     var app = express();
     ...
     module.exports = app;
    @@ -367,7 +367,7 @@ var app = express();
     
     

    Рассмотрим детали файла app.js. Сначала при помощи require(...) выполняется импорт некоторых полезных библиотек node: express, serve-favicon, morgan, cookie-parse, body-parser (они ранее были загружены для нашего приложения командой npm install), а также path из основной библиотеки node (применяется для разбора путей каталогов и файлов).

    -
    var express = require('express');
    +
    var express = require('express');
     var path = require('path');
     var favicon = require('serve-favicon');
     var logger = require('morgan');
    @@ -377,7 +377,7 @@ var bodyParser = require('body-parser');
     
     

    Затем require запрашивает модули из каталога путей route. Эти модули и файлы содержат код для обработки конкретного набора соответствующих путей (URL маршрутов). Если мы расширим каркас приложения, например, чтобы получить список книг библиотеки, нам следует добавить новый файл для обработки пути, связанного с книгами.

    -
    var index = require('./routes/index');
    +
    var index = require('./routes/index');
     var users = require('./routes/users');
     
    @@ -387,7 +387,7 @@ var users = require('./routes/users');

    Далее, импортированные модули express применяются для создания объекта app, который потом устанавливает движки-шаблоны представления. Установка движков состоит их двух частей. В первой мы задаём значение 'view', указывая папку, в которой будут размещаться шаблоны (у нас это /views). Во второй мы задаём значение движка 'view engine', указывая на библиотеку шаблона (у нас — "pug").

    -
    var app = express();
    +
    var app = express();
     
     // view engine setup
     app.set('views', path.join(__dirname, 'views'));
    @@ -396,7 +396,7 @@ app.set('view engine', 'pug');
     
     

    Следующие строки вызывают app.use(...), чтобы добавить промежуточные (middleware) библиотеки в цепочку обработки запросов. Кроме сторонних библиотек, импортированных ранее, используем библиотеку Express.static, что позволит обрабатывать статические файлы из папки /public корня проекта.

    -
    // uncomment after placing your favicon in /public
    +
    // uncomment after placing your favicon in /public
     //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
     app.use(logger('dev'));
     app.use(bodyParser.json());
    @@ -407,7 +407,7 @@ app.use(cookieParser());
     
     

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

    -
    app.use('/', index);
    +
    app.use('/', index);
     app.use('/users', users);
     
    @@ -417,7 +417,7 @@ app.use('/users', users);

    Последняя в файле промежуточная библиотека добавляет методы обработки ошибок и ответов 404 от HTTP.

    -
    // catch 404 and forward to error handler
    +
    // catch 404 and forward to error handler
     app.use(function(req, res, next) {
       var err = new Error('Not Found');
       err.status = 404;
    @@ -438,13 +438,13 @@ app.use(function(err, req, res, next) {
     
     

    Объект app приложения Express теперь полностью настроен. Остался последний шаг - добавить его к экспортируемым элементам модуля (это позволит импортировать его в файле /bin/www).

    -
    module.exports = app;
    +
    module.exports = app;

    Пути (Routes)

    Файл путей /routes/users.js приведён ниже (файлы путей имеют сходную структуру, поэтому нет необходимости приводить также index.js). Сначала загружается модуль Express, затем он используется для получения объекта express.Router. После этого для этого объекта задаётся путь, и, наконец, объект-роутер экспортируется из модуля (именно это позволяет импортировать файл в app.js):.

    -
    var express = require('express');
    +
    var express = require('express');
     var router = express.Router();
     
     /* GET users listing. */
    @@ -467,7 +467,7 @@ module.exports = router;
     
     

    Файлы преставлений (шаблонов) хранятся в каталоге /views (это указано в app.js) и имеют расширение .pug. Метод Response.render() выполняет указанный шаблон, передавая объекту значение именованной переменной, и затем посылает результат как ответ. В коде из /routes/index.js (приводится ниже) можно увидеть, что роут отвечает, используя шаблон "index" с переданным значением переменной "title" из шаблона.

    -
    /* GET home page. */
    +
    /* GET home page. */
     router.get('/', function(req, res) {
       res.render('index', { title: 'Express' });
     });
    @@ -475,7 +475,7 @@ router.get('/', function(req, res) {
     
     

    Шаблон для пути '/' приведён ниже (файл index.pug). О синтаксисе мы поговорим позже. Сейчас важно знать, что переменная title со значением 'Express' помещена в определённое место шаблона.

    -
    extends layout
    +
    extends layout
     
     block content
       h1= title
    diff --git a/files/ru/learn/server-side/first_steps/client-server_overview/index.html b/files/ru/learn/server-side/first_steps/client-server_overview/index.html
    index 38168b8a5d..5521c93ef0 100644
    --- a/files/ru/learn/server-side/first_steps/client-server_overview/index.html
    +++ b/files/ru/learn/server-side/first_steps/client-server_overview/index.html
    @@ -82,7 +82,7 @@ translation_of: Learn/Server-side/First_steps/Client-Server_overview
     
     

    Каждая строка запроса содержит информацию о запросе. Первая часть называется заголовок и содержит важную информацию о запросе, точно так же, как HTML head содержит важную информацию о HTML-документе (но не содержимое документа, которое расположено внутри тэга "body"):

    -
    GET https://developer.mozilla.org/en-US/search?q=client+server+overview&topic=apps&topic=html&topic=css&topic=js&topic=api&topic=webdev HTTP/1.1
    +
    GET https://developer.mozilla.org/en-US/search?q=client+server+overview&topic=apps&topic=html&topic=css&topic=js&topic=api&topic=webdev HTTP/1.1
     Host: developer.mozilla.org
     Connection: keep-alive
     Pragma: no-cache
    @@ -132,7 +132,7 @@ Cookie: sessionid=6ynxs23n521lu21b1t136rhbv7ezngie; csrftoken=zIPUJsAZv6pcgCBJSC
     
     

    В конце сообщения мы видим содержимое body, содержащее HTML-код возвращаемого ответа.

    -
    HTTP/1.1 200 OK
    +
    HTTP/1.1 200 OK
     Server: Apache
     X-Backend-Server: developer1.webapp.scl3.mozilla.com
     Vary: Accept,Cookie, Accept-Encoding
    @@ -166,7 +166,7 @@ Content-Length: 41823
     
     

    В приведённом ниже тексте показан HTTP-запрос, сделанный когда пользователь загружает новые данные профиля на этом сайте. Формат запроса почти такой же, как пример запроса GET, показанный ранее, хотя первая строка идентифицирует этот запрос как POST.

    -
    POST https://developer.mozilla.org/en-US/profiles/hamishwillee/edit HTTP/1.1
    +
    POST https://developer.mozilla.org/en-US/profiles/hamishwillee/edit HTTP/1.1
     Host: developer.mozilla.org
     Connection: keep-alive
     Content-Length: 432
    @@ -190,7 +190,7 @@ csrfmiddlewaretoken=zIPUJsAZv6pcgCBJSCj1zU6pQZbfMUAT&user-username=hamishwil
     
     

    Ответ от запроса показан ниже. Код состояния «302 Found» сообщает браузеру, что сообщение обработано, и что необходим второй HTTP-запрос для загрузки страницы, указанной в поле Location. В остальном информация аналогична информации для ответа на запрос GET .

    -
    HTTP/1.1 302 FOUND
    +
    HTTP/1.1 302 FOUND
     Server: Apache
     X-Backend-Server: developer3.webapp.scl3.mozilla.com
     Vary: Cookie
    @@ -277,7 +277,7 @@ Content-Length: 0
     
     

    Для примера рассмотрим следующий код Django (Python), который связывает два URL-шаблона с двумя функциями просмотра. Первый шаблон проверяет, что HTTP-запрос с URL-адресом ресурса /best будет передан функции с именем index() в модуле views. Запрос, который имеет шаблон «/best/junior», вместо этого будет передан функции просмотра junior().

    -
    # file: best/urls.py
    +
    # file: best/urls.py
     #
     
     from django.conf.urls import url
    @@ -299,7 +299,7 @@ urlpatterns = [
     
     

    В приведённом ниже примере представлен список всех команд, у которых есть точный (с учётом регистра) team_type «junior» («младший») — обратите внимание на формат: имя поля (team_type), за которым следует двойной знак подчёркивания, а затем тип соответствия для использования (в этом случае exact («точное»)). Существует много других типов соответствия, и мы можем объединить их. Мы также можем контролировать порядок и количество возвращаемых результатов.

    -
    #best/views.py
    +
    #best/views.py
     
     from django.shortcuts import render
     
    diff --git a/files/ru/learn/server-side/first_steps/web_frameworks/index.html b/files/ru/learn/server-side/first_steps/web_frameworks/index.html
    index 54d00a7baf..ec0f7368c9 100644
    --- a/files/ru/learn/server-side/first_steps/web_frameworks/index.html
    +++ b/files/ru/learn/server-side/first_steps/web_frameworks/index.html
    @@ -47,7 +47,7 @@ translation_of: Learn/Server-side/First_steps/Web_frameworks
     
     

    Пример ниже показывает, как это работает в веб-фреймворке Django (Python). Каждая функция «view» (обработчик запроса) получает объект HttpRequest, содержащий информацию о запросе, и должна вернуть объект HttpResponse с форматированным выводом (в этом случае строка).

    -
    # Django view function
    +
    # Django view function
     from django.http import HttpResponse
     
     def index(request):
    @@ -63,13 +63,13 @@ def index(request):
     
     

    Различные фреймворки используют различные механизмы для сопоставления. Например, веб-фреймворк Flask (Python) добавляет маршруты для просмотра функций используя декораторы.

    -
    @app.route("/")
    +
    @app.route("/")
     def hello():
         return "Hello World!"

    Django ожидает, что разработчики определят список сопоставлений URL-адресов между шаблоном URL-адреса и функцией просмотра.

    -
    urlpatterns = [
    +
    urlpatterns = [
         url(r'^$', views.index),
         # example: /best/myteamname/5/
         url(r'^best/(?P<team_name>\w.+?)/(?P<team_number>[0-9]+)/$', views.best),
    @@ -97,7 +97,7 @@ def hello():
      
    Первый фрагмент кода ниже показывает очень простую модель Django для объекта Team. Это сохраняет название команды и уровень команды как символьные поля и определяет максимальное количество символов для каждой записи. team_level  это поле выбора, поэтому здесь мы связываем варианты значений на выбор с сохраняемыми данными, а также значение по умолчанию.

    -
    #best/models.py
    +
    #best/models.py
     
     from django.db import models
     
    @@ -117,7 +117,7 @@ class Team(models.Model):
      
    Второй фрагмент кода показывает функцию представления (обработчик ресурсов) для отображения всех наших команд U09. В этом случае мы указываем, что мы хотим фильтровать для всех записей, где поле team_level имеет в точности текст «U09» (обратите внимание ниже, как этот критерий передаётся функции filter() в качестве аргумента с именем поля и типом соответствия, отделённым двойным подчёркиванием: team_level__exact).

    -
    #best/views.py
    +
    #best/views.py
     
     from django.shortcuts import render
     from .models import Team
    @@ -145,7 +145,7 @@ def youngest(request):
     
     

    Фрагмент кода ниже показывает, как это работает. Продолжая пример «самой молодой команды» из предыдущего раздела, HTML-шаблон передаёт представлению переменную списка youngest_teams. Внутри скелета HTML у нас есть выражение, которое сначала проверяет, существует ли переменная youngest_teams, а затем повторяет её в цикле for. На каждой итерации шаблон отображает значение team_name команды в элементе списка.

    -
    #best/templates/best/index.html
    +
    #best/templates/best/index.html
     
     <!DOCTYPE html>
     <html lang="en">
    diff --git a/files/ru/learn/server-side/first_steps/website_security/index.html b/files/ru/learn/server-side/first_steps/website_security/index.html
    index 1f976dd741..912fa2c0d5 100644
    --- a/files/ru/learn/server-side/first_steps/website_security/index.html
    +++ b/files/ru/learn/server-side/first_steps/website_security/index.html
    @@ -73,11 +73,11 @@ original_slug: Learn/Server-side/First_steps/Веб_Безопасность
      
    Эта уязвимость присутствует, если пользовательский ввод, который передаётся в базовый оператор SQL, может изменить смысл оператора. Например, следующий код предназначен для перечисления всех пользователей с определённым именем (userName), которое было предоставлено из формы HTML:

    -
    statement = "SELECT * FROM users WHERE name = '" + userName + "';"
    +
    statement = "SELECT * FROM users WHERE name = '" + userName + "';"

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

    -
    SELECT * FROM users WHERE name = 'a';DROP TABLE users; SELECT * FROM userinfo WHERE 't' = 't';
    +
    SELECT * FROM users WHERE name = 'a';DROP TABLE users; SELECT * FROM userinfo WHERE 't' = 't';
     

    Модифицированный оператор создаёт действительный оператор SQL, который удаляет таблицу пользователей и выбирает все данные из таблицы userinfo (которая раскрывает информацию о каждом пользователе). Это работает, потому что первая часть введённого текста (a ';) завершает исходное утверждение.
    @@ -90,7 +90,7 @@ original_slug: Learn/Server-side/First_steps/Веб_Безопасность

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

    -
    SELECT * FROM users WHERE name = 'a\';DROP TABLE users; SELECT * FROM userinfo WHERE \'t\' = \'t';
    +
    SELECT * FROM users WHERE name = 'a\';DROP TABLE users; SELECT * FROM userinfo WHERE \'t\' = \'t';
     
     
    -- cgit v1.2.3-54-g00ecf