社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Django

如何在Django中创建自定义命令?

马哥Linux运维 • 4 年前 • 540 次点击  

在开发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 usersusers = User.objects.all()
# loop through all usersfor 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 djangodjango.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 modelsfrom 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 categoryclass Category(CommonInfo): name = models.CharField(max_length=255)
def __str__(self):return self.name
# blog post instanceclass 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 osos.environ.setdefault('DJANGO_SETTINGS_MODULE', 'commands.settings')
import djangodjango.setup()
from django.contrib.auth import get_user_modelfrom 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, CommandErrorfrom posts.models import Category, Post
class Command(BaseCommand): help = 'Marks the specified blog post as published.'
# allows for command line argsdef 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, CommandErrorfrom 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”编辑标题和内容,则可以提供两个参数。
文章转载:Python运维技术

(版权归原作者所有,侵删)

点击下方“阅读原文”查看更多

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/113786
 
540 次点击