前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Django 添加自定义命令

Django 添加自定义命令

作者头像
緣來
发布2020-01-02 15:09:39
1.1K0
发布2020-01-02 15:09:39
举报
文章被收录于专栏:緣來來來

使用Django开发,对 python manage.py *** 命令模式肯定不会陌生。比较常用的有 runservermigrate等!

有时候会有这样的需求,为 Django 执行一些定时任务,比如通知搜索引擎,例如百度,提交网站的一些地址给他们,则可以通过为 Djangomanage.py 添加自定义命令可以很容易的解决这个问题。

所以我们就来讲讲如何自定义扩展manage命令。

源码分析

manage.py 文件是通过 django-admin startproject project_name 生成的。

  1. manage.py的源码
    • 首先设置了 settings 文件
    • 其次执行了一个函数django.core.management.execute_from_command_line(sys.argv),这个函数传入了命令行参数 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)
  2. execute_from_command_line 里面调用了ManagementUtility类中的execute方法 def execute_from_command_line(argv=None): """ A simple method that runs a ManagementUtility. """ utility = ManagementUtility(argv) utility.execute() 在 execute 中主要是解析了传入的参数 sys.argv ,并且调用了get_command()
  3. 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 里遍历所有注册的 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('_')] 可以发现并注册的命令是commands目录下不以"_"开头的文件名。
  4. load_command_class 将命令文件***.py中的Command类加载进去。 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()
  5. CommandCommand 类要继承 BaseCommand 类,其中很多方法,一定要实现的是 handle 方法,handle 方法是命令实际执行的代码。

具体实现

根据上面说的原理,我们只需要在创建好的应用的根目录创建文件夹名为 management 的目录,然后继续在该目录创建 commands 的目录,并在两个目录中都要创建__init__.py 的 python 文件。 目录创建好之后继续在commands 的目录中添加 ping_baidu.py 文件,文件名将会是 manage.py 的命令名. 目录结构如下:

代码语言:javascript
复制
(python3) ➜  blog tree   
.
├── __init__.py
└── management
    ├── __init__.py
    └── commands
        ├── __init__.py 
        └── ping_baidu.py

ping_baidu.py 中实现命令的具体内容

代码语言:javascript
复制
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 也很简单:

代码语言:javascript
复制
from django.contrib.sitemaps import ping_google
import requests
from django.conf import settings


class SpiderNotify():
  //提交百度统计
    @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)
 //熊掌号接入
    @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)

至此,基本都完成了,可以终端执行./manage.py查看输出:

代码语言:javascript
复制
(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 命令已经出现了,./manage.py ping_baidu --help 可以查看帮助:

代码语言:javascript
复制
(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.

最后在终端执行: ./manage.py ping_baidu all 即可。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-10-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 源码分析
  • 具体实现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档