#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)
Завдання
- Зняти фікстуру з вашої бази, вивчити отриманий файл.
- Для вашого модуля створити дата міграцію, яка буде створювати два нових товари.
- Написати менеджмент команду, щоб відхилити всі заявки на повернення.