zoukankan      html  css  js  c++  java
  • 如何在Django中创建自定义命令?

    在开发Django项目时,有时候需要编写一次性脚本来自动化特定任务。
    例如:
    1、清理错误的数据列
    2、导入初始数据库数据
    我们可以通过两种方式在django中运行这些类型的命令。第一是编写一个普通的python脚本,然后可以通过运行python file_name.py来调用它,而另一个方法是使用django-admin命令。这些是通过调用python manage.py command_name运行的。

    对于这篇文章,我将通过一个博客应用程序进行演示,该应用程序只有3个数据库表:User,Category和Post。
    普通的python脚本方法
    对于第一个示例,我们将尝试使用以下脚本列出所有系统用户:

    from django.contrib.auth import get_user_model
    
    User = get_user_model()
    
    # retrieve all users
    users = User.objects.all()
    
    # loop through all users
    for user in users:
        print(f'user is {user.get_full_name()} and their username is {user.get_username()}')

    可以命名脚本list_users.py并通过python list_users.py运行它,但运行的时候会遇到如下错误:

    django.core.exceptions.ImproperlyConfigured: Requested setting AUTH_USER_MODEL, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings

    可能有人会假设,如果脚本位于django的项目目录中,脚本可以正常运行。然而,这种情况也不对。这是因为脚本不知道该脚本将应用于哪个项目。因为我们可能在一台计算机或虚拟环境中有多个项目。因此,给脚本一些上下文信息很重要。

    我们将通过稍微修改脚本来做到这一点。

    import os
    
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'projectname.settings')
    
    import django
    django.setup()
    
    from django.contrib.auth import get_user_model
    
    User = get_user_model()
    
    users = User.objects.all()
    
    for user in users:
        print(f'user is {user.get_full_name()} and their username is {user.get_username()}')

    在这里,我们指定项目的设置,不仅如此,还可以调用django.setup()方法。该方法配置设置,记录日志并填充应用程序注册表。总之,我们要使脚本知道我们的项目上下文。

    请注意,导入顺序很重要,不要调整,这里有坑。

    如果再次运行脚本,则所有用户都会正常打印到终端,没有报错了。

    接下来,我们将通过运行django-admin startapp posts来创建一个名为posts的app应用。

    该应用程序将包含我们的博客文章模型

    from django.db import models
    from django.contrib.auth import get_user_model
    
    User = get_user_model()
    
    # Create your models here.
    class CommonInfo(models.Model):
        created_at = models.DateTimeField(auto_now_add=True)
        updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
            abstract = True
            ordering = ('-created_at',)
    
    # blog post category
    class Category(CommonInfo):
        name = models.CharField(max_length=255)
    
    def __str__(self):
    return self.name
    
    # blog post instance
    class Post(CommonInfo):
        title = models.CharField(max_length=255)
        category = models.ForeignKey(Category,related_name='posts',on_delete=models.PROTECT)
        author = models.ForeignKey(User,related_name='posts',on_delete=models.PROTECT)
        content = models.TextField()
        published = models.BooleanField(default=False)
    
    def __str__(self):
    return f'{self.title} by {self.author.get_full_name()}'

    对于此示例,我们将从命令行创建博客帖子的实例。脚本名为create_post.py

    import os
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'commands.settings')
    
    import django
    django.setup()
    
    from django.contrib.auth import get_user_model
    from posts.models import Category, Post
    
    User = get_user_model()
    
    def select_category():
    # retrieve categories. (You can create some examples from the django admin)
        categories = Category.objects.all().order_by('created_at')
        print('Please select a category for your post: ')
    for category in categories:
            print(f'{category.id}: {category}')
        category_id = input()
        category = Category.objects.get(id=category_id)
    return category
    
    
    def select_author():
    # retrieve all users
        users = User.objects.all()
        print('Please select an author for your post: ')
    for user in users:
            print(f'{user.id}: {user}')
        user_id = input()
        user = User.objects.get(id=user_id)
    return user
    
    
    
    def create_post():
        title = input("Title of your post: ")
        content = input("Long post content: ")
        category = select_category()
        author = select_author()
        Post(**locals()).save()
        print('Post created successfully!')
    
    if __name__ == "__main__":
        create_post()

    在这里,我们正在创建博客帖子的实例。请注意我们要如何处理ForeignKey关系?其实就是确保将相关数据库表的对象实例分配给该字段。

    通过运行python create_post.py,然后提示我们进行一些输入。

    编写自定义django管理命令方法
    文章刚看开始也提到了,django-admin命令是通过运行python manage.py command_name来执行的,我们平时用的有runserver,migrate和collectstatic。如果要获取可用命令的列表,可以运行python manage.py help。这将显示可用命令以及它们所在的django app文件夹的列表。

    要注册自定义管理命令,需要在django应用程序文件夹中添加一个management commands目录。在我们的例子中,它将位于posts management commands中。

    设置完成后,我们便可以在命令文件夹中初始化自定义脚本。对于第一个示例,我们将编写一个命令,将之前创建的博客文章标记为已发布。

    请创建一个文件并将其命名为publish_post.py

    from django.core.management.base import BaseCommand, CommandError
    from posts.models import Category, Post
    
    class Command(BaseCommand):
        help = 'Marks the specified blog post as published.'
    
    # allows for command line args
    def add_arguments(self, parser):
            parser.add_argument('post_id', type=int)
    
    def handle(self, *args, **options):
    try:
                post = Post.objects.get(id=options['post_id'])
    except Post.DoesNotExist:
    raise CommandError(f'Post with id {options["post_id"]} does not exist')
    if post.published:
                self.stdout.write(self.style.ERROR(f'Post: {post.title} was already published'))
    else:
                post.published = True
                post.save()
                self.stdout.write(self.style.SUCCESS(f'Post: {post.title} successfully published'))

    Django管理命令由一个名为Command的类组成,该类继承自BaseCommand。

    为了接收参数,该类利用argparse。方法add_arguments允许我们的函数接收参数。

    在我们的例子中,该函数期望一个参数,该参数将被分配键post_id

    然后,handle()函数评估输入并执行我们的逻辑。

    在上面的示例中,期望的参数类型称为位置参数,必须提供该参数才能运行该函数。为此,我们运行python manage.py publish_post 1(或任何发布主键)

    顾名思义,可以将另一种类型的参数称为可选参数应用于方法,缺少这些参数不会影响函数的执行。

    下面提供了一个示例。我们将初始化一个文件并将其命名为edit_post.py。代码如下:

    from django.core.management.base import BaseCommand, CommandError
    from posts.models import Category, Post
    
    class Command(BaseCommand):
        help = 'Edits the specified blog post.'
    
    def add_arguments(self, parser):
            parser.add_argument('post_id', type=int)
    
    # optional arguments
            parser.add_argument('-t', '--title',type=str, help='Indicate new name of the blog post.')
            parser.add_argument('-c', '--content',type=str, help='Indicate new blog post content.')
    
    def handle(self, *args, **options):
            title = options['title']
            content = options['content']
    try:
                post = Post.objects.get(id=options['post_id'])
    except Post.DoesNotExist:
    raise CommandError(f'Post with id {options["post_id"]} does not exist')
    
    if title or content:
    if title:
                    old_title = post.title
                    post.title = title
                    post.save()
                    self.stdout.write(self.style.SUCCESS(f'Post: {old_title} has been update with a new title, {post.title}'))
    if content:
                    post.content = content
                    post.save()
                    self.stdout.write(self.style.SUCCESS('Post: has been update with new text content.'))
    else:
                self.stdout.write(self.style.NOTICE('Post content remains the same as no arguments were given.'))

    在这里,我们只是在编辑博客文章标题或内容。所以我们可以运行python manage.py edit_post 2 -t“ new title”仅编辑标题

    或python manage.py edit_post -c “new content ”仅编辑内容。如果我们希望通过`python manage.py edit_post 2 -t “new title again” -c “new title again”编辑标题和内容,则可以提供两个参数。

  • 相关阅读:
    Linux驱动之内核自带的S3C2440的LCD驱动分析
    【双11狂欢的背后】微服务注册中心如何承载大型系统的千万级访问? Eureka 注册中心
    java GC算法 垃圾收集器
    Java嵌入式数据库H2学习总结(三)——在Web应用中嵌入H2数据库
    Java嵌入式数据库H2学习总结(二)——在Web应用程序中使用H2数据库
    Java嵌入式数据库H2学习总结(一)——H2数据库入门
    Java Master-Worker模式实现
    RSA公钥格式PKCS#1,PKCS#8互转(微信获取RSA加密公钥)
    自定义注解完成数据库切库(读写分离)
    Linux下CPU飘高定位
  • 原文地址:https://www.cnblogs.com/kcxg/p/14734589.html
Copyright © 2011-2022 走看看