#36. Management commands

Management Commands

Менеджмент команды и настройки.

Management commands в рамках Django - это возможность запустить скрипт из консоли для выполнения абсолютно различных действий.

Наиболее частые сферы применения — это действия, выполняемые разово или периодически. Например, отправка пользователям разовых сообщений, получение выборки данных из БД, проверка наличия необходимых файлов и папок перед накатыванием обновлений, быстрое создание объектов модели при разработке и т.д.

Существует три способа запуска менеджмент команды

django-admin <command> [options]
python manage.py <command> [options]
python -m django <command> [options]

В случае запуска через django-admin вы можете указать какой файл настроек использовать при помощи опции --settings.

Если вы запускаете команду через manage.py (самый распространенный способ) файл настроек будет выбран в соответствии с самим файлом manage.py , так как информация о файле настроек для Django проекта находится именно в файле manage.py, в DJANGO_SETTINGS_MODULE

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

Доступные команды

help

Список доступных команд

>>> python manage.py help


Type 'manage.py help <subcommand>' for help on a specific subcommand.

Available subcommands:

[auth]
    changepassword
    createsuperuser

[contenttypes]
    remove_stale_contenttypes

[django]
    check
    compilemessages
    createcachetable
    dbshell
    diffsettings
    dumpdata
    flush
    inspectdb
    loaddata
    makemessages
    makemigrations
    migrate
    sendtestemail
    shell
    showmigrations
    sqlflush
    sqlmigrate
    sqlsequencereset
    squashmigrations
    startapp
    startproject
    test
    testserver

[sessions]
    clearsessions

[staticfiles]
    collectstatic
    findstatic
    runserver

Интернационализация и локализация

В Django уже есть встроеннае поддержка переводов.
Django полностью поддерживает перевод текста, форматирование дат, времени и чисел, а также часовых поясов.
Подробнее тут

Слова «интернационализация» и «локализация» часто вызывают путаницу.

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

Для добавления строки в список для перевода ее необходимо передавать в gettext

from django.utils.translation import gettext as _

_('Translate it')

В теплейтах используется тег translate(до Django 3.0 включительно был тег trans). Для его подключение необходимо подкгрузить модуль i18n вверху шаблона :

{%  load  i18n  %}

{% translate "This is the title." %}
{% translate myvar %}

Либо blocktranslate для многострочных строк:

{%  load  i18n  %}

{% blocktranslate %}Back to '{{ race }}' homepage{% endblocktranslate %}

В settings.py есть переменные, отвечаюшие за перевод:

LANGUAGE_CODE = 'en-us'  # язык по умолчанию, можно поставить ru, и даже админка станет на русском
  
USE_I18N = True  # i18n - сокращение от 'internationalization'
  
USE_L10N = True  # L10n - от 'localization' 

makemessages

python manage.py makemessages

Команда для работы с переводами (локализацией) сайтов.

Переводы попадут в папку locale приложения, если ее нет - ее необходимо создать!

Команда проходит через весь код и ищет места, которое заготовлены для перевода (для python кода, это везде где вы используете метод gettext, для шаблонов везде где используется темплейт тег translate, )

Создаёт\Обновляет файлы в которых хранятся\будут храниться переводы текста на друге языки. Принимает параметры --all , --extension, --locale, --exclude, --domain, --ignore итд.

Подробности использования параметров тут

Обсудим основные.

--locale LOCALE, -l LOCALE нужно, чтобы указать на какой язык планируется перевод (на самом деле повлияет только на то, как будет называться файл с переводами, и как этот перевод будет называться в системе), например для французского можно
назвать файл fr, для итальянского it итд.

django-admin makemessages --locale=pt_BR
django-admin makemessages --locale=pt_BR --locale=fr
django-admin makemessages -l pt_BR
django-admin makemessages -l pt_BR -l fr

--ignore PATTERN - Игнорировать (не искать) переводы в определённых местах, например `–ignore *.py’ - игнорировать все пайтон файлы.

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

>>> python manage.py makemessages -l=ru
>
processing locale ru

enter image description here

# SOME DESCRIPTIVE TITLE.  
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER  
# This file is distributed under the same license as the PACKAGE package.  
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.  
#  
#, fuzzy  
msgid ""  
msgstr ""  
"Project-Id-Version: PACKAGE VERSION\n"  
"Report-Msgid-Bugs-To: \n"  
"POT-Creation-Date: 2021-10-18 22:33+0000\n"  
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"  
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"  
"Language-Team: LANGUAGE <LL@li.org>\n"  
"Language: \n"  
"MIME-Version: 1.0\n"  
"Content-Type: text/plain; charset=UTF-8\n"  
"Content-Transfer-Encoding: 8bit\n"  
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"  
"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"  
"%100>=11 && n%100<=14)? 2 : 3);\n"  
  
#: myapp/models.py:22  
msgid "Not selected"  
msgstr ""  
  
#: myapp/models.py:23  
msgid "Comedy"  
msgstr ""  
  
#: myapp/models.py:24  
msgid "Action"  
msgstr ""  
  
#: myapp/models.py:25  
msgid "Beauty"  
msgstr ""  
  
#: myapp/models.py:26  
msgid "Other"  
msgstr ""  
  
#: myapp/templates/base.html:6  
msgid "Title"  
msgstr ""

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

compilemessages

Компилирует файлы для переводов.
Делает из .po (Portable Object) файлов .mo (Machine Object) файлы. Django принимает именно .mo как файлы откуда брать перевод.
Поддерживает указание локали и игнор, подробнее в документации)

>>> python manage.py compilemessages

processing file django.po in mysite/myapp/locale/ru/LC_MESSAGES

enter image description here

createcachedtable

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

Остальные команды

check

python manage.py check [app_label [app_label ...]]

Например:

>>> python manage.py check auth admin myapp

System check identified no issues (0 silenced).

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

shell

Уже известная вам команда shell открывает интерактивную python консоль, с уже импортированными библиотеками вашего проекта, например Django.

dbshell

По аналогии со знакомой нам командой shell открывает консоль со всеми необходимыми импортированными данными, но для базы данных.

Например для postgresql, откроется psql итд.

diffsettings

Команда, которая покажет чем, отличается ваш файл settings.py от оригинала.

Фискстуры

dumpdata

Команда для работы с фикстурами.

Фикстуры - это заранее подготовленные данные. В основном, это файлы отображения базы данных в формат JSON.

Команда dumpdata вытащит все данные из базы данных, и преобразует всё в формат JSON.

Может принимать имя только нескольких приложений, или даже только некоторых моделей, или наоборот исключить какие-то приложения или модели

loaddata

Команда, обратная команде dumpdata. Для загрузки JSON файла в базу данных.
Подробно будем рассматривать эти командны на практике занятия по тестированию джанго.

flush

Команда необходимая для очистки базы данных, но не отмены миграций (Сохраняем структуру, теряем все данные)

sqlflush

Отпечатает какой SQL код будет выполнен при применении команды flush

inspectdb

Команда необходимая для проверки соответствия ваших моделей и вашей базы данных. Незаменимо при переносе проекта из вне на Django.

Миграции

makemigrations

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

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

>>> python manage.py makemigrations myapp --empty
Migrations for 'myapp':
  myapp/migrations/0004_auto_20211018_2255.py

# Generated by Django 3.2.7 on 2021-10-18 22:55  
  
from django.db import migrations  
  
  
class Migration(migrations.Migration):  
  
    dependencies = [  
        ('myapp', '0003_note'),  
    ]  
  
    operations = [  
    ]

Тут указано приложение для которого миграция будет применена, и прошлая миграция с которой текущая миграция будет
связана.

Зачем это вообще надо?

Мы можем в операции добавить любые интересующие нас действия, например выполнения кода на python

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

# Generated by Django 3.0.7 on 2020-10-29 11:59

from django.db import migrations


def some_forward_action(apps, schema_editor):
    Team = apps.get_model('storages', 'Team') # Приложение и модель
    names = ('B2B', 'CX', 'SFA')
    
    teams = []
	for name in names:
		 teams.append(Team(name=name))
    
    Team.objects.bulk_create(teams)
    

def some_backward_action(apps, schema_editor):
    pass


class Migration(migrations.Migration):
    dependencies = [
        ('storages', '0003_auto_20201029_1352'),
    ]

    operations = [
        migrations.RunPython(some_forward_action, some_backward_action)
    ]

Такие миграции называются Data Migrations

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

Для обратной миграции чаще всего действия не требуются (хоть и далеко не всегда), поэтому чаще всего обратная миграция не указывается вовсе или записывается в виде лямбда выражения lambda x, y: None

Также можно выполнять чистый SQL, используя класс RunSQL:

operations =  [
	migrations.RunSQL("INSERT INTO musician (name) VALUES ('Reinhardt');")
]

migrate

Уже известная вам команда для применения миграции

manage.py migrate [app_label] [migration_name]

Может быть указано приложение, к которому применяется, и имя миграции (на самом деле достаточно первых четырех цифр), указывание имени нужно для отката миграций, допустим у вас уже применена миграция номер 8, а вы поняли, что проблема была в миграции номер 6, это значит что можно откатить базу о миграции номер 5, естественно с потерей данных, и провести новые миграции, для этого нужно сделать:

manage.py migrate my_app 0002

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

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

sqlmigrate

Отпечатает какой SQL код будет выполнен при применении команды migrate

showmigrations

Также уже известная вам команда, которая отобразит список миграций, и их состояние (Применена или нет)

runserver

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

sendtestemail

Отправка тестового имейла (работает только если отправка писем была настроена) принимает два параметра, от кого и кому.

Например:

python manage.py sendtestemail myownemail@gmail.com myanotheremail@gmail.com

sqlsequencereset

Команда для сброса последовательностей базы данных, может принимать название приложения.

Если вы удалите все объекты из базы, и начнёте создавать новые, id будут продолжаться в не зависимости от того сколько
объектов было раньше, потому что id вычисляется из специальных объектов базы которые называются sequence.

Если из сбросить, то id будет назначаться снова с 1.

Не применять на базах с данными!!

squashmigrations

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

Например в приложении myapp миграции от 4-ой до 7-ой это добавления новых полей в одну и туже модель, чтобы сжать эти
миграции в одну нужно выполнить

python manage.py squashmigrations myapp 0004 0007

startapp

Команда для создания нового приложения.

startproject

Команда для создания нового проекта.

test

Команда для запуска тестов. Рассмотрим её на следующих занятиях.

Команды базовых приложений

django.contrib.auth

changepassword

Команда для смены пароля конкретному пользователю

manage.py changepassword ringo

createsuperuser

Команда для создания пользователя со всеми правами

django.contrib.sessions

clearsession

Команда для очистки базы данных от информации о сессиях. При базовых настройках вся информация о сессиях автоматически пишется в базу данных.

django.contrib.staticfiles

collectstatic

Команды для статики, вообще работу статики и медиа, рассмотрим на следующих занятиях

Написание своих скриптов

Документация
По факту все выше описанные команды написаны на python, а это значит, что мы можем написать свои команды.

Все команды должны храниться внутри папки вашего проекта appname/management/commands/, если у вас отсутствуют эти директории, их необходимо создать.

myapp/
    __init__.py
    models.py
    management/
        commands/
            welcome.py
    tests.py
    views.py

Название будет присвоено такое же, как и у файла в котором она записана. То-есть, создав модуль под названием welcome.py, чтобы в дальнейшем выполнить эту команду необходимо ввести:

python3 manage.py welcome

welcome.py

from django.core.management.base import BaseCommand

class Command(BaseCommand):
    help = "say hello"

    def handle(self, *args, **options):
	    self.stdout.write(self.style.SUCCESS('Welcome'))

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

В атрибут help мы помещаем текст, который будет описывать, что команда делает, и именно этот текст будет показан при выполнении команды python3 manage.py help <command>. Код команды должен быть определен внутри метода handle.

Использование аргументов

Команды принимают два типа аргументов: именованные и позиционные.

Именованные аргументы это аргументы с префиксом ‘-‘ или ‘–‘ и не имеет значения, в каком порядке они будут передаваться команде.

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

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

from django.core.management.base import BaseCommand


class Command(BaseCommand):

    def add_arguments(self, parser):
        # позиционный аргумент
        parser.add_argument('name', type=str)

        # именованный аргумент
        parser.add_argument('-l', '--lastname', type=str, help='Last Name')

    def handle(self, *args, **options):
        name = options['name']
        lastname = options['lastname']
        self.stdout.write(self.style.SUCCESS(f'Welcome {name} {lastname}'))

В приведенном выше примере аргумент может быть указан как -l или --lastname.

>>> python3 manage.py welcome Name --lastname Lastname

Welcome Name Lastname

Аргументы со значениями по умолчанию

Для упрощения работы хочется, чтобы простой передачи аргумента было достаточно, для понимания, что делать в дальнейшем.

Для того чтобы создать аргумент со значением по умолчанию, при вызове метода add_argument, нам нужно добавить параметр action.

Значение параметра action будет зависеть от типа и значения по умолчанию. Если вы хотите, чтобы аргумент был булевым, то-есть при его присутствии, он должен быть True или False, он должен иметь значение store_true или store_false соответственно:

parser.add_argument('--noreload', action='store_true')

Аргументы также могут быть постоянными или делать действительно интересные вещи. Если вы обратитесь к официальной документации argparse, вы можете знать все, что можно сделать с помощью параметра action. Это пример того, как мы можем хранить константу по умолчанию:

parser.add_argument('--foo', action='store_const', const=42)

Задания

  1. Снять фикстуру с вашей базы, изучить полученный файл.
  2. Для вашего модуля создать дата миграцию, которая будет создавать два новых товаров.
  3. Написать менеджмент команду, чтобы отклонить все заявки на возврат.

Литература (что почитать)

  1. Интернационализация и локализация
  2. Создание management commands в Django
  3. Шпаргалка по командам django-admin
  4. Создание менеджемент команды