#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.

Ми вже використовували деякі команди, але давайте подивимося докладніше.

Доступні команди

допомога

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

>>> 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
    optimizemigration
    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' # мова за замовчуванням, можна поставити uk, і навіть адмінка стане українською
  
USE_I18N = True # i18n - скорочення від 'internationalization'
  
USE_L10N = True # L10n - від 'localization' 

makemessages

python manage.py makemessages

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

Переклади потраплять до папки locale додатка, якщо її немає - її необхідно створити!
Або прописати у settings.py шлях, де вони будуть створені.

LOCALE_PATHS = (  
    BASE_DIR / "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=uk
>
processing locale uk

# 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: 2024-02-24 19:45+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 % 1 == 0 && n % 10 == 1 && n % 100 != "  
"11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % "  
"100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || "  
"(n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n"  
  
#: core/forms.py:9 core/forms.py:23  
msgid "Username"  
msgstr ""  
  
#: core/forms.py:10 core/forms.py:24  
msgid "Password"  
msgstr ""  
  
#: core/forms.py:19  
msgid "User does not exist"  
msgstr ""  
  
#: core/forms.py:25  
msgid "Repeat password"  
msgstr ""  
  
#: core/forms.py:31  
msgid "This username is already in use"  
msgstr ""  
  
#: core/forms.py:39  
msgid "Password are not similar"  
msgstr ""

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

compilemessages

Компілює файли для перекладів.
Робить із .po (Portable Object) файлів .mo (Machine Object) файли. Django приймає саме .mo як файли звідки брати переклад.
Підтримує вказівку локалі та ігнор, докладніше в документації)

>>> python manage.py compilemessages

processing file django.po in /Users/artem/PycharmProjects/ALevel/todolist/locale/uk/LC_MESSAGES

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 від оригіналу.

Fixtures

dumpdata

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

Фікстури - це заздалегідь підготовлені дані. Здебільшого, це файли відображення бази даних у формат JSON.

Команда dumpdata витягне всі дані з бази даних, і перетворить все у формат JSON.

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

loaddata

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

flush

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

sqlflush

Віддрукує який SQL код буде виконано при застосуванні команди flush

inspectdb

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

Migrations

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') # application and model
    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. Writing Custom Management Commands in Django