تغییرات اخیر

در اینجا اطلاعیه‌ها، نسخه‌ها و تغییرات جدید لیارا فهرست می‌شوند.

نحوه ایجاد دستورات مدیریتی جدید در Django


۲۴ تیر ۱۳۹۹

جنگو، ابزارها و دستورات خط فرمان گوناگون و زیادی را فراهم می‌کند که می‌توان آن‌ها را توسط django-admin.py و یا manage.py در هر پروژه، اجرا کرد. اما یک نکته مثبت این است که می‌توانید دستورات خود را نیز به آن‌ها اضافه کنید. این دستورات مدیریتی می‌تواند برای تعامل با برنامه‌تان از طریق خط فرمان، بسیار مناسب باشد، همچنین می‌تواند به عنوان واسطی برای اجرای corn jobها نیز ظاهر شود. در این مقاله آموزشی نحوه ایجاد دستورات خودتان در جنگو را خواهید آموخت.

در ادامه خواهید خواند:

  • آشنایی با دستورات خط فرمان در جنگو
  • مثال‌های پایه‌ای
  • مدیریت ورودی‌ها
  • استایل‌دهی
  • Cron Job
  • اطلاعات بیشتر
  • جمع بندی
نحوه ایجاد دستورات مدیریتی جدید در Django

آشنایی با دستورات خط فرمان در جنگو

قبل از اینکه شروع کنیم، بیایید لحظاتی را صرف آشنایی با رابط خط فرمان جنگو کنیم. به احتمال زیاد با دستوراتی نظیر startproject، runserver و یا collectstatic آشنا هستید. برای مشاهده تمام دستوراتی که می‌توانید در یک پروژه جنگو ایجاد کنید، از دستور زیر در پروژه‌تان (در واقع جایی که فایل manage.py قرار دارد) استفاده کنید:

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

می‌توانیم دستورات خود را ایجاد کنیم و آن‌ها را به این لیست اضافه کنیم. این کار را با ایجاد پوشه management/commands در پوشه برنامه، همانند مثال زیر انجام می‌دهیم:

mysite/                                   <-- project directory
 |-- core/                                <-- app directory
 |    |-- management/
 |    |    +-- commands/
 |    |         +-- my_custom_command.py  <-- module where command is going to live
 |    |-- migrations/
 |    |    +-- __init__.py
 |    |-- __init__.py
 |    |-- admin.py
 |    |-- apps.py
 |    |-- models.py
 |    |-- tests.py
 |    +-- views.py
 |-- mysite/
 |    |-- __init__.py
 |    |-- settings.py
 |    |-- urls.py
 |    |-- wsgi.py
 +-- manage.py

نام این فایل برای ایجاد دستور استفاده خواهد شد. برای مثال اگر اسم فایل my_custom_command.py باشد، از طریق دستور زیر می‌توانیم آن را اجرا کنیم:

python manage.py my_custom_command

بیایید سراغ مثال‌های بعدی برویم.

هاست اختصاصی برای اجرای حرفه‌ای اپلیکیشن‌های Django، بدون پیچیدگی‌های زیرساختی.
✅ پشتیبانی کامل از فریم‌ورک Django✅ دیپلوی آسان با Git یا CLI✅ منابع اختصاصی، امنیت و عملکرد پایدار
خرید و راه‌اندازی هاست جنگو

مثال‌های پایه‌ای

مثال زیر، نمونه‌ای از شیوه صحیح ایجاد یک دستور است:

management/commands/what_time_is_it.py

from django.core.management.base import BaseCommand
from django.utils import timezone

class Command(BaseCommand):
    help = 'Displays current time'

    def handle(self, *args, **kwargs):
        time = timezone.now().strftime('%X')
        self.stdout.write("It's now %s" % time)

اساسا دستورات مدیریتی جنگو با کلاسی به نام Command، که از BaseCommand ارث‌بری می‌کند، ساخته شده‌اند. به این نکته توجه کنید که دستور را باید در داخل متد handle() تعریف کنید.

در این مثال اسم فایل what_time_is_it.py است، در نتیجه برای استفاده از آن، از دستور زیر استفاده می‌کنیم:

python manage.py what_time_is_it

خروجی:

It's now 18:35:31

شاید از خودتان بپرسید که تفاوت این روش با استفاده از یک اسکریپت ساده پایتونی چیست؟ و یا مزایای این روش چیست؟ مزیت اصلی این است که جنگو به طور کامل لود شده است و آماده استفاده است، این یعنی می‌توانید مدل‌ها را وارد کنید و کوئری‌هایی که نیاز دارید بر دیتابیس اجرا کنید را توسط ORM جنگو، بر روی دیتابیس پیاده کنید. در ضمن این اجازه را به شما می‌دهد که با تمام منابع پروژه‌تان نیز کار کنید.

مدیریت ورودی‌ها

جنگو امکان استفاده از argparse (که جزو کتابخانه‌های استاندارد پایتون است) را فراهم می‌کند. برای مدیریت ورودی‌ها در دستورات‌مان، باید متدی با نام add_arguments ایجاد کنیم.

ورودی‌های اجباری

در مثال بعد دستوری را ایجاد می‌کنیم که به صورت رندوم کاربر می‌سازد (در واقع آبجکتی از کلاس User بوجود می‌آورد و آن را در دیتابیس ذخیره می‌کند). این دستور یک ورودی اجباری، به نام total می‌گیرد، که این ورودی مشخص کننده تعداد کاربرهایی است که می‌خواهیم ایجاد کنیم:

management/commands/create_users.py

from django.contrib.auth.models import User
from django.core.management.base import BaseCommand
from django.utils.crypto import get_random_string

class Command(BaseCommand):
    help = 'Create random users'

    def add_arguments(self, parser):
        parser.add_argument('total', type=int, help='Indicates the number of users to be created')

    def handle(self, *args, **kwargs):
        total = kwargs['total']
        for i in range(total):
            User.objects.create_user(username=get_random_string(), email='', password='123')

با روش زیر از این دستور استفاده می‌کنیم:

python manage.py create_users 10
هاست Python لیارا؛ اجرای سریع، امن و پایدار اپلیکیشن‌های پایتون شما
✅ پشتیبانی کامل از فریم‌ورک‌های محبوب✅ دیپلوی آسان و سریع با Git یا CLI✅ منابع اختصاصی و امنیت بالا
خرید و راه‌اندازی هاست پایتون

ورودی‌های اختیاری

ورودی‌های اختیاری و نام‌گذاری‌شده می‌توانند در هر ترتیبی به عنوان ورودی به دستور داده شوند. در مثال زیر یک ورودی با نام prefix تعریف می‌کنیم، که از آن به عنوان پیشوندی برای ایجاد نام‌کاربری استفاده خواهیم کرد:

management/commands/create_users.py

from django.contrib.auth.models import User
from django.core.management.base import BaseCommand
from django.utils.crypto import get_random_string

class Command(BaseCommand):
    help = 'Create random users'

    def add_arguments(self, parser):
        parser.add_argument('total', type=int, help='Indicates the number of users to be created')

        # Optional argument
        parser.add_argument('-p', '--prefix', type=str, help='Define a username prefix', )

    def handle(self, *args, **kwargs):
        total = kwargs['total']
        prefix = kwargs['prefix']

        for i in range(total):
            if prefix:
                username = '{prefix}_{random_string}'.format(prefix=prefix, random_string=get_random_string())
            else:
                username = get_random_string()
            User.objects.create_user(username=username, email='', password='123')

نحوه استفاده از این دستور:

python manage.py create_users 10 --prefix custom_user

و یا:

python manage.py create_users 10 -p custom_user

اگر prefix تعیین شده باشد، نام‌کاربری مثل custom_user_oYwoxtt4vNHR ایجاد خواهد شد، اما اگر prefix تعیین نشود، نام کاربری oYwoxtt4vNHR خواهد بود، یعنی مقدار رندومی که ایجاد شده است.

سوییچ‌ها

مدل دیگری از ورودی‌های اختیاری، سوییچ‌ها و یا flagها هستند، که برای کنترل و تعیین مقادیر صحیح و غلط استفاده می‌شود. در نظر بگیرید که می‌خواهیم سوییچ --admin را اضافه کنیم، تا بتوانیم به هنگام اجرای دستور، کاربری با دسترسی ادمین و یا کاربری با دسترسی معمولی، ایجاد کنیم:

management/commands/create_users.py

from django.contrib.auth.models import User
from django.core.management.base import BaseCommand
from django.utils.crypto import get_random_string

class Command(BaseCommand):
    help = 'Create random users'

    def add_arguments(self, parser):
        parser.add_argument('total', type=int, help='Indicates the number of users to be created')
        parser.add_argument('-p', '--prefix', type=str, help='Define a username prefix')
        parser.add_argument('-a', '--admin', action='store_true', help='Create an admin account')

    def handle(self, *args, **kwargs):
        total = kwargs['total']
        prefix = kwargs['prefix']
        admin = kwargs['admin']

        for i in range(total):
            if prefix:
                username = '{prefix}_{random_string}'.format(prefix=prefix, random_string=get_random_string())
            else:
                username = get_random_string()

            if admin:
                User.objects.create_superuser(username=username, email='', password='123')
            else:
                User.objects.create_user(username=username, email='', password='123')

نحوه استفاده:

python manage.py create_users 2 --admin

و یا:

python manage.py create_users 2 -a
جنگو (Django) چیست؟
جنگو Django

لیستی دلخواه از ورودی‌ها

بیایید دستور جدیدی با نام delete_users ایجاد کنیم. در این دستور می‌توانیم لیستی از id کاربران را وارد کنیم، در نتیجه این دستور باید این کاربران را از دیتابیس حذف کند:

management/commands/delete_users.py

from django.contrib.auth.models import User
from django.core.management.base import BaseCommand

class Command(BaseCommand):
    help = 'Delete users'

    def add_arguments(self, parser):
        parser.add_argument('user_id', nargs='+', type=int, help='User ID')

    def handle(self, *args, **kwargs):
        users_ids = kwargs['user_id']

        for user_id in users_ids:
            try:
                user = User.objects.get(pk=user_id)
                user.delete()
                self.stdout.write('User "%s (%s)" deleted with success!' % (user.username, user_id))
            except User.DoesNotExist:
                self.stdout.write('User with id "%s" does not exist.' % user_id)

نحوه استفاده:

python manage.py delete_users 1

خروجی:

User "SMl5ISqAsIS8 (1)" deleted with success!

همچنین می‌توانیم لیستی از اعداد که با فاصله از هم جدا شده‌اند را نیز به این دستور بدهیم تا کاربران با این idهای مشخص، توسط تنها یک دستور از دیتابیس حذف شوند:

python manage.py delete_users 1 2 3 4

خروجی:

User with id "1" does not exist.
User "9teHR4Y7Bz4q (2)" deleted with success!
User "ABdSgmBtfO2t (3)" deleted with success!
User "BsDxOO8Uxgvo (4)" deleted with success!

استایل‌دهی

می‌توانیم مثال‌های قبلی را با دادن رنگ مناسب به خروجی، بهبود ببخشیم:

management/commands/delete_users.py

from django.contrib.auth.models import User
from django.core.management.base import BaseCommand

class Command(BaseCommand):
    help = 'Delete users'

    def add_arguments(self, parser):
        parser.add_argument('user_id', nargs='+', type=int, help='User ID')

    def handle(self, *args, **kwargs):
        users_ids = kwargs['user_id']

        for user_id in users_ids:
            try:
                user = User.objects.get(pk=user_id)
                user.delete()
                self.stdout.write(self.style.SUCCESS('User "%s (%s)" deleted with success!' % (user.username, user_id)))
            except User.DoesNotExist:
                self.stdout.write(self.style.WARNING('User with id "%s" does not exist.' % user_id))

نحوه استفاده همانند مثال‌های قبل است، اما تفاوت تنها در خروجی است:

python manage.py delete_users 3 4 5 6

خروجی:

خروجی دستور python manage.py delete_users

لیست زیر تمامی استایل‌های موجود برای دستورات مدیریتی را نشان می‌دهد:

from django.core.management.base import BaseCommand

class Command(BaseCommand):
    help = 'Show all available styles'

    def handle(self, *args, **kwargs):
        self.stdout.write(self.style.ERROR('error - A major error.'))
        self.stdout.write(self.style.NOTICE('notice - A minor error.'))
        self.stdout.write(self.style.SUCCESS('success - A success.'))
        self.stdout.write(self.style.WARNING('warning - A warning.'))
        self.stdout.write(self.style.SQL_FIELD('sql_field - The name of a model field in SQL.'))
        self.stdout.write(self.style.SQL_COLTYPE('sql_coltype - The type of a model field in SQL.'))
        self.stdout.write(self.style.SQL_KEYWORD('sql_keyword - An SQL keyword.'))
        self.stdout.write(self.style.SQL_TABLE('sql_table - The name of a model in SQL.'))
        self.stdout.write(self.style.HTTP_INFO('http_info - A 1XX HTTP Informational server response.'))
        self.stdout.write(self.style.HTTP_SUCCESS('http_success - A 2XX HTTP Success server response.'))
        self.stdout.write(self.style.HTTP_NOT_MODIFIED('http_not_modified - A 304 HTTP Not Modified server response.'))
        self.stdout.write(self.style.HTTP_REDIRECT('http_redirect - A 3XX HTTP Redirect server response other than 304.'))
        self.stdout.write(self.style.HTTP_NOT_FOUND('http_not_found - A 404 HTTP Not Found server response.'))
        self.stdout.write(self.style.HTTP_BAD_REQUEST('http_bad_request - A 4XX HTTP Bad Request server response other than 404.'))
        self.stdout.write(self.style.HTTP_SERVER_ERROR('http_server_error - A 5XX HTTP Server Error response.'))
        self.stdout.write(self.style.MIGRATE_HEADING('migrate_heading - A heading in a migrations management command.'))
        self.stdout.write(self.style.MIGRATE_LABEL('migrate_label - A migration name.'))
خروجی تمامی استایل‌های موجود برای دستورات جنگو

Cron Job

اگر تسکی (task) داشته باشید که بخواهید آن را به صورت دوره‌ای اجرا کنید، مثل ایجاد گزارش در هر دوشنبه و یا یک scrapper دارید که می‌خواهید برخی از داده‌ها را از چند صفحه وب مشخص، در هر ۱۰ دقیقه، ذخیره کند. می‌توانید این کد را به عنوان یک دستور مدیریتی ایجاد کنید و آن را به فایل crontab سرورتان اضافه کنید:

# m h  dom mon dow   command
0 4 * * * /home/mysite/venv/bin/python /home/mysite/mysite/manage.py my_custom_command

مثال بالا، دستور my_custom_command را هر روز ساعت ۴ صبح، اجرا می‌کند.

اطلاعات بیشتر

مثال‌هایی که در این مقاله بررسی کردیم، برای شروع کار با دستورات سفارشی جنگو کافی هستند. اما اگر قصد دارید دستورات پیچیده‌تری بنویسید—مانند دریافت ورودی‌های مختلف، بررسی اعتبار آن‌ها یا تعریف رفتارهای شرطی—نیاز دارید که با ماژول قدرتمند argparse که جنگو از آن برای مدیریت آرگومان‌ها و گزینه‌های خط فرمان استفاده می‌کند، بیشتر آشنا شوید.

به کمک argparse می‌توانید پارامترهای اختیاری و اجباری تعریف کنید، پیام‌های راهنما اضافه کنید، مقدار پیش‌فرض تنظیم نمایید و انواع مختلفی از ورودی‌ها را مدیریت کنید. این امکانات باعث می‌شوند دستورات شما انعطاف‌پذیرتر، کاربرپسندتر و قابل استفاده در سناریوهای گسترده‌تری باشند.

همچنین توصیه می‌شود مستندات رسمی جنگو را در زمینه custom management commands مطالعه کنید. این مستندات حاوی مثال‌های دقیق‌تری هستند و نکات مهمی مثل ساختار پروژه، ارث‌بری صحیح از کلاس‌های BaseCommand یا AppCommand و روش‌های برخورد با استثناها را توضیح می‌دهند.

در نهایت، با تمرین بیشتر و استفاده از مستندات، می‌توانید دستورات سفارشی حرفه‌ای‌تری بنویسید که به‌خوبی با سایر اجزای پروژه‌ی جنگوی شما هماهنگ باشند.

۸ پکیجی که باید در پروژه‌های جنگو استفاده کنید
۸ پکیج در پروژه‌های جنگو

جمع بندی

با استفاده از قابلیت ایجاد دستورات سفارشی در جنگو، می‌توان فرآیندهای تکراری یا پیچیده را به شکل ساده‌تری اجرا کرد و آن‌ها را به بخشی از ابزارهای خط فرمان پروژه تبدیل کرد. این ویژگی نه‌تنها توسعه و نگهداری پروژه را تسهیل می‌کند، بلکه امکان خودکارسازی بسیاری از کارهای رایج را نیز فراهم می‌سازد. با درک ساختار مدیریت دستورات و نحوه پیاده‌سازی آن‌ها، می‌توانید ابزارهایی دقیق و کاربردی برای پروژه خود بسازید و کنترل بیشتری روی محیط توسعه و اجرای برنامه داشته باشید.

منبع:https://simpleisbetterthancomplex.com/tutorial/2018/08/27/how-to-create-custom-django-management-commands.html

به اشتراک بگذارید