One place for hosting & domains

      управления

      Использование ps, kill и nice для управления процессами в Linux


      Введение


      Сервер Linux, как и любой другой компьютер, использует приложения. Компьютер рассматривает эти приложения как процессы.

      Хотя Linux автоматически выполняет все скрытые низкоуровневые задачи жизненного цикла процесса, нам необходим способ взаимодействия с операционной системой для управления на более высоком уровне.

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

      В качестве примера мы используем Ubuntu 12.04 VPS, но любые современные дистрибутивы Linux будут работать аналогичным образом.

      Просмотр запущенных процессов в Linux


      top


      Чтобы посмотреть, какие процессы запущены на вашем сервере, нужно запустить команду top:

      top***
      
      top - 15:14:40 up 46 min,  1 user,  load average: 0.00, 0.01, 0.05 Tasks:  56 total,   1 running,  55 sleeping,   0 stopped,   0 zombie Cpu(s):  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st Mem:   1019600k total,   316576k used,   703024k free,     7652k buffers Swap:        0k total,        0k used,        0k free,   258976k cached   PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND               1 root      20   0 24188 2120 1300 S  0.0  0.2   0:00.56 init                   2 root      20   0     0    0    0 S  0.0  0.0   0:00.00 kthreadd               3 root      20   0     0    0    0 S  0.0  0.0   0:00.07 ksoftirqd/0            6 root      RT   0     0    0    0 S  0.0  0.0   0:00.00 migration/0            7 root      RT   0     0    0    0 S  0.0  0.0   0:00.03 watchdog/0             8 root       0 -20     0    0    0 S  0.0  0.0   0:00.00 cpuset                 9 root       0 -20     0    0    0 S  0.0  0.0   0:00.00 khelper               10 root      20   0     0    0    0 S  0.0  0.0   0:00.00 kdevtmpfs          
      

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

      Вы можете легко увидеть, что в системе запущен 1 процесс, а 55 процессов находятся в режиме сна (т. е. не активны/не используют ресурсы ЦП).

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

      htop


      В репозиториях доступна улучшенная версия top, которая называется htop. Установите ее с помощью следующей команды:

      sudo apt-get install htop
      

      Если мы запустим команду htop, мы увидим отображение информации в более удобном формате:

      htop***
      
        Mem[|||||||||||           49/995MB]     Load average: 0.00 0.03 0.05   CPU[                          0.0%]     Tasks: 21, 3 thr; 1 running   Swp[                         0/0MB]     Uptime: 00:58:11   PID USER      PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command  1259 root       20   0 25660  1880  1368 R  0.0  0.2  0:00.06 htop     1 root       20   0 24188  2120  1300 S  0.0  0.2  0:00.56 /sbin/init   311 root       20   0 17224   636   440 S  0.0  0.1  0:00.07 upstart-udev-brid   314 root       20   0 21592  1280   760 S  0.0  0.1  0:00.06 /sbin/udevd --dae   389 messagebu  20   0 23808   688   444 S  0.0  0.1  0:00.01 dbus-daemon --sys   407 syslog     20   0  243M  1404  1080 S  0.0  0.1  0:00.02 rsyslogd -c5   408 syslog     20   0  243M  1404  1080 S  0.0  0.1  0:00.00 rsyslogd -c5   409 syslog     20   0  243M  1404  1080 S  0.0  0.1  0:00.00 rsyslogd -c5   406 syslog     20   0  243M  1404  1080 S  0.0  0.1  0:00.04 rsyslogd -c5   553 root       20   0 15180   400   204 S  0.0  0.0  0:00.01 upstart-socket-br
      

      Вы можете узнать больше об использовании top и htop здесь.

      Использование ps для вывода перечня процессов


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

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

      При вызове без аргументов вывод может быть довольно сжатым:

      ps***
      
        PID TTY          TIME CMD  1017 pts/0    00:00:00 bash  1262 pts/0    00:00:00 ps
      

      Вывод показывает все процессы, связанные с текущим пользователем и текущим сеансом терминала. Это имеет смысл, потому что мы запускаем на этом терминале только bash и ps.

      Чтобы получить более полное представление о процессах в данной системе, мы можем использовать следующую команду:

      ps aux***
      
      USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND root         1  0.0  0.2  24188  2120 ?        Ss   14:28   0:00 /sbin/init root         2  0.0  0.0      0     0 ?        S    14:28   0:00 [kthreadd] root         3  0.0  0.0      0     0 ?        S    14:28   0:00 [ksoftirqd/0] root         6  0.0  0.0      0     0 ?        S    14:28   0:00 [migration/0] root         7  0.0  0.0      0     0 ?        S    14:28   0:00 [watchdog/0] root         8  0.0  0.0      0     0 ?        S<   14:28   0:00 [cpuset] root         9  0.0  0.0      0     0 ?        S<   14:28   0:00 [khelper] . . .
      

      Эти опции предписывают ps показать процессы, принадлежащие всем пользователям (вне зависимости от привязки терминала) в удобном формате.

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

      ps axjf***
      
       PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND     0     2     0     0 ?           -1 S        0   0:00 [kthreadd]     2     3     0     0 ?           -1 S        0   0:00  _ [ksoftirqd/0]     2     6     0     0 ?           -1 S        0   0:00  _ [migration/0]     2     7     0     0 ?           -1 S        0   0:00  _ [watchdog/0]     2     8     0     0 ?           -1 S<       0   0:00  _ [cpuset]     2     9     0     0 ?           -1 S<       0   0:00  _ [khelper]     2    10     0     0 ?           -1 S        0   0:00  _ [kdevtmpfs]     2    11     0     0 ?           -1 S<       0   0:00  _ [netns] . . .
      

      Как видите, процесс kthreadd отображается как родитель процесса ksoftirqd/0 и других процессов.

      Примечание об идентификаторах процессов


      В системах Linux и Unix каждому процессу назначается идентификатор процесса или PID. Операционная система использует их для идентификации и отслеживания процессов.

      Чтобы быстро узнать PID процесса, вы можете использовать команду pgrep:

      pgrep bash***
      
      1017
      

      Эта команда просто запросит идентификатор процесса и выведет его.

      Процессу init, который создается первым при загрузке, присваивается PID “1”.

      pgrep init***
      
      1
      

      Этот процесс отвечает за создание всех остальных процессов в системе. Последующим процессам присваиваются большие номера PID.

      Родитель процесса — это процесс, который отвечает за его создание. Родительские процессы имеют идентификатор PPID, который можно увидеть в заголовках столбцов многих приложений для управления процессами, включая top, htop и ps.

      Любое взаимодействие между пользователем и операционной системой, связанное с процессами, включает взаимное преобразование имен процессов и PID. Именно поэтому утилиты сообщают вам PID.

      Отношения родительских и дочерних элементов


      Создание дочернего процесса осуществляется в два этапа: fork() создает новое адресное пространство и копирует в него ресурсы, принадлежащие родительскому процессу, с помощью copy-on-write; а exec() загружает исполняемый блок в адресное пространство и выполняет его.

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

      Отправка сигналов процессам в Linux


      Все процессы Linux реагируют на сигналы. Операционная система использует сигналы, чтобы отправить программам команду остановиться или изменить поведение.

      Отправка сигналов процессам по PID


      Наиболее распространенный способ передачи сигналов в программу — использовать команду kill.

      Как вы можете догадаться, по умолчанию эта утилита пытается уничтожить процесс:

      <pre>kill <span class=“highlight”>PIDoftarget_process</span></pre>

      Она отправляет процессору сигнал TERM. Сигнал TERM просит процесс остановиться. Это позволяет программе выполнить операции по очистке и нормально завершить работу.

      Если программа работает неправильно и не завершает работу после получения сигнала TERM, мы можем отправить сигнал более высокого уровня — KILL:

      <pre>kill -KILL <span class=“highlight”>PIDoftarget_process</span></pre>

      Это специальный сигнал, который не отправляется программе.

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

      Каждому сигналу присвоено число, которое можно передать вместо имени. Например, вы можете передать “-15” вместо “-TERM” и “-9” вместо “-KILL”.

      Использование сигналов для других целей


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

      Например, многие демоны перезапускаются при получении сигнала HUP или прекращения работы. Например, так работает Apache.

      <pre>sudo kill -HUP <span class=“highlight”>pidofapache</span></pre>

      Получив вышеуказанную команду, Apache перезагрузит файл конфигурации и возобновит вывод контента.

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

      kill -l***
      
      1) SIGHUP    2) SIGINT   3) SIGQUIT  4) SIGILL   5) SIGTRAP  6) SIGABRT  7) SIGBUS   8) SIGFPE   9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM . . .
      

      Отправка сигналов процессам по имени


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

      Команда pkill работает практически точно так же как и kill, но использует имя процесса:

      pkill -9 ping
      

      Вышеуказанная команда эквивалентна команде:

      kill -9 `pgrep ping`
      

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

      killall firefox
      

      Приведенная выше команда отправит сигнал TERM всем экземплярам firefox, запущенным на этом компьютере.

      Настройка приоритетов процессов


      Часто бывает необходимо изменить приоритет процессов в серверной среде.

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

      Linux контролирует приоритеты с помощью значения вежливости.

      Приоритетные задачи считаются менее вежливыми, потому что они вообще не делятся ресурсами. Процессы с низким приоритетом считаются более вежливыми, потому что они используют минимум ресурсов.

      Когда мы запускали команду top в начале этого учебного модуля, мы видели столбец “NI”. В этом столбце отображается значение вежливости процесса:

      top***
      
       Tasks:  56 total,   1 running,  55 sleeping,   0 stopped,   0 zombie Cpu(s):  0.0%us,  0.3%sy,  0.0%ni, 99.7%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st Mem:   1019600k total,   324496k used,   695104k free,     8512k buffers Swap:        0k total,        0k used,        0k free,   264812k cached   PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND            1635 root      20   0 17300 1200  920 R  0.3  0.1   0:00.01 top                    1 root      20   0 24188 2120 1300 S  0.0  0.2   0:00.56 init                   2 root      20   0     0    0    0 S  0.0  0.0   0:00.00 kthreadd               3 root      20   0     0    0    0 S  0.0  0.0   0:00.11 ksoftirqd/0
      

      В зависимости от системы, значения вежливости могут различаться от “-19/-20” (наибольший приоритет) до “19/20” (наименьший приоритет).

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

      <pre>nice -n 15 <span class=“highlight”>commandtoexecute</span></pre>

      Это работает только в начале новой программы.

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

      <pre>renice 0 <span class=“highlight”>PIDtoprioritize</span></pre>

      Примечание. Хотя nice по необходимости использует имя команды, renice вызывает PID процесса.

      Заключение


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

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

      <div class=“author”>Джастин Эллингвуд</div>



      Source link

      Использование Systemctl для управления службами и блоками Systemd


      Введение

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

      В этом руководстве мы обсудим команду systemctl, которая является инструментом центрального управления для контроля системы инициализации. Мы расскажем о том, как управлять службами, проверять статус, изменять состояние системы и работать с файлами конфигурации.

      Обратите внимание, что хотя система systemd стала системой инициализации по умолчанию для многих дистрибутивов Linux, она не используется повсеместно во всех дистрибутивах. По мере изучения этого руководства, если ваш терминал выводит ошибку bash: systemctl is not installed, скорее всего, на вашей машине установлена другая система инициализации.

      Управление службами

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

      В systemd целью большинства действий являются «модули», являющиеся ресурсами, которыми systemd знает, как управлять. Модули распределяются по категориям по типу ресурса, который они представляют, и определяются файлами, известными как файлы модулей. Тип каждого модуля можно вывести из суффикса в конце файла.

      Для задач по управлению службами целевым модулем будут модули службы, которые имеют файлы модулей с суффиксом .service. Однако для большинства команд по управлению службами вы можете не использовать суффикс .service, поскольку systemd достаточно умна, чтобы знать, что вы, возможно, хотите работать со службой при использовании команд по управлению службами.

      Запуск и остановка служб

      Чтобы запустить службу systemd, используя инструкции в файле модуля службы, используйте команду start. Если вы работаете как пользователь без прав root, вам потребуется использовать sudo, поскольку это влияет на состояние операционной системы:

      • sudo systemctl start application.service

      Как мы уже упомянули выше, systemd будет искать файлы *.service для команд управления службами, так что команду можно легко ввести следующим образом:

      • sudo systemctl start application

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

      Чтобы остановить работающую в данный момент службу, можно использовать команду stop:

      • sudo systemctl stop application.service

      Перезапуск и перезагрузка

      Чтобы перезапустить работающую службу, можно использовать команду restart:

      • sudo systemctl restart application.service

      Если данное приложение может перезагрузить файлы конфигурации (без перезапуска), вы можете выдать команду reload для инициализации этого процесса:

      • sudo systemctl reload application.service

      Если вы не уверены, есть ли у службы функция перезагрузки своей конфигурации, можно использовать команду reload-or-restart. Это перезагрузит необходимую конфигурацию при наличии. В противном случае будет перезагружена служба для выбора новой конфигурации:

      • sudo systemctl reload-or-restart application.service

      Включение и отключение служб

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

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

      • sudo systemctl enable application.service

      При этом будет создана символическая ссылка из системной копии служебного файла (обычно в /lib/systemd/system или /etc/systemd/system) в месте на диске, где systemd ищет файлы для автозапуска (обычно /etc/systemd/system/some_target.target.wants; что такое цель, мы рассмотрим далее в этом руководстве).

      Чтобы отключить автоматический запуск службы, можно ввести следующее:

      • sudo systemctl disable application.service

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

      Помните, что включение службы не запустит ее в текущем сеансе. Если вы хотите запустить службу и включить ее при загрузке, необходимо дать обе команды, start и enable.

      Проверка статуса служб

      Чтобы проверить статус службы в вашей системе, можно использовать команду status:

      • systemctl status application.service

      При этом вы получите статус службы, иерархию контрольных групп и первые несколько строк журнала.

      Например, при проверке статуса сервера Nginx вы можете видеть следующий вывод:

      Output

      ● nginx.service - A high performance web server and a reverse proxy server Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled) Active: active (running) since Tue 2015-01-27 19:41:23 EST; 22h ago Main PID: 495 (nginx) CGroup: /system.slice/nginx.service ├─495 nginx: master process /usr/bin/nginx -g pid /run/nginx.pid; error_log stderr; └─496 nginx: worker process Jan 27 19:41:23 desktop systemd[1]: Starting A high performance web server and a reverse proxy server... Jan 27 19:41:23 desktop systemd[1]: Started A high performance web server and a reverse proxy server.

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

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

      • systemctl is-active application.service

      Это вернет текущий статус модуля, который обычно active или inactive. Код выхода будет «0», если он активен, и результат будет проще парсить в скрипты оболочки.

      Чтобы увидеть, включен ли модуль, можно использовать команду is-enabled:

      • systemctl is-enabled application.service

      Это выведет информацию о том, что служба enabled или disabled, и снова установит код выхода на «0» или «1» в зависимости от вопроса команды.

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

      • systemctl is-failed application.service

      Это вернет active, если он работает должным образом, или failed, если возникла ошибка. Если модуль был намеренно остановлен, может вернуться unknown или inactive. Статус выхода «0» означает, что произошел сбой, а статус выхода «1» указывает на какой-либо другой статус.

      Обзор состояния системы

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

      Список текущих модулей

      Чтобы увидеть список всех активных модулей, о которых знает systemd, можно использовать команду list-units:

      Это покажет вам список всех модулей, которые у systemd активны в системе. Результат будет выглядеть примерно так:

      Output

      UNIT LOAD ACTIVE SUB DESCRIPTION atd.service loaded active running ATD daemon avahi-daemon.service loaded active running Avahi mDNS/DNS-SD Stack dbus.service loaded active running D-Bus System Message Bus dcron.service loaded active running Periodic Command Scheduler dkms.service loaded active exited Dynamic Kernel Modules System [email protected] loaded active running Getty on tty1 . . .

      Вывод содержит следующие столбцы:

      • UNIT: имя модуля systemd
      • LOAD: указывает на то, парсила ли systemd конфигурацию модуля. Конфигурация загруженных модулей сохраняется в памяти.
      • ACTIVE: краткое состояние активности модуля. Обычно это довольно стандартный способ сообщить, запущен модуль или нет.
      • SUB: это состояние более низкого уровня, которое указывает более подробную информацию о модуле. Это часто зависит от типа модуля, состояния и фактического метода работы модуля.
      • DESCRIPTION: краткое текстовое описание того, чем является модуль/что делает.

      Поскольку команда list-units показывает по умолчанию только активные модули, для всех вводов выше отобразится loaded в столбце LOAD и active в столбце ACTIVE. Это отображение фактически является поведением по умолчанию systemctl при вызове без дополнительных команд, поэтому вы увидите то же, что и при вызове systemctl без аргументов:

      Мы можем использовать systemctl для вывода различной информации путем добавления дополнительных флагов. Например, чтобы увидеть все модули, которые загрузила система systemd (или пыталась загрузить), независимо от их активности в данный момент, можно использовать следующий флаг --all:

      • systemctl list-units --all

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

      Вы можете использовать другие флаги для фильтрации этих результатов. Например, мы можем использовать флаг --state= для указания состояния LOAD, ACTIVE или SUB, которое мы хотим увидеть. Вам потребуется сохранить флаг --all, чтобы systemctl позволила отображать неактивные модули:

      • systemctl list-units --all --state=inactive

      Другим распространенным фильтром является фильтр ---type=. Мы можем задать systemctl только для отображения модулей интересующего нас типа. Например, чтобы увидеть только активные модули службы, мы можем:

      • systemctl list-units --type=service

      Список все файлов модулей

      Команда list-units отображает только модули, которые система systemd пыталась парсить или загрузить в память. Поскольку systemd будет считывать только те модули, которые считает необходимыми, это необязательно будут все модули, доступные в системе. Чтобы увидеть все доступные файлы модулей в путях systemd, включая те, что система systemd пыталась загрузить, можно использовать команду list-unit-files:

      • systemctl list-unit-files

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

      Output

      UNIT FILE STATE proc-sys-fs-binfmt_misc.automount static dev-hugepages.mount static dev-mqueue.mount static proc-fs-nfsd.mount static proc-sys-fs-binfmt_misc.mount static sys-fs-fuse-connections.mount static sys-kernel-config.mount static sys-kernel-debug.mount static tmp.mount static var-lib-nfs-rpc_pipefs.mount static org.cups.cupsd.path enabled . . .

      Состояние будет, как правило, enabled, disabled, static или masked. В этом контексте static обозначает, что файл модуля не содержит раздел install, который используется для включения модуля. Эти модули как таковые не могут быть включены. Обычно это означает, что модуль выполняет разовое действие или используется только как зависимость другого модуля и не должен работать самостоятельно.

      Мы рассмотрим сразу же, что означает masked.

      Управление модулями

      До сих пор мы работали со службами и отображали информацию о модулях и файлах модулей, о которых знает systemd. Однако мы можем узнать более конкретную информацию о модулях, используя некоторые дополнительные команды.

      Отображение файла модуля

      Чтобы отобразить файл модуля, который система systemd загрузила в систему, можно использовать команду cat (она была добавлена в версию systemd 209). Например, чтобы увидеть файл модуля демона-планировщика atd, можно ввести следующее:

      • systemctl cat atd.service

      Output

      [Unit] Description=ATD daemon [Service] Type=forking ExecStart=/usr/bin/atd [Install] WantedBy=multi-user.target

      Вывод — это файл модуля, известный выполняемому в настоящее время процессу systemd. Это может быть важно, если вы недавно модифицировали файлы модуля или если вы переопределяете определенные опции во фрагменте файла модуля (мы рассмотрим это позже).

      Отображение зависимостей

      Чтобы увидеть дерево зависимостей модуля, можно использовать команду list-dependencies:

      • systemctl list-dependencies sshd.service

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

      Output

      sshd.service ├─system.slice └─basic.target ├─microcode.service ├─rhel-autorelabel-mark.service ├─rhel-autorelabel.service ├─rhel-configure.service ├─rhel-dmesg.service ├─rhel-loadmodules.service ├─paths.target ├─slices.target . . .

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

      Чтобы отобразить обратные зависимости (модули, зависящие от указанного модуля), можно добавить в команду флаг --reverse. Другие полезные флаги --before и --after могут быть использованы для отображения модулей, которые зависят от указанного модуля, соответственно, перед ними и после.

      Проверка свойств модуля

      Чтобы увидеть свойства более низкого уровня модуля, можно использовать команду show. При этом будет выведен список свойств, заданных для указанного модуля с помощью формата key=value:

      • systemctl show sshd.service

      Output

      Id=sshd.service Names=sshd.service Requires=basic.target Wants=system.slice WantedBy=multi-user.target Conflicts=shutdown.target Before=shutdown.target multi-user.target After=syslog.target network.target auditd.service systemd-journald.socket basic.target system.slice Description=OpenSSH server daemon . . .

      Если вы хотите отобразить одно свойство, можно передать флаг -p с именем свойства. Например, чтобы увидеть конфликты, которые есть у модуля sshd.service, можно ввести следующее:

      • systemctl show sshd.service -p Conflicts

      Output

      Conflicts=shutdown.target

      Маскировка и снятие маскировки модулей

      В разделе управления службами мы узнали, как остановить или отключить службу, но systemd также имеет возможность отметить модуль как полностью незапускаемый, автоматически или вручную, связав его с /dev/null. Это называется маскировкой модуля, и она возможна с помощью команды mask:

      • sudo systemctl mask nginx.service

      Это не позволит запустить службу Nginx автоматически или вручную, пока она замаскирована.

      Если вы проверите list-unit-files, вы увидите, что служба теперь указана как замаскированная:

      • systemctl list-unit-files

      Output

      . . . kmod-static-nodes.service static ldconfig.service static mandb.service static messagebus.service static nginx.service masked quotaon.service static rc-local.service static rdisc.service disabled rescue.service static . . .

      Если вы попытаетесь запустить службу, вы увидите следующее сообщение:

      • sudo systemctl start nginx.service

      Output

      Failed to start nginx.service: Unit nginx.service is masked.

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

      • sudo systemctl unmask nginx.service

      Это вернет модуль в его предыдущее состояние, что позволит его запускать или включать.

      Редактирование файлов модулей

      Хотя конкретный формат файлов модулей выходит за рамки этого руководства, systemctl предоставляет встроенные механизмы для редактирования и модификации файлов модулей при необходимости изменений. Эта функция добавлена в версию systemd 218.

      Команда edit по умолчанию откроет фрагмент файла модуля для интересующего модуля:

      • sudo systemctl edit nginx.service

      Это будет пустой файл, который можно использовать для переопределения или добавления директив в определение модуля. Каталог будет создан в каталоге /etc/systemd/system, который содержит название модуля с добавлением .d. Например, для nginx.service будет создан каталог под названием nginx.service.d.

      В этом каталоге будет создан фрагмент под названием override.conf. При загрузке модуля systemd в памяти соединит фрагмент переопределения с полным файлом модуля. Директивы фрагмента получат приоритет над найденными в оригинальном файле модуля.

      Если вы хотите редактировать весь файл модуля, а не создавать фрагмент, можно передать флаг --full:

      • sudo systemctl edit --full nginx.service

      Это загрузит текущий файл модуля в редактор, где его можно редактировать. После выхода из редактора измененный файл будет записан в /etc/systemd/system, что будет иметь приоритет над определением модуля системы (обычно находится где-то в /lib/systemd/system).

      Чтобы удалить какие-либо сделанные добавления, удалите либо каталог конфигурации модуля .d или модифицированный служебный файл из /etc/systemd/system. Например, для удаления фрагмента можно ввести следующее:

      • sudo rm -r /etc/systemd/system/nginx.service.d

      Чтобы удалить весь отредактированный файл модуля, добавим:

      • sudo rm /etc/systemd/system/nginx.service

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

      • sudo systemctl daemon-reload

      Настройка состояния системы (уровень запуска) с помощью целей

      Целями являются специальные файлы модулей, которые описывают состояние системы или точку синхронизации. Как и другие модули, файлы, которые определяют цели, могут быть идентифицированы по суффиксу, которым в данном случае является .target. Цели сами по себе немного значат, а используются для группировки других модулей.

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

      Например, swap.target используется для указания того, что переключение готово к использованию. Модули, являющиеся частью этого процесса, могут синхронизироваться с этой целью путем указания в своей конфигурации, что они WantedBy= или RequiredBy= swap.target. Модули, которым требуется возможность переключения, могут указывать это состояние с помощью спецификаций Wants=, Requires= и After= для указания характера их отношений.

      Получение и настройка цели по умолчанию

      Процесс systemd имеет цель по умолчанию, которую он использует при загрузке системы. Удовлетворение каскада зависимостей от этой одной цели приведет систему в желаемое состояние. Чтобы найти цель по умолчанию для вашей системы, введите:

      Output

      multi-user.target

      Если вы хотите задать другую цель по умолчанию, можно использовать set-default. Например, если у вас установлен графический рабочий стол и вы хотите загрузить систему в него по умолчанию, можно изменить цель по умолчанию соответственно:

      • sudo systemctl set-default graphical.target

      Список доступных целей

      Вы можете получить список имеющихся целей в вашей системе, введя:

      • systemctl list-unit-files --type=target

      В отличие от уровней запуска, несколько целей могут быть активны одновременно. Активная цель указывает, что система systemd попыталась запустить все модули, привязанные к цели, и не попыталась закрыть их снова. Чтобы увидеть все активные цели, введите:

      • systemctl list-units --type=target

      Изолирование целей

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

      Например, если вы работаете в графической среде с активным graphical.target, можно закрыть графическую систему и перевести систему в состояние многопользовательской командной строки путем изоляции multi-user.target. Поскольку graphical.target зависит от multi-user.target, а не наоборот, все графические модули будут остановлены.

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

      • systemctl list-dependencies multi-user.target

      Если вы удовлетворены модулями, которые будут сохранены в активном состоянии, можно изолировать цель, введя:

      • sudo systemctl isolate multi-user.target

      Использование комбинации быстрого ввода для важных событий

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

      Например, чтобы перевести систему в режим спасения (один пользователь), можно использовать команду rescue вместо isolate rescue.target:

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

      Чтобы остановить систему, можно использовать команду halt:

      Для инициализации полного отключения можно использовать команду poweroff:

      Перезапуск можно начать с помощью команды reboot:

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

      Например, для перезагрузки системы обычно можно ввести следующее:

      Заключение

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

      Хотя systemctl работает главным образом с основным процессом systemd, в экосистеме systemd есть другие компоненты, которые контролируются другими утилитами. Другие возможности, такие как управление журналами и сеансы пользователя, обрабатываются отдельными демонами и утилитами управления (journald/journalctl и logind/loginctl соответственно). Знакомство с этими другими инструментами и демонами упростит задачу управления.



      Source link

      Создание с помощью Django и React современного веб-приложения для управления данными клиентов в Ubuntu 18.04


      Автор выбрал организацию Open Sourcing Mental Illness Ltd для получения пожертвований в рамках программы Write for DOnations.

      Введение

      Для подключения к Интернету и просмотра сайтов люди используют разные устройства. В связи с этим, приложения должны быть доступными из разных мест. Для традиционных сайтов часто достаточно иметь быстрый пользовательский интерфейс, однако для более сложных приложений обычно требуются другие методики и архитектуры. В ним относятся отдельные серверные и клиентские приложения REST, которые можно реализовать как клиентские веб-приложения, прогрессивные веб-приложения (PWA) или мобильные приложения.

      Помимо прочего, для создания более сложных веб-приложений можно использовать следующие инструменты:

      • React, система JavaScript, позволяющая разработчикам создавать веб-приложения и собственные клиентские приложения для серверных REST API.
      • Django, бесплатная веб-система Python с открытым исходным кодом, соответствующая архитектурной схеме программного обеспечения контроллера отображения моделей (MVC).
      • Система Django REST, представляющая собой мощный и гибкий набор инструментов для создания REST API в Django.

      В этом обучающем руководстве вы создадите современное веб-приложение с отдельными серверным и клиентским REST API, используя React, Django и систему Django REST. Используя React вместе с Django, вы сможете воспользоваться последними достижениями JavaScript и разработки клиентской части. Вместо создания приложения Django, использующего встроенный механизм шаблонов, вы используете React как библиотеку пользовательского интерфейса. Это позволяет воспользоваться преимуществами виртуальной объектной модели документов (DOM), декларативного подхода и компонентов, быстро обрабатывающих изменения данных.

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

      Предварительные требования

      Для данного курса обучения вам потребуется следующее:

      Шаг 1 — Создание виртуальной среды Python и установка зависимостей

      На этом шаге мы создадим виртуальную среду и установим требуемые зависимости для нашего приложения, в том числе Django, систему Django REST и заголовки django-cors-headers.

      Наше приложение будет использовать два разных сервера разработки для Django и React. Они будут работать на разных портах и функционировать как два отдельных домена. В связи с этим нам потребуется совместное использование ресурсов между разными источниками (CORS) для отправки запросов HTTP из React в Django без блокировки браузером.

      Перейдите в каталог home и создайте виртуальную среду, используя модуль venv Python 3:

      • cd ~
      • python3 -m venv ./env

      Активируйте созданную виртуальную среду с помощью source:

      Установите зависимости проекта с помощью pip. К ним будут относиться:

      • Django: веб-система для данного проекта.
      • Система Django REST: стороннее приложение, создающее REST API с помощью Django.
      • django-cors-headers: пакет для активации CORS.

      Установите систему Django:

      • pip install django djangorestframework django-cors-headers

      После установки зависимостей проекта вы сможете создать проект Django и клиентскую часть React.

      Шаг 2 — Создание проекта Django

      На этом шаге мы создадим проект Django, используя следующие команды и утилиты:

      • django-admin startproject project-name: django-admin— утилита командной строки, используемая для выполнения задач с помощью Django. Команда startproject создает новый проект Django.

      • python manage.py startapp myapp: manage.py — утилитарный сценарий, автоматически добавляемый в каждый проект Django. Он выполняет ряд административных задач, включая создание новых приложений, миграцию базы данных и локальное обслуживание проекта Django. Его команда startapp создает приложение Django внутри проекта Django. В Django термин приложение **описывает пакет Python, предоставляющий определенный набор функций проекта.

      Для начала создайте проект Django с помощью команды django-admin startproject. Назовем наш проект djangoreactproject:

      • django-admin startproject djangoreactproject

      Прежде чем продолжить, посмотрим структуру каталогов нашего проекта Django с помощью команды tree.

      Подсказка: tree — это полезная команда для просмотра структур файлов и каталогов из командной строки. Для установки вы можете использовать следующую команду:

      • sudo apt-get install tree

      Для использования перейдите в желаемый каталог с помощью команды cd, а затем введите команду tree или укажите путь к начальной точке команды tree /home/sammy/sammys-project.

      Перейдите в папку проекта djangoreactproject в корневом каталоге проекта и запустите команду tree:

      • cd ~/djangoreactproject
      • tree

      Результат будет выглядеть следующим образом:

      Output

      ├── djangoreactproject │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── manage.py

      Папка ~/djangoreactproject является корневым каталогом проекта. В этой папке содержится несколько файлов, важных для вашей работы:

      • manage.py: утилитарный сценарий, выполняющий ряд административных задач.
      • settings.py: основной файл конфигурации проекта Django, где вы можете изменять настройки проекта. В число этих настроек входят переменные, такие как INSTALLED_APPS, список строк с обозначением приложений, активированных для вашего проекта. Более подробную информацию по доступным настройкам можно найти в документации по Django.
      • urls.py: этот файл содержит список шаблонов URL и связанных просмотров. В каждом шаблоне сопоставляются URL и функция, вызываемая для этого URL. Более подробную информацию об URL и представлениях можно найти в обучающем руководстве «Создание представления Django».

      Первым шагом в работе с проектом будет настройка пакетов, установленных на предыдущем шаге, включая систему Django REST и пакет Django CORS. Для этого их нужно добавить в settings.py. Откройте файл с помощью nano или своего любимого редактора:

      • nano ~/djangoreactproject/djangoreactproject/settings.py

      Перейдите в настройки INSTALLED_APPS и добавьте приложения rest_framework и corsheaders в нижнюю часть списка:

      ~/djangoreactproject/djangoreactproject/settings.py

      ...
      INSTALLED_APPS = [
          'django.contrib.admin',
          'django.contrib.auth',
          'django.contrib.contenttypes',
          'django.contrib.sessions',
          'django.contrib.messages',
          'django.contrib.staticfiles',
          'rest_framework',
          'corsheaders'
      ]
      

      Затем добавьте промежуточный уровень corsheaders.middleware.CorsMiddleware из ранее установленного пакета CORS в параметр MIDDLEWARE. Этот параметр представляет собой перечень промежуточных уровней. Промежуточный уровень — это класс Python, который содержит код, выполняемый каждый раз, когда ваше веб-приложение обрабатывает запрос или отклик:

      ~/djangoreactproject/djangoreactproject/settings.py

      ...
      
      MIDDLEWARE = [
      ...
      'django.contrib.messages.middleware.MessageMiddleware',
      'django.middleware.clickjacking.XFrameOptionsMiddleware',
      'corsheaders.middleware.CorsMiddleware'
      ]
      

      Далее вы можете активировать CORS. Параметр CORS_ORIGIN_ALLOW_ALL указывает, нужно ли разрешать CORS для всех доменов, а CORS_ORIGIN_WHITELIST — это запись Python, содержащая перечень разрешенных URL. В нашем случае сервер разработки React будет работать по URL http://localhost:3000, и поэтому мы добавим новые параметры CORS_ORIGIN_ALLOW_ALL = False и CORS_ORIGIN_WHITELIST('localhost:3000',) в наш файл settings.py. Эти параметры можно добавить в любую часть файла:

      ~/djangoreactproject/djangoreactproject/settings.py

      
      ...
      CORS_ORIGIN_ALLOW_ALL = False
      
      CORS_ORIGIN_WHITELIST = (
             'localhost:3000',
      )
      ...
      

      Дополнительные параметры конфигурации можно найти в документации по django-cors-headers.

      После завершения работы сохраните файл и закройте редактор.

      Находясь в каталоге ~/djangoreactproject, создайте новое приложение Django под названием customers:

      • python manage.py startapp customers

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

      Затем добавьте это приложение в список установленных приложений в файле проекта settings.py, чтобы Django распознавал его как часть проекта. Откройте файл settings.py еще раз:

      • nano ~/djangoreactproject/djangoreactproject/settings.py

      Добавьте приложение customers:

      ~/djangoreactproject/djangoreactproject/settings.py

      ...
      INSTALLED_APPS = [
          ...
          'rest_framework',
          'corsheaders',
          'customers'
      ]
      ...
      

      Затем выполните миграцию базы данных и запустите локальный сервер разработки. Миграция используется Django для записи изменений модели в схему базы данных. Изменения могут включать добавление поля, удаление модели и т. д. Более подробную информацию о моделях и миграции можно найти в документе «Создание моделей Django».

      Выполните миграцию базы данных:

      Запустите локальный сервер разработки:

      • python manage.py runserver

      Вы увидите примерно следующий результат:

      Output

      Performing system checks... System check identified no issues (0 silenced). October 22, 2018 - 15:14:50 Django version 2.1.2, using settings 'djangoreactproject.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.

      Ваше веб-приложение будет работать с http://127.0.0.1:8000. Если вы откроете этот адрес в браузере, вы должны увидеть следующую страницу:

      Демонстрационная страница Django

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

      Шаг 3 — Создание клиентской части React

      В этом разделе мы создадим клиентское приложение нашего проекта с помощью React.

      В React есть официальная утилита, позволяющая быстро генерировать проекты React без прямой настройки Webpack. Webpack — это компоновщик модулей, используемый для компоновки веб-ресурсов, таких как код JavaScript, стили CSS и изображения. Обычно перед использованием Webpack необходимо настроить различные параметры конфигурации, однако утилита create-react-app позволяет не работать с Webpack напрямую, если вы не захотите внести более детальные настройки. Для запуска create-react-app вы можете использовать инструмент npx, служащий для выполнения двоичных пакетов npm.

      Переключитесь на второй терминал и убедитесь, что вы находитесь в каталоге проекта:

      Создайте проект React под названием frontend с помощью create-react-app и npx:

      • npx create-react-app frontend

      Воспользуйтесь навигацией по вашему приложению React и запустите сервер разработки:

      • cd ~/djangoreactproject/frontend
      • npm start

      Ваше приложение будет запускаться с http://localhost:3000/:

      Демонстрационная страница React

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

      Чтобы увидеть полную структуру каталогов проекта на этом этапе, перейдите в корневую папку root и снова запустите команду tree:

      • cd ~/djangoreactproject
      • tree

      Вы увидите следующую структуру:

      Output

      ├── customers │ ├── admin.py │ ├── apps.py │ ├── __init__.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py ├── djangoreactproject │ ├── __init__.py │ ├── __pycache__ │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── frontend │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ └── manifest.json │ ├── README.md │ ├── src │ │ ├── App.css │ │ ├── App.js │ │ ├── App.test.js │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ └── registerServiceWorker.js │ └── yarn.lock └── manage.py

      Наше приложение будет использовать Bootstrap 4 для стилизации интерфейса React, и поэтому мы добавим его в файл frontend/src/App.css, управляющий настройками CSS. Откройте файл:

      • nano ~/djangoreactproject/frontend/src/App.css

      Добавьте следующий элемент import в начало файла. Вы можете удалить содержание файла, хотя это не обязательно:

      ~/djangoreactproject/frontend/src/App.css

      @import  'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css';
      

      Здесь @import — это инструкция CSS, используемая для импорта правил стиля из других таблиц стилей.

      Мы создали серверную и клиентскую часть приложения. Теперь мы создадим модель клиентов и добавим демонстрационные данные.

      Шаг 4 — Создание модели клиентов и начального набора данных

      После создания приложения Django и клиентской части React мы создадим модель клиентов, представляющую таблицу базы данных, где будет храниться информация о клиентах. Вам не потребуется SQL, поскольку Django Object Relational Mapper (ORM) будет обрабатывать операции базы данных посредством сопоставления классов и переменных Python с таблицами и столбцами SQL. Так Django ORM абстрагирует взаимодействия SQL с базой данных через интерфейс Python.

      Запустите свою виртуальную среду еще раз:

      • cd ~
      • source env/bin/activate

      Перейдите в каталог customers и откройте models.py, файл Python, где хранятся модели вашего приложения:

      • cd ~/djangoreactproject/customers/
      • nano models.py

      Файл будет иметь следующее содержание:

      ~/djangoreactproject/customers/models.py

      from django.db import models
      # Create your models here.
      

      API клиентской модели уже импортирован в файл из django.db с помощью выражения import models. Теперь вы добавите класс Customer как расширение models.Model. Каждая модель в Django представляет собой класс Python, являющийся расширением django.db.models.Model.

      Customer модель будет иметь следующие поля базы данных:

      • first_name — имя клиента.
      • last_name — фамилия клиента.
      • email — адрес электронной почты клиента.
      • phone — номер телефона клиента.
      • address — адрес клиента.
      • description — описание клиента.
      • createdAt — дата добавления клиента.

      Также мы добавим функцию __str__(), определяющую способ отображения модели. В нашем случае это будет имя клиента. Более подробную информацию по построению классов и определению объектов можно найти в документе «Построение классов и определение объектов в Python 3».

      Добавьте в файл следующий код:

      ~/djangoreactproject/customers/models.py

      from django.db import models
      
      class Customer(models.Model):
          first_name = models.CharField("First name", max_length=255)
          last_name = models.CharField("Last name", max_length=255)
          email = models.EmailField()
          phone = models.CharField(max_length=20)
          address =  models.TextField(blank=True, null=True)
          description = models.TextField(blank=True, null=True)
          createdAt = models.DateTimeField("Created At", auto_now_add=True)
      
          def __str__(self):
              return self.first_name
      

      Затем выполните миграцию базы данных для создания таблиц базы данных. Команда makemigrations создает файлы миграции, куда добавляются изменения модели, в команда migrate применяет все изменения в файлах миграции к базе данных.

      Перейдите обратно в корневую папку проекта root:

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

      • python manage.py makemigrations

      Вы получите результат, выглядящий следующим образом:

      Output

      customers/migrations/0001_initial.py - Create model Customer

      Примените эти изменения в базе данных:

      Вы увидите следующий результат, показывающий, что миграция выполнена успешно:

      Output

      Operations to perform: Apply all migrations: admin, auth, contenttypes, customers, sessions Running migrations: Applying customers.0001_initial... OK

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

      • python manage.py makemigrations --empty --name customers customers

      Вы увидите следующее подтверждение с именем файла миграции:

      Output

      Migrations for 'customers': customers/migrations/0002_customers.py

      Обратите внимание, что имя файла миграции 0002_customers.py.

      Затем перейдите в папку migrations приложения customers:

      • cd ~/djangoreactproject/customers/migrations

      Откройте созданный файл миграции:

      Это первоначальное содержание файла:

      ~/djangoreactproject/customers/migrations/0002_customers.py

      from django.db import migrations
      
      class Migration(migrations.Migration):
          dependencies = [
              ('customers', '0001_initial'),
          ]
          operations = [
          ]        
      

      Команда import импортирует API migrations. Это API Django для создания миграции из django.db, встроенного пакета, содержащего классы для работы с базами данных.

      Класс Migration — это класс Python, описывающий операции, выполняемые во время миграции баз данных. Этот класс является расширением migrations.Migration и содержит два списка:

      • dependencies: содержит зависимые миграции.
      • operations: содержит операции, которые будут выполняться во время проведения миграции.

      Затем добавьте метод для создания данных демо-клиента. Следующий метод нужно добавить перед определением класса Migration:

      ~/djangoreactproject/customers/migrations/0002_customers.py

      ...
      def create_data(apps, schema_editor):
          Customer = apps.get_model('customers', 'Customer')
          Customer(first_name="Customer 001", last_name="Customer 001", email="[email protected]", phone="00000000", address="Customer 000 Address", description= "Customer 001 description").save()
      
      ...
      

      В этом методе мы берем класс Customer нашего приложения customers и создаем демо-клиента для добавления в базу данных.

      Чтобы получить класс Customer, который позволит нам создавать новых клиентов, мы используем метод get_model() объекта apps. Объект apps представляет реестр установленных приложений и их моделей баз данных.

      Объект apps передается из метода RunPython(), когда мы используем его для запуска create_data(). Добавьте метод migrations.RunPython() в пустой список operations:

      ~/djangoreactproject/customers/migrations/0002_customers.py

      
      ...
          operations = [
              migrations.RunPython(create_data),
          ]  
      

      RunPython() — часть API Migrations, позволяющая запускать заданный код Python при миграции. Список operations указывает, что этот метод будет выполняться при проведении миграции.

      Полный файл будет выглядеть следующим образом:

      ~/djangoreactproject/customers/migrations/0002_customers.py

      from django.db import migrations
      
      def create_data(apps, schema_editor):
          Customer = apps.get_model('customers', 'Customer')
          Customer(first_name="Customer 001", last_name="Customer 001", email="[email protected]", phone="00000000", address="Customer 000 Address", description= "Customer 001 description").save()
      
      class Migration(migrations.Migration):
          dependencies = [
              ('customers', '0001_initial'),
          ]
          operations = [
              migrations.RunPython(create_data),
          ]        
      

      Дополнительную информацию по миграции данных можно найти в документации по миграции данных в Django

      Чтобы выполнить миграцию базы данных, вернитесь в папку root вашего проекта:

      Выполните миграцию базы данных для создания демонстрационных данных:

      Вы увидите результат, подтверждающий миграцию:

      Output

      Operations to perform: Apply all migrations: admin, auth, contenttypes, customers, sessions Running migrations: Applying customers.0002_customers... OK

      Более подробную информацию об этом процессе можно найти в документе «Создание моделей Django».

      После создания модели Customer и демо-данных, мы можем продолжить работу по созданию API REST.

      Шаг 5 — Создание API REST

      На этом шаге мы создадим API REST с помощью системы Django REST. Мы создадим несколько разных представлений API. Представление API представляет собой функцию, выполняющую запрос или вызов API, а конечная точка API — уникальный URL, представляющий собой элемент связи с системой REST. Например, когда пользователь направляет запрос GET в конечную точку API, Django вызывает соответствующую функцию или представление API для обработки запроса и возвращения любых возможных результатов.

      Также мы используем сериализаторы. Сериализатор в системе Django REST позволяет преобразовывать экземпляры сложных моделей и наборы QuerySets в формат JSON для использования API. Класс serializer может работать и в другом направлении, предоставляя механизмы для синтаксического анализа и десериализации данных в моделях Django и QuerySets.

      Мы получим следующие конечные точки API:

      • api/customers: эта конечная точка используется для создания клиентов и возврата разбитых на страницы наборов клиентов.
      • api/customers/: эта конечная точка испольузется для получения, обновления и удаления отдельных клиентов по первичным ключам или идентификаторам.

      Мы также создадим URL в файле проекта urls.py для соответствующих конечных точек (т.е. api/customers и api/customers/).

      Начнем с создания класса serializer для нашей модели Customer.

      Добавление класса Serializer

      Создание класса serializer для нашей модели Customer необходимо для преобразования экземпляров клиентов и наборов QuerySets в JSON и обратно. Чтобы создать класс serializer, сначала создайте файл serializers.py в приложении customers:

      • cd ~/djangoreactproject/customers/
      • nano serializers.py

      Добавьте следующий код для импорта API serializers и модели Customer:

      ~/djangoreactproject/customers/serializers.py

      from rest_framework import serializers
      from .models import Customer
      

      Затем создайте класс serializer, являющийся расширением serializers.ModelSerializer и указывающий поля для сериализации:

      ~/djangoreactproject/customers/serializers.py

      
      ...
      class CustomerSerializer(serializers.ModelSerializer):
      
          class Meta:
              model = Customer
              fields = ('pk','first_name', 'last_name', 'email', 'phone','address','description')
      

      Класс Meta указывает модель и поля для сериализации: pk, first_name, last_name, email, phone, address, description:

      Это полное содержание файла:

      ~/djangoreactproject/customers/serializers.py

      from rest_framework import serializers
      from .models import Customer
      
      class CustomerSerializer(serializers.ModelSerializer):
      
          class Meta:
              model = Customer
              fields = ('pk','first_name', 'last_name', 'email', 'phone','address','description')
      

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

      Добавление представлений API

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

      Откройте ~/djangoreactproject/customers/views.py:

      • nano ~/djangoreactproject/customers/views.py

      Удалите содержимое и добавьте следующие операции импорта:

      ~/djangoreactproject/customers/views.py

      from rest_framework.response import Response
      from rest_framework.decorators import api_view
      from rest_framework import status
      
      from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
      from .models import Customer
      from .serializers import *
      

      Мы импортируем созданный сериализатор, а также модель Customer и API Django и системы Django REST.

      Затем добавьте представление для обработки запросов POST и GET HTTP:

      ~/djangoreactproject/customers/views.py

      ...
      
      @api_view(['GET', 'POST'])
      def customers_list(request):
          """
       List  customers, or create a new customer.
       """
          if request.method == 'GET':
              data = []
              nextPage = 1
              previousPage = 1
              customers = Customer.objects.all()
              page = request.GET.get('page', 1)
              paginator = Paginator(customers, 10)
              try:
                  data = paginator.page(page)
              except PageNotAnInteger:
                  data = paginator.page(1)
              except EmptyPage:
                  data = paginator.page(paginator.num_pages)
      
              serializer = CustomerSerializer(data,context={'request': request} ,many=True)
              if data.has_next():
                  nextPage = data.next_page_number()
              if data.has_previous():
                  previousPage = data.previous_page_number()
      
              return Response({'data': serializer.data , 'count': paginator.count, 'numpages' : paginator.num_pages, 'nextlink': '/api/customers/?page=' + str(nextPage), 'prevlink': '/api/customers/?page=' + str(previousPage)})
      
          elif request.method == 'POST':
              serializer = CustomerSerializer(data=request.data)
              if serializer.is_valid():
                  serializer.save()
                  return Response(serializer.data, status=status.HTTP_201_CREATED)
              return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
      

      Вначале мы используем декоратор @api_view(['GET', 'POST']) для создания представления API, способного принимать запросы GET и POST. Декоратор — это функция, которая берет другую функцию и динамически расширяет ее.

      В теле метода мы используем переменную request.method для проверки текущего метода HTTP и выполнения соответствующей логики в зависимости от типа запроса:

      • Если это запрос GET, метод разбивает данные на страницы с помощью Django Paginator и возвращает первую страницу данных после сериализации, количество доступных клиентов, количество доступных страниц и ссылки на предыдущие и последующие страницы. Paginator — это встроенный класс Django, выполняющий разбивку списка данных на страницы и предоставляющий методы доступа к элементам на каждой странице.
      • Если это запрос POST, метод выполняет сериализацию полученных данных клиента, а затем вызывает метод save() с помощью объекта serializer. Затем он возвращает объект Response, являющийся экземпляром HttpResponse, с кодом состояния 201. Каждое создаваемое представление отвечает за возврат объекта HttpResponse. Метод save() сохраняет сериализированные данные в базе данных.

      Дополнительную информацию об HttpResponse и представлениях можно найти в этой дискуссии о создании функций представления.

      Теперь добавьте представление API, которое будет отвечать за обработку запросов GET, PUT и DELETE для получения, обновления и удаления клиентов по pk (первичный ключ):

      ~/djangoreactproject/customers/views.py

      
      ...
      @api_view(['GET', 'PUT', 'DELETE'])
      def customers_detail(request, pk):
       """
       Retrieve, update or delete a customer by id/pk.
       """
          try:
              customer = Customer.objects.get(pk=pk)
          except Customer.DoesNotExist:
              return Response(status=status.HTTP_404_NOT_FOUND)
      
          if request.method == 'GET':
              serializer = CustomerSerializer(customer,context={'request': request})
              return Response(serializer.data)
      
          elif request.method == 'PUT':
              serializer = CustomerSerializer(customer, data=request.data,context={'request': request})
              if serializer.is_valid():
                  serializer.save()
                  return Response(serializer.data)
              return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
      
          elif request.method == 'DELETE':
              customer.delete()
              return Response(status=status.HTTP_204_NO_CONTENT)
      

      Метод оформлен с помощью декораторов @api_view(['GET', 'PUT', 'DELETE']). Это означает, что он является представлением API, который может принимать запросы GET, PUT и DELETE.

      Проверка поля request.method проверяет метод запроса и вызывает нужную логику в зависимости от значения:

      • Если это запрос GET, выполняется сериализация данных клиента и их отправка с помощью объекта Response.
      • Если это запрос PUT, метод создает сериализатор для новых данных клиента. Затем он вызывает метод save() созданного объекта serializer. Наконец, он отправляет объект Response с обновленными данными клиента.
      • Если это запрос DELETE, метод вызывает метод delete() объекта customer для его удаления, после чего возвращает объект Response, не содержащий никаких данных.

      Завершенный файл выглядит следующим образом:

      ~/djangoreactproject/customers/views.py

      from rest_framework.response import Response
      from rest_framework.decorators import api_view
      from rest_framework import status
      
      from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
      from .models import Customer
      from .serializers import *
      
      
      @api_view(['GET', 'POST'])
      def customers_list(request):
          """
       List  customers, or create a new customer.
       """
          if request.method == 'GET':
              data = []
              nextPage = 1
              previousPage = 1
              customers = Customer.objects.all()
              page = request.GET.get('page', 1)
              paginator = Paginator(customers, 5)
              try:
                  data = paginator.page(page)
              except PageNotAnInteger:
                  data = paginator.page(1)
              except EmptyPage:
                  data = paginator.page(paginator.num_pages)
      
              serializer = CustomerSerializer(data,context={'request': request} ,many=True)
              if data.has_next():
                  nextPage = data.next_page_number()
              if data.has_previous():
                  previousPage = data.previous_page_number()
      
              return Response({'data': serializer.data , 'count': paginator.count, 'numpages' : paginator.num_pages, 'nextlink': '/api/customers/?page=' + str(nextPage), 'prevlink': '/api/customers/?page=' + str(previousPage)})
      
          elif request.method == 'POST':
              serializer = CustomerSerializer(data=request.data)
              if serializer.is_valid():
                  serializer.save()
                  return Response(serializer.data, status=status.HTTP_201_CREATED)
              return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
      
      @api_view(['GET', 'PUT', 'DELETE'])
      def customers_detail(request, pk):
          """
       Retrieve, update or delete a customer by id/pk.
       """
          try:
              customer = Customer.objects.get(pk=pk)
          except Customer.DoesNotExist:
              return Response(status=status.HTTP_404_NOT_FOUND)
      
          if request.method == 'GET':
              serializer = CustomerSerializer(customer,context={'request': request})
              return Response(serializer.data)
      
          elif request.method == 'PUT':
              serializer = CustomerSerializer(customer, data=request.data,context={'request': request})
              if serializer.is_valid():
                  serializer.save()
                  return Response(serializer.data)
              return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
      
          elif request.method == 'DELETE':
              customer.delete()
              return Response(status=status.HTTP_204_NO_CONTENT)
      

      Теперь мы можем перейти к созданию конечных точек.

      Добавление конечных точек API

      Теперь мы создадим конечные точки API: api/customers/ для запроса и создания клиентов и api/customers/<pk> для получения, обновления и удаления отдельных клиентов по pk.

      Откройте ~/djangoreactproject/djangoreactproject/urls.py:

      • nano ~/djangoreactproject/djangoreactproject/urls.py

      Оставьте его содержание без изменений, но добавьте в начало файла импорт в представление customers:

      ~/djangoreactproject/djangoreactproject/urls.py

      from django.contrib import admin
      from django.urls import path
      from customers import views
      from django.conf.urls import url
      

      Затем добавьте URL-адреса api/customers/ и api/customers/ в список urlpatterns, содержащий URL приложения:

      ~/djangoreactproject/djangoreactproject/urls.py

      ...
      
      urlpatterns = [
          path('admin/', admin.site.urls),
          url(r'^api/customers/$', views.customers_list),
          url(r'^api/customers/(?P<pk>[0-9]+)$', views.customers_detail),
      ]
      

      Мы создали конечные точки REST, теперь рассмотрим их употребление.

      Шаг 6 — Употребление REST API с Axios

      На этом шаге мы установим Axios, клиент HTTP, служащий для вызова API. Также мы создадим класс для потребления созданных нами конечных точек API.

      Вначале отключите свою виртуальную среду:

      Затем перейдите в папку frontend:

      • cd ~/djangoreactproject/frontend

      Установите axios из npm с помощью следующей команды:

      Опция --save добавляет зависимость axios в файл package.json вашего приложения.

      Затем создайте файл JavaScript под названием CustomersService.js, который будет содержать код для вызова API REST. Это будет реализовано в папке src, где будет располагаться код приложения для нашего проекта:

      • cd src
      • nano CustomersService.js

      Добавьте следующий код, содержащий методы подключения к Django REST API:

      ~/djangoreactproject/frontend/src/CustomersService.js

      import axios from 'axios';
      const API_URL = 'http://localhost:8000';
      
      export default class CustomersService{
      
          constructor(){}
      
      
          getCustomers() {
              const url = `${API_URL}/api/customers/`;
              return axios.get(url).then(response => response.data);
          }  
          getCustomersByURL(link){
              const url = `${API_URL}${link}`;
              return axios.get(url).then(response => response.data);
          }
          getCustomer(pk) {
              const url = `${API_URL}/api/customers/${pk}`;
              return axios.get(url).then(response => response.data);
          }
          deleteCustomer(customer){
              const url = `${API_URL}/api/customers/${customer.pk}`;
              return axios.delete(url);
          }
          createCustomer(customer){
              const url = `${API_URL}/api/customers/`;
              return axios.post(url,customer);
          }
          updateCustomer(customer){
              const url = `${API_URL}/api/customers/${customer.pk}`;
              return axios.put(url,customer);
          }
      }
      

      Класс CustomersService будет вызывать следующие методы Axios:

      • getCustomers(): получает первую страницу клиентов.
      • getCustomersByURL(): получает клиентов по URL. Это позволяет получить следующие страницы клиентов путем передачи таких ссылок, как /api/customers/?page=2.
      • getCustomer(): получает клиента по первичному ключу.
      • createCustomer(): создает клиента.
      • updateCustomer(): обновляет клиента.
      • deleteCustomer(): удаляет клиента.

      Теперь мы можем выводить данные нашего API в пользовательском интерфейсе React, создавая компонент CustomersList.

      Шаг 7 — Отображение данных из API в приложении React

      На этом шаге мы создадим компонент CustomersList React. Компонент React представляет часть пользовательского интерфейса; также он позволяет разделять пользовательский интерфейс на независимые элементы многоразового использования.

      Для начала создайте CustomersList.js в папке frontend/src:

      • nano ~/djangoreactproject/frontend/src/CustomersList.js

      Вначале импортируйте React и Component для создания компонента React:

      ~/djangoreactproject/frontend/src/CustomersList.js

      import  React, { Component } from  'react';
      

      Затем импортируйте и создайте экземпляр модуля CustomersService, который вы создали на предыдущем шаге и который предоставляет методы взаимодействия с серверной частью REST API:

      ~/djangoreactproject/frontend/src/CustomersList.js

      
      ...
      import  CustomersService  from  './CustomersService';
      
      const  customersService  =  new  CustomersService();
      

      Затем создайте компонент CustomersList, расширяющий Component для вызова REST API. Компонент React должен расширять класс Component или создавать его подкласс. Дополнительную информацию о классах E6 и наследовании можно найти в обучающем руководстве «Понимание классов в JavaScript».

      Добавьте следующий код для создания компонента React, являющегося расширением react.Component:

      ~/djangoreactproject/frontend/src/CustomersList.js

      
      ...
      class  CustomersList  extends  Component {
      
          constructor(props) {
              super(props);
              this.state  = {
                  customers: [],
                  nextPageURL:  ''
              };
              this.nextPage  =  this.nextPage.bind(this);
              this.handleDelete  =  this.handleDelete.bind(this);
          }
      }
      export  default  CustomersList;
      

      Внутри конструктора мы выполняем инициализацию объекта state. В нем хранятся переменные state нашего компонента, для чего используется пустой массив customers. В этом массиве будут храниться клиенты и nextPageURL, где будет храниться URL следующей страницы для получения от серверного API. Также мы выполняем привязку методов nextPage() и handleDelete(), this чтобы они были доступны из кода HTML.

      Затем добавьте метод componentDidMount() и вызов getCustomers() из класса CustomersList, после чего закройте фигурную скобку.

      Метод componentDidMount() — это метод жизненного цикла компонента, вызываемый, когда компонент создается и вставляется в DOM. getCustomers() вызывает объект Customers Service для получения первой страницы данных и ссылки на следующую страницу из серверной части Django:

      ~/djangoreactproject/frontend/src/CustomersList.js

      
      ...
      componentDidMount() {
          var  self  =  this;
          customersService.getCustomers().then(function (result) {
              self.setState({ customers:  result.data, nextPageURL:  result.nextlink})
          });
      }
      

      Теперь добавьте метод handleDelete(), который обрабатывает удаление клиента, под componentDidMount():

      ~/djangoreactproject/frontend/src/CustomersList.js

      
      ...
      handleDelete(e,pk){
          var  self  =  this;
          customersService.deleteCustomer({pk :  pk}).then(()=>{
              var  newArr  =  self.state.customers.filter(function(obj) {
                  return  obj.pk  !==  pk;
              });
              self.setState({customers:  newArr})
          });
      }
      

      Метод handleDelete() вызывает метод deleteCustomer() для удаления клиента по pk (первичный ключ). Если операция выполняется успешно, удаленный customer отфильтровывается из массива

      Затем добавьте метод nextPage() для получения данных следующей страницы и обновления ссылки на следующую страницу:

      ~/djangoreactproject/frontend/src/CustomersList.js

      
      ...
      nextPage(){
          var  self  =  this;
          customersService.getCustomersByURL(this.state.nextPageURL).then((result) => {
              self.setState({ customers:  result.data, nextPageURL:  result.nextlink})
          });
      }
      

      Метод nextPage() вызывает метод getCustomersByURL(), который берет URL следующей страницы из объекта состояния this.state.nextPageURL и обновляет массив customers, добавляя в него возвращаемые данные.

      Наконец, добавьте метод render() компонента, который выполняет рендеринг таблицы клиентов из состояния компонента:

      ~/djangoreactproject/frontend/src/CustomersList.js

      
      ...
      render() {
      
          return (
          <div  className="customers--list">
              <table  className="table">
                  <thead  key="thead">
                  <tr>
                      <th>#</th>
                      <th>First Name</th>
                      <th>Last Name</th>
                      <th>Phone</th>
                      <th>Email</th>
                      <th>Address</th>
                      <th>Description</th>
                      <th>Actions</th>
                  </tr>
                  </thead>
                  <tbody>
                      {this.state.customers.map( c  =>
                      <tr  key={c.pk}>
                          <td>{c.pk}  </td>
                          <td>{c.first_name}</td>
                          <td>{c.last_name}</td>
                          <td>{c.phone}</td>
                          <td>{c.email}</td>
                          <td>{c.address}</td>
                          <td>{c.description}</td>
                          <td>
                          <button  onClick={(e)=>  this.handleDelete(e,c.pk) }> Delete</button>
                          <a  href={"/customer/" + c.pk}> Update</a>
                          </td>
                      </tr>)}
                  </tbody>
              </table>
              <button  className="btn btn-primary"  onClick=  {  this.nextPage  }>Next</button>
          </div>
          );
      }
      

      Это полное содержание файла:

      ~/djangoreactproject/frontend/src/CustomersList.js

      import  React, { Component } from  'react';
      import  CustomersService  from  './CustomersService';
      
      const  customersService  =  new  CustomersService();
      
      class  CustomersList  extends  Component {
      
      constructor(props) {
          super(props);
          this.state  = {
              customers: [],
              nextPageURL:  ''
          };
          this.nextPage  =  this.nextPage.bind(this);
          this.handleDelete  =  this.handleDelete.bind(this);
      }
      
      componentDidMount() {
          var  self  =  this;
          customersService.getCustomers().then(function (result) {
              console.log(result);
              self.setState({ customers:  result.data, nextPageURL:  result.nextlink})
          });
      }
      handleDelete(e,pk){
          var  self  =  this;
          customersService.deleteCustomer({pk :  pk}).then(()=>{
              var  newArr  =  self.state.customers.filter(function(obj) {
                  return  obj.pk  !==  pk;
              });
      
              self.setState({customers:  newArr})
          });
      }
      
      nextPage(){
          var  self  =  this;
          console.log(this.state.nextPageURL);        
          customersService.getCustomersByURL(this.state.nextPageURL).then((result) => {
              self.setState({ customers:  result.data, nextPageURL:  result.nextlink})
          });
      }
      render() {
      
          return (
              <div  className="customers--list">
                  <table  className="table">
                  <thead  key="thead">
                  <tr>
                      <th>#</th>
                      <th>First Name</th>
                      <th>Last Name</th>
                      <th>Phone</th>
                      <th>Email</th>
                      <th>Address</th>
                      <th>Description</th>
                      <th>Actions</th>
                  </tr>
                  </thead>
                  <tbody>
                  {this.state.customers.map( c  =>
                      <tr  key={c.pk}>
                      <td>{c.pk}  </td>
                      <td>{c.first_name}</td>
                      <td>{c.last_name}</td>
                      <td>{c.phone}</td>
                      <td>{c.email}</td>
                      <td>{c.address}</td>
                      <td>{c.description}</td>
                      <td>
                      <button  onClick={(e)=>  this.handleDelete(e,c.pk) }> Delete</button>
                      <a  href={"/customer/" + c.pk}> Update</a>
                      </td>
                  </tr>)}
                  </tbody>
                  </table>
                  <button  className="btn btn-primary"  onClick=  {  this.nextPage  }>Next</button>
              </div>
              );
        }
      }
      export  default  CustomersList;
      

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

      Шаг 8 — Добавление компонентов React для создания и обновления клиентов

      На этом шаге мы создадим компонент CustomerCreateUpdate, который будет заниматься созданием и обновлением клиентов. Для этого он будет предоставлять форму, где пользователи смогут вводить данные о новом клиенте или изменять уже записанные данные.

      Создайте в папке frontend/src файл CustomerCreateUpdate.js:

      • nano ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      Добавьте следующий код для создания компонента React, импортировав классы React и Component:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      import  React, { Component } from  'react';
      

      Также мы можем импортировать класс CustomersService, созданный на предыдущем шаге и создать экземпляр этого класса, который предоставляет методы взаимодействия с серверной частью API REST:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      ...
      import  CustomersService  from  './CustomersService';
      
      const  customersService  =  new  CustomersService();
      

      Далее создайте компонент CustomerCreateUpdate, расширяющий класс Component для создания и обновления клиентов:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      
      ...
      class  CustomerCreateUpdate  extends  Component {
      
          constructor(props) {
              super(props);
          }
      
      }
      export default CustomerCreateUpdate;
      

      Добавьте в определение класса метод компонента render(), отвечающий за рендеринг формы HTML, куда вводится информация о клиенте:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      
      ...
      render() {
              return (
                <form onSubmit={this.handleSubmit}>
                <div className="form-group">
                  <label>
                    First Name:</label>
                    <input className="form-control" type="text" ref='firstName' />
      
                  <label>
                    Last Name:</label>
                    <input className="form-control" type="text" ref='lastName'/>
      
                  <label>
                    Phone:</label>
                    <input className="form-control" type="text" ref='phone' />
      
                  <label>
                    Email:</label>
                    <input className="form-control" type="text" ref='email' />
      
                  <label>
                    Address:</label>
                    <input className="form-control" type="text" ref='address' />
      
                  <label>
                    Description:</label>
                    <textarea className="form-control" ref='description' ></textarea>
      
      
                  <input className="btn btn-primary" type="submit" value="Submit" />
                  </div>
                </form>
              );
        }
      

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

      Над методом render() создайте определение метода handleSubmit(event), чтобы при нажатии на кнопку Submit (Отправить) функция работала надлежащим образом:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      
      ...
      handleSubmit(event) {
          const { match: { params } } =  this.props;
          if(params  &&  params.pk){
              this.handleUpdate(params.pk);
          }
          else
          {
              this.handleCreate();
          }
          event.preventDefault();
      }
      
      ...
      

      Метод handleSubmit(event) обрабатывает отправку форм и (в зависимости от маршрута) вызывет метод handleUpdate(pk) для обновления клиента с переданным pk или метод handleCreate() для создания нового клиента. Мы дадим краткое определение этих методов.

      Выполните в конструкторе компонентов привязку добавленного метода handleSubmit() к этому методу, чтобы вы могли использовать его в своей форме:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      ...
      class CustomerCreateUpdate extends Component {
      
      constructor(props) {
          super(props);
          this.handleSubmit = this.handleSubmit.bind(this);
      }
      ...
      

      Затем дайте определение методу handleCreate() для создания клиента на основе данных формы. Над методом handleSubmit(event) добавьте следующий код:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      
      ...
      handleCreate(){
          customersService.createCustomer(
              {
              "first_name":  this.refs.firstName.value,
              "last_name":  this.refs.lastName.value,
              "email":  this.refs.email.value,
              "phone":  this.refs.phone.value,
              "address":  this.refs.address.value,
              "description":  this.refs.description.value
              }).then((result)=>{
                      alert("Customer created!");
              }).catch(()=>{
                      alert('There was an error! Please re-check your form.');
              });
      }
      
      ...
      

      Метод handleCreate() будет использоваться для создания клиента на основе введенных данных. Он вызывает соответствующий метод CustomersService.createCustomer(), который API использует для вызова серверной части для создания клиента.

      Под методом handleCreate() дайте определение метода handleUpdate(pk) для выполнения обновлений:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      
      ...
      handleUpdate(pk){
      customersService.updateCustomer(
          {
          "pk":  pk,
          "first_name":  this.refs.firstName.value,
          "last_name":  this.refs.lastName.value,
          "email":  this.refs.email.value,
          "phone":  this.refs.phone.value,
          "address":  this.refs.address.value,
          "description":  this.refs.description.value
          }
          ).then((result)=>{
      
              alert("Customer updated!");
          }).catch(()=>{
              alert('There was an error! Please re-check your form.');
          });
      }
      

      Метод updateCustomer() обновляет клиента по pk, используя новую информацию из формы данных о клиенте. Он вызывает метод customersService.updateCustomer().

      Затем добавьте метод componentDidMount(). Если пользователь посещает маршрут customer/:pk, нужно заполнить форму информацией о клиенте, используя первичный ключ из URL. Для этого мы можем добавить метод getCustomer(pk) после монтирования компонента в событии жизненного цикла componentDidMount(). Для добавления этого метода добавьте следующий код после конструктора компонентов:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      
      ...
      componentDidMount(){
          const { match: { params } } =  this.props;
          if(params  &&  params.pk)
          {
              customersService.getCustomer(params.pk).then((c)=>{
                  this.refs.firstName.value  =  c.first_name;
                  this.refs.lastName.value  =  c.last_name;
                  this.refs.email.value  =  c.email;
                  this.refs.phone.value  =  c.phone;
                  this.refs.address.value  =  c.address;
                  this.refs.description.value  =  c.description;
              })
          }
      }
      

      Это полное содержание файла:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      import React, { Component } from 'react';
      import CustomersService from './CustomersService';
      
      const customersService = new CustomersService();
      
      class CustomerCreateUpdate extends Component {
          constructor(props) {
              super(props);
      
              this.handleSubmit = this.handleSubmit.bind(this);
            }
      
            componentDidMount(){
              const { match: { params } } = this.props;
              if(params && params.pk)
              {
                customersService.getCustomer(params.pk).then((c)=>{
                  this.refs.firstName.value = c.first_name;
                  this.refs.lastName.value = c.last_name;
                  this.refs.email.value = c.email;
                  this.refs.phone.value = c.phone;
                  this.refs.address.value = c.address;
                  this.refs.description.value = c.description;
                })
              }
            }
      
            handleCreate(){
              customersService.createCustomer(
                {
                  "first_name": this.refs.firstName.value,
                  "last_name": this.refs.lastName.value,
                  "email": this.refs.email.value,
                  "phone": this.refs.phone.value,
                  "address": this.refs.address.value,
                  "description": this.refs.description.value
              }          
              ).then((result)=>{
                alert("Customer created!");
              }).catch(()=>{
                alert('There was an error! Please re-check your form.');
              });
            }
            handleUpdate(pk){
              customersService.updateCustomer(
                {
                  "pk": pk,
                  "first_name": this.refs.firstName.value,
                  "last_name": this.refs.lastName.value,
                  "email": this.refs.email.value,
                  "phone": this.refs.phone.value,
                  "address": this.refs.address.value,
                  "description": this.refs.description.value
              }          
              ).then((result)=>{
                console.log(result);
                alert("Customer updated!");
              }).catch(()=>{
                alert('There was an error! Please re-check your form.');
              });
            }
            handleSubmit(event) {
              const { match: { params } } = this.props;
      
              if(params && params.pk){
                this.handleUpdate(params.pk);
              }
              else
              {
                this.handleCreate();
              }
      
              event.preventDefault();
            }
      
            render() {
              return (
                <form onSubmit={this.handleSubmit}>
                <div className="form-group">
                  <label>
                    First Name:</label>
                    <input className="form-control" type="text" ref='firstName' />
      
                  <label>
                    Last Name:</label>
                    <input className="form-control" type="text" ref='lastName'/>
      
                  <label>
                    Phone:</label>
                    <input className="form-control" type="text" ref='phone' />
      
                  <label>
                    Email:</label>
                    <input className="form-control" type="text" ref='email' />
      
                  <label>
                    Address:</label>
                    <input className="form-control" type="text" ref='address' />
      
                  <label>
                    Description:</label>
                    <textarea className="form-control" ref='description' ></textarea>
      
      
                  <input className="btn btn-primary" type="submit" value="Submit" />
                  </div>
                </form>
              );
            }  
      }
      
      export default CustomerCreateUpdate;
      

      После создания компонента CustomerCreateUpdate мы можем обновить компонент main App и добавить ссылки на различные созданные нами компоненты.

      Шаг 9 — Обновление компонента Main App

      На этом шаге мы обновим компонент App нашего приложения для создания ссылок на компоненты, созданные на предыдущих шагах.

      Запустите из папки frontend следующую команду для установки React Router, что позволит добавить маршрутизацию и навигацию между разными компонентами React:

      • cd ~/djangoreactproject/frontend
      • npm install --save react-router-dom

      Далее откройте ~/djangoreactproject/frontend/src/App.js:

      • nano ~/djangoreactproject/frontend/src/App.js

      Удалите все содержимое и добавьте следующий код, чтобы импортировать необходимые классы для добавления маршрутизации. В их число входит класс BrowserRouter, создающий компонент Router, и класс Route, создающий компонент route:

      ~/djangoreactproject/frontend/src/App.js

      import  React, { Component } from  'react';
      import { BrowserRouter } from  'react-router-dom'
      import { Route, Link } from  'react-router-dom'
      import  CustomersList  from  './CustomersList'
      import  CustomerCreateUpdate  from  './CustomerCreateUpdate'
      import  './App.css';
      

      BrowserRouter обеспечивает синхронизацию пользовательского интерфейса с URL, используя API истории HTML5.

      Далее создайте базовую схему, обеспечивающую базовый компонент для включения в компонент BrowserRouter:

      ~/djangoreactproject/frontend/src/App.js

      ...
      
      const  BaseLayout  = () => (
      <div  className="container-fluid">
          <nav  className="navbar navbar-expand-lg navbar-light bg-light">
              <a  className="navbar-brand"  href="https://www.digitalocean.com/#">Django React Demo</a>
              <button  className="navbar-toggler"  type="button"  data-toggle="collapse"  data-target="#navbarNavAltMarkup"  aria-controls="navbarNavAltMarkup"  aria-expanded="false"  aria-label="Toggle navigation">
              <span  className="navbar-toggler-icon"></span>
          </button>
          <div  className="collapse navbar-collapse"  id="navbarNavAltMarkup">
              <div  className="navbar-nav">
                  <a  className="nav-item nav-link"  href="/">CUSTOMERS</a>
                  <a  className="nav-item nav-link"  href="http://www.digitalocean.com/customer">CREATE CUSTOMER</a>
              </div>
          </div>
          </nav>
          <div  className="content">
              <Route  path="/"  exact  component={CustomersList}  />
              <Route  path="/customer/:pk"  component={CustomerCreateUpdate}  />
              <Route  path="/customer/"  exact  component={CustomerCreateUpdate}  />
          </div>
      </div>
      )
      

      Мы используем компонент Route для определения маршрутов нашего приложения; компонент, который должен загружать маршрутизатор после обнаружения совпадения. Для каждого маршрута требуются параметры path для указания пути сопоставления и component для указания загружаемого компонента. Свойство exact указывает маршрутизатору на необходимость точного соответствия пути.

      Наконец, создайте компонент App. Это корневой компонент или компонент самого верхнего уровня нашего приложения React:

      ~/djangoreactproject/frontend/src/App.js

      ...
      
      class  App  extends  Component {
      
      render() {
          return (
          <BrowserRouter>
              <BaseLayout/>
          </BrowserRouter>
          );
      }
      }
      export  default  App;
      

      Мы поместили компонент BaseLayout в компонент BrowserRouter, потому что наше приложение предусматривает работу через браузер.

      Завершенный файл выглядит следующим образом:

      ~/djangoreactproject/frontend/src/App.js

      import React, { Component } from 'react';
      import { BrowserRouter } from 'react-router-dom'
      import { Route, Link } from 'react-router-dom'
      
      import  CustomersList from './CustomersList'
      import  CustomerCreateUpdate  from './CustomerCreateUpdate'
      import './App.css';
      
      const BaseLayout = () => (
        <div className="container-fluid">
      <nav className="navbar navbar-expand-lg navbar-light bg-light">
        <a className="navbar-brand" href="https://www.digitalocean.com/#">Django React Demo</a>
        <button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
          <span className="navbar-toggler-icon"></span>
        </button>
        <div className="collapse navbar-collapse" id="navbarNavAltMarkup">
          <div className="navbar-nav">
            <a className="nav-item nav-link" href="/">CUSTOMERS</a>
            <a className="nav-item nav-link" href="http://www.digitalocean.com/customer">CREATE CUSTOMER</a>
      
          </div>
        </div>
      </nav>  
      
          <div className="content">
            <Route path="/" exact component={CustomersList} />
            <Route path="/customer/:pk"  component={CustomerCreateUpdate} />
            <Route path="/customer/" exact component={CustomerCreateUpdate} />
      
          </div>
      
        </div>
      )
      
      class App extends Component {
        render() {
          return (
            <BrowserRouter>
              <BaseLayout/>
            </BrowserRouter>
          );
        }
      }
      
      export default App;
      

      После добавления маршрутизации в приложение можно начать его тестирование. Откройте адрес http://localhost:3000. Вы увидите первую страницу приложения:

      Главная страница приложения

      С этим приложением вы получили основу для создания приложения CRM.

      Заключение

      Это обучающее руководство помогло вам научиться создавать демонстрационное приложение с помощью Django и React. Вы использовали систему Django REST для создания REST API, Axios для потребления API, и Bootstrap 4 для стилизации CSS. Исходный код этого проекта можно найти в хранилище GitHub.

      В этом обучающем руководстве были использованы отдельные приложения для клиентской части и серверной части. Другой подход к интеграции React с Django можно найти в этом руководстве и этом руководстве.

      Дополнительную информацию о создании приложений с помощью Django можно найти в серии публикаций «Разработка в Django». Также вы можете ознакомиться с официальной документацией по Django.



      Source link