如何获取Django项目自身应用列表,排除pip安装的第三方应用?
当然有办法做到!下面我给你介绍几种实用的方案,从简单易维护到自动检测的都有,你可以根据自己的项目情况选择:
这是最清晰、最容易维护的做法,从一开始就把应用分类管理。你可以在settings.py里把应用分成三类:Django内置应用、第三方pip安装的应用,以及你自己项目的应用,然后合并成INSTALLED_APPS。
比如:
# settings.py # Django自带的核心应用 DJANGO_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] # 通过pip安装的第三方应用 THIRD_PARTY_APPS = [ 'rest_framework', 'django_filters', 'crispy_forms', ] # 你自己开发的项目专属应用 PROJECT_APPS = [ 'blog', 'accounts', 'shop', ] # 合并成最终的INSTALLED_APPS INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + PROJECT_APPS
之后你想要获取项目自身的应用列表时,直接调用settings.PROJECT_APPS就可以了,完全不用额外处理,而且后续维护应用时分类清晰,不容易出错。
如果你的项目已经成型,不想改动现有的INSTALLED_APPS结构,可以通过检查应用的文件路径来区分——你自己写的应用肯定在项目目录下,而pip安装的应用会在Python的site-packages目录里。
你可以写一个小函数来实现:
import os import importlib from django.conf import settings def get_project_local_apps(): # 获取项目根目录(settings.BASE_DIR一般就是你的项目根路径) project_root = settings.BASE_DIR local_apps = [] for app_name in settings.INSTALLED_APPS: try: # 导入应用模块 app_module = importlib.import_module(app_name) # 获取应用模块的绝对路径 app_path = os.path.abspath(app_module.__file__) # 判断应用路径是否包含项目根目录(说明是本地开发的) if os.path.commonprefix([project_root, app_path]) == project_root: local_apps.append(app_name) except ImportError: # 跳过导入失败的应用(理论上INSTALLED_APPS里的都能正常导入) continue return local_apps
调用这个函数就能得到所有项目自身的应用列表了。需要注意的是,如果你的应用结构比较特殊(比如把本地应用放在某个子目录里),可能需要稍微调整project_root的取值,确保路径判断准确。
还有一种思路是对比INSTALLED_APPS和pip已安装的包列表,但这种方法容易出现误判,比如如果你的本地应用和某个第三方包同名,就会出错。不过还是给你参考一下:
import pkg_resources from django.conf import settings def get_project_apps_via_pip(): # 获取所有通过pip安装的包的名称 installed_packages = {pkg.key for pkg in pkg_resources.working_set} local_apps = [] for app_name in settings.INSTALLED_APPS: # 取应用的根包名,比如"django.contrib.admin"的根包是"django" root_package = app_name.split('.')[0] # 如果根包不在已安装列表里,说明是本地应用 if root_package not in installed_packages: local_apps.append(app_name) return local_apps
这种方法的局限性比较大,比如Django内置应用的根包是django,而django本身是pip安装的,所以这个方法会把Django内置应用也排除掉,但如果你只想要自己的应用,可能需要再额外过滤掉django.contrib开头的应用,比较麻烦,所以不太推荐。
总的来说,方案一是最优解,既能清晰管理应用,又能直接获取目标列表;如果没法改动settings,就用方案二的路径检测方法,可靠性更高。
内容的提问来源于stack exchange,提问作者Alexander Nestorov

