使用Django開(kāi)發(fā)偏塞,對(duì) python manage.py ***
命令模式肯定不會(huì)陌生施逾。比較常用的有 runserver
,migrate
等呈野!
有時(shí)候會(huì)有這樣的需求低矮,為 Django
執(zhí)行一些定時(shí)任務(wù),比如通知搜索引擎被冒,例如百度军掂,提交網(wǎng)站的一些地址給他們,則可以通過(guò)為 Django
的 manage.py
添加自定義命令可以很容易的解決這個(gè)問(wèn)題昨悼。
所以我們就來(lái)講講如何自定義擴(kuò)展manage命令蝗锥。
源碼分析
manage.py
文件是通過(guò) django-admin startproject project_name
生成的。
-
manage.py的源碼
首先設(shè)置了
settings
文件-
其次執(zhí)行了一個(gè)函數(shù)
django.core.management.execute_from_command_line(sys.argv)
率触,這個(gè)函數(shù)傳入了命令行參數(shù)sys.argv
#!/usr/bin/env python import os import sys if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CIServer.settings") try: from django.core.management import execute_from_command_line except ImportError: raise ImportError( "Couldn't import Django. Are you sure it's installed and available " "on your PATH environment variable? Did you forget to activate a " "virtual environment?" ) execute_from_command_line(sys.argv)
-
execute_from_command_line
里面調(diào)用了ManagementUtility類中的execute方法
def execute_from_command_line(argv=None): """ A simple method that runs a ManagementUtility. """ utility = ManagementUtility(argv) utility.execute()
在
execute
中主要是解析了傳入的參數(shù)sys.argv
终议,并且調(diào)用了get_command()
-
get_command
def get_commands(): """ Returns a dictionary mapping command names to their callback applications. This works by looking for a management.commands package in django.core, and in each installed application -- if a commands package exists, all commands in that package are registered. Core commands are always included. If a settings module has been specified, user-defined commands will also be included. The dictionary is in the format {command_name: app_name}. Key-value pairs from this dictionary can then be used in calls to load_command_class(app_name, command_name) If a specific version of a command must be loaded (e.g., with the startapp command), the instantiated module can be placed in the dictionary in place of the application name. The dictionary is cached on the first call and reused on subsequent calls. """ commands = {name: 'django.core' for name in find_commands(upath(__path__[0]))} if not settings.configured: return commands for app_config in reversed(list(apps.get_app_configs())): path = os.path.join(app_config.path, 'management') commands.update({name: app_config.name for name in find_commands(path)}) return commands
get_command 里遍歷所有注冊(cè)的
INSTALLED_APPS
路徑下的management
尋找 (find_commands) 用戶自定義的命令。def find_commands(management_dir): """ Given a path to a management directory, returns a list of all the command names that are available. Returns an empty list if no commands are defined. """ command_dir = os.path.join(management_dir, 'commands') # Workaround for a Python 3.2 bug with pkgutil.iter_modules sys.path_importer_cache.pop(command_dir, None) return [name for _, name, is_pkg in pkgutil.iter_modules([npath(command_dir)]) if not is_pkg and not name.startswith('_')]
可以發(fā)現(xiàn)并注冊(cè)的命令是commands目錄下不以"_"開(kāi)頭的文件名。
-
load_command_class
將命令文件***.py中的Command類加載進(jìn)去穴张。
def load_command_class(app_name, name): """ Given a command name and an application name, returns the Command class instance. All errors raised by the import process (ImportError, AttributeError) are allowed to propagate. """ module = import_module('%s.management.commands.%s' % (app_name, name)) return module.Command()
-
Command
類Command
類要繼承BaseCommand
類细燎,其中很多方法,一定要實(shí)現(xiàn)的是handle
方法皂甘,handle
方法是命令實(shí)際執(zhí)行的代碼玻驻。
具體實(shí)現(xiàn)
根據(jù)上面說(shuō)的原理,我們只需要在創(chuàng)建好的應(yīng)用的根目錄創(chuàng)建文件夾名為 management
的目錄偿枕,然后繼續(xù)在該目錄創(chuàng)建 commands
的目錄璧瞬,并在兩個(gè)目錄中都要?jiǎng)?chuàng)建__init__.py
的 python 文件。 目錄創(chuàng)建好之后繼續(xù)在commands
的目錄中添加 ping_baidu.py
文件益老,文件名將會(huì)是 manage.py
的命令名. 目錄結(jié)構(gòu)如下:
(python3) ? blog tree
.
├── __init__.py
└── management
├── __init__.py
└── commands
├── __init__.py
└── ping_baidu.py
在 ping_baidu.py
中實(shí)現(xiàn)命令的具體內(nèi)容
from django.core.management.base import BaseCommand, CommandError
from blog.models import Article, Tag, Category
from DjangoBlog.spider_notify import sipder_notify
from django.contrib.sites.models import Site
site = Site.objects.get_current().domain
class Command(BaseCommand):
help = 'notify baidu url'
def add_arguments(self, parser):
parser.add_argument('data_type', type=str, choices=['all', 'article', 'tag', 'category'],
help='article : all article,tag : all tag,category: all category,all: All of these')
def get_full_url(self, path):
url = "https://{site}{path}".format(site=site, path=path)
return url
def handle(self, *args, **options):
type = options['data_type']
self.stdout.write('start get %s' % type)
notify = sipder_notify()
urls = []
if type == 'article' or type == 'all':
for article in Article.objects.filter(status='p'):
urls.append(article.get_full_url())
if type == 'tag' or type == 'all':
for tag in Tag.objects.all():
url = tag.get_absolute_url()
urls.append(self.get_full_url(url))
if type == 'category' or type == 'all':
for category in Category.objects.all():
url = category.get_absolute_url()
urls.append(self.get_full_url(url))
self.stdout.write(self.style.SUCCESS('start notify %d urls' % len(urls)))
notify.baidu_notify(urls)
self.stdout.write(self.style.SUCCESS('finish notify'))
sipder_notify.py
也很簡(jiǎn)單:
from django.contrib.sitemaps import ping_google
import requests
from django.conf import settings
class SpiderNotify():
//提交百度統(tǒng)計(jì)
@staticmethod
def baidu_notify(urls):
try:
data = '\n'.join(urls)
result = requests.post(settings.BAIDU_NOTIFY_URL, data=data)
print(result.text)
except Exception as e:
print(e)
//熊掌號(hào)接入
@staticmethod
def baidu_bear_notify(urls):
try:
data = '\n'.join(urls)
result = requests.post(settings.BAIDU_BEAR_NOTIFY_URL, data=data)
print(result.text)
except Exception as e:
print(e)
//提交到谷歌
@staticmethod
def __google_notify():
try:
ping_google('/sitemap.xml')
except Exception as e:
print(e)
@staticmethod
def notify(url):
SpiderNotify.baidu_notify(url)
SpiderNotify.__google_notify()
SpiderNotify.baidu_bear_notify(url)
至此彪蓬,基本都完成了,可以終端執(zhí)行./manage.py查看輸出:
(python3) ? DjangoBlog ./manage.py
Type 'manage.py help <subcommand>' for help on a specific subcommand.
Available subcommands:
[auth]
changepassword
createsuperuser
[blog]
ping_baidu
可以看到 ping_baidu
命令已經(jīng)出現(xiàn)了捺萌,./manage.py ping_baidu --help
可以查看幫助:
(python3) ? DjangoBlog ./manage.py ping_baidu --help
usage: manage.py ping_baidu [-h] [--version] [-v {0,1,2,3}]
[--settings SETTINGS] [--pythonpath PYTHONPATH]
[--traceback] [--no-color]
{all,article,tag,category}
notify baidu url
positional arguments:
{all,article,tag,category}
article : all article,tag : all tag,category: all
category,all: All of these
optional arguments:
-h, --help show this help message and exit
--version show program's version number and exit
-v {0,1,2,3}, --verbosity {0,1,2,3}
Verbosity level; 0=minimal output, 1=normal output,
2=verbose output, 3=very verbose output
--settings SETTINGS The Python path to a settings module, e.g.
"myproject.settings.main". If this isn't provided, the
DJANGO_SETTINGS_MODULE environment variable will be
used.
--pythonpath PYTHONPATH
A directory to add to the Python path, e.g.
"/home/djangoprojects/myproject".
--traceback Raise on CommandError exceptions
--no-color Don't colorize the command output.
最后在終端執(zhí)行: ./manage.py ping_baidu all
即可档冬。
此文章同時(shí)同步到我的個(gè)人博客緣來(lái)來(lái)來(lái) ? Django 添加自定義命令](https://www.fkomm.cn/article/2018/10/17/55.html)