1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2026-02-04 16:01:55 +00:00
community.general/plugins/modules/django_manage.py
Felix Fontein 236b9c0e04
Sort imports with ruff check --fix (#11400)
Sort imports with ruff check --fix.
2026-01-09 07:40:58 +01:00

369 lines
13 KiB
Python

#!/usr/bin/python
# Copyright (c) 2022, Alexei Znamensky <russoz@gmail.com>
# Copyright (c) 2013, Scott Anderson <scottanderson42@gmail.com>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
DOCUMENTATION = r"""
module: django_manage
short_description: Manages a Django application
description:
- Manages a Django application using the C(manage.py) application frontend to C(django-admin). With the O(virtualenv) parameter,
all management commands are executed by the given C(virtualenv) installation.
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: none
diff_mode:
support: none
options:
command:
description:
- The name of the Django management command to run. The commands listed below are built in this module and have some
basic parameter validation.
- V(collectstatic) - Collects the static files into C(STATIC_ROOT).
- V(createcachetable) - Creates the cache tables for use with the database cache backend.
- V(flush) - Removes all data from the database.
- V(loaddata) - Searches for and loads the contents of the named O(fixtures) into the database.
- V(migrate) - Synchronizes the database state with models and migrations.
- V(test) - Runs tests for all installed apps.
- Custom commands can be entered, but they fail unless they are known to Django. Custom commands that may prompt for
user input should be run with the C(--noinput) flag.
- Support for the values V(cleanup), V(syncdb), V(validate) was removed in community.general 9.0.0. See note about supported
versions of Django.
type: str
required: true
project_path:
description:
- The path to the root of the Django application where C(manage.py) lives.
type: path
required: true
aliases: [app_path, chdir]
settings:
description:
- The Python path to the application's settings module, such as V(myapp.settings).
type: path
pythonpath:
description:
- A directory to add to the Python path. Typically used to include the settings module if it is located external to
the application directory.
- This would be equivalent to adding O(pythonpath)'s value to the E(PYTHONPATH) environment variable.
type: path
aliases: [python_path]
virtualenv:
description:
- An optional path to a C(virtualenv) installation to use while running the manage application.
- The virtual environment must exist, otherwise the module fails.
type: path
aliases: [virtual_env]
apps:
description:
- A list of space-delimited apps to target. Used by the V(test) command.
type: str
cache_table:
description:
- The name of the table used for database-backed caching. Used by the V(createcachetable) command.
type: str
clear:
description:
- Clear the existing files before trying to copy or link the original file.
- Used only with the V(collectstatic) command. The C(--noinput) argument is added automatically.
default: false
type: bool
database:
description:
- The database to target. Used by the V(createcachetable), V(flush), V(loaddata), V(syncdb), and V(migrate) commands.
type: str
failfast:
description:
- Fail the command immediately if a test fails. Used by the V(test) command.
default: false
type: bool
aliases: [fail_fast]
fixtures:
description:
- A space-delimited list of fixture file names to load in the database. B(Required) by the V(loaddata) command.
type: str
skip:
description:
- Skips over out-of-order missing migrations, you can only use this parameter with V(migrate) command.
type: bool
merge:
description:
- Runs out-of-order or missing migrations as they are not rollback migrations, you can only use this parameter with
V(migrate) command.
type: bool
link:
description:
- Creates links to the files instead of copying them, you can only use this parameter with V(collectstatic) command.
type: bool
testrunner:
description:
- Controls the test runner class that is used to execute tests.
- This parameter is passed as-is to C(manage.py).
type: str
aliases: [test_runner]
notes:
- 'B(ATTENTION): Support for Django releases older than 4.1 has been removed in community.general version 9.0.0. While the
module allows for free-form commands, not verifying the version of Django being used, it is B(strongly recommended) to
use a more recent version of the framework.'
- Please notice that Django 4.1 requires Python 3.8 or greater.
- This module does not create a virtualenv if the O(virtualenv) parameter is specified and a virtual environment does not
already exist at the given location. This behavior changed in community.general version 9.0.0.
- The recommended way to create a virtual environment in Ansible is by using M(ansible.builtin.pip).
- This module assumes English error messages for the V(createcachetable) command to detect table existence, unfortunately.
- To be able to use the V(collectstatic) command, you must have enabled C(staticfiles) in your settings.
- Your C(manage.py) application must be executable (C(rwxr-xr-x)), and must have a valid shebang, for example C(#!/usr/bin/env
python), for invoking the appropriate Python interpreter.
seealso:
- name: django-admin and manage.py Reference
description: Reference for C(django-admin) or C(manage.py) commands.
link: https://docs.djangoproject.com/en/4.1/ref/django-admin/
- name: Django Download page
description: The page showing how to get Django and the timeline of supported releases.
link: https://www.djangoproject.com/download/
- name: What Python version can I use with Django?
description: From the Django FAQ, the response to Python requirements for the framework.
link: https://docs.djangoproject.com/en/dev/faq/install/#what-python-version-can-i-use-with-django
requirements: ["django >= 4.1"]
author:
- Alexei Znamensky (@russoz)
- Scott Anderson (@tastychutney)
"""
EXAMPLES = r"""
- name: Run cleanup on the application installed in django_dir
community.general.django_manage:
command: clearsessions
project_path: "{{ django_dir }}"
- name: Load the initial_data fixture into the application
community.general.django_manage:
command: loaddata
project_path: "{{ django_dir }}"
fixtures: "{{ initial_data }}"
- name: Run syncdb on the application
community.general.django_manage:
command: migrate
project_path: "{{ django_dir }}"
settings: "{{ settings_app_name }}"
pythonpath: "{{ settings_dir }}"
virtualenv: "{{ virtualenv_dir }}"
- name: Run the SmokeTest test case from the main app. Useful for testing deploys
community.general.django_manage:
command: test
project_path: "{{ django_dir }}"
apps: main.SmokeTest
- name: Create an initial superuser
community.general.django_manage:
command: "createsuperuser --noinput --username=admin --email=admin@example.com"
project_path: "{{ django_dir }}"
"""
import os
import shlex
import sys
from ansible.module_utils.basic import AnsibleModule
def _fail(module, cmd, out, err, **kwargs):
msg = ""
if out:
msg += f"stdout: {out}"
if err:
msg += f"\n:stderr: {err}"
module.fail_json(cmd=cmd, msg=msg, **kwargs)
def _ensure_virtualenv(module):
venv_param = module.params["virtualenv"]
if venv_param is None:
return
vbin = os.path.join(venv_param, "bin")
activate = os.path.join(vbin, "activate")
if not os.path.exists(activate):
module.fail_json(msg=f"{venv_param} does not point to a valid virtual environment")
os.environ["PATH"] = f"{vbin}:{os.environ['PATH']}"
os.environ["VIRTUAL_ENV"] = venv_param
def createcachetable_check_changed(output):
return "already exists" not in output
def flush_filter_output(line):
return "Installed" in line and "Installed 0 object" not in line
def loaddata_filter_output(line):
return "Installed" in line and "Installed 0 object" not in line
def migrate_filter_output(line):
return (
("Migrating forwards " in line)
or ("Installed" in line and "Installed 0 object" not in line)
or ("Applying" in line)
)
def collectstatic_filter_output(line):
return line and "0 static files" not in line
def main():
command_allowed_param_map = dict(
createcachetable=(
"cache_table",
"database",
),
flush=("database",),
loaddata=(
"database",
"fixtures",
),
test=(
"failfast",
"testrunner",
"apps",
),
migrate=(
"apps",
"skip",
"merge",
"database",
),
collectstatic=(
"clear",
"link",
),
)
command_required_param_map = dict(
loaddata=("fixtures",),
)
# forces --noinput on every command that needs it
noinput_commands = (
"flush",
"migrate",
"test",
"collectstatic",
)
# These params are allowed for certain commands only
specific_params = ("apps", "clear", "database", "failfast", "fixtures", "testrunner")
# These params are automatically added to the command if present
general_params = (
"settings",
"pythonpath",
"database",
)
specific_boolean_params = ("clear", "failfast", "skip", "merge", "link")
end_of_command_params = ("apps", "cache_table", "fixtures")
module = AnsibleModule(
argument_spec=dict(
command=dict(required=True, type="str"),
project_path=dict(required=True, type="path", aliases=["app_path", "chdir"]),
settings=dict(type="path"),
pythonpath=dict(type="path", aliases=["python_path"]),
virtualenv=dict(type="path", aliases=["virtual_env"]),
apps=dict(),
cache_table=dict(type="str"),
clear=dict(default=False, type="bool"),
database=dict(type="str"),
failfast=dict(default=False, type="bool", aliases=["fail_fast"]),
fixtures=dict(type="str"),
testrunner=dict(type="str", aliases=["test_runner"]),
skip=dict(type="bool"),
merge=dict(type="bool"),
link=dict(type="bool"),
),
)
command_split = shlex.split(module.params["command"])
command_bin = command_split[0]
project_path = module.params["project_path"]
virtualenv = module.params["virtualenv"]
for param in specific_params:
value = module.params[param]
if value and param not in command_allowed_param_map[command_bin]:
module.fail_json(msg=f"{param} param is incompatible with command={command_bin}")
for param in command_required_param_map.get(command_bin, ()):
if not module.params[param]:
module.fail_json(msg=f"{param} param is required for command={command_bin}")
_ensure_virtualenv(module)
run_cmd_args = ["./manage.py"] + command_split
if command_bin in noinput_commands and "--noinput" not in command_split:
run_cmd_args.append("--noinput")
for param in general_params:
if module.params[param]:
run_cmd_args.append(f"--{param}={module.params[param]}")
for param in specific_boolean_params:
if module.params[param]:
run_cmd_args.append(f"--{param}")
# these params always get tacked on the end of the command
for param in end_of_command_params:
if module.params[param]:
if param in ("fixtures", "apps"):
run_cmd_args.extend(shlex.split(module.params[param]))
else:
run_cmd_args.append(module.params[param])
rc, out, err = module.run_command(run_cmd_args, cwd=project_path)
if rc != 0:
if command_bin == "createcachetable" and "table" in err and "already exists" in err:
out = "already exists."
else:
if "Unknown command:" in err:
_fail(module, run_cmd_args, err, f"Unknown django command: {command_bin}")
_fail(module, run_cmd_args, out, err, path=os.environ["PATH"], syspath=sys.path)
changed = False
lines = out.split("\n")
filt = globals().get(f"{command_bin}_filter_output", None)
if filt:
filtered_output = list(filter(filt, lines))
if len(filtered_output):
changed = True
check_changed = globals().get(f"{command_bin}_check_changed", None)
if check_changed:
changed = check_changed(out)
module.exit_json(
changed=changed,
out=out,
cmd=run_cmd_args,
app_path=project_path,
project_path=project_path,
virtualenv=virtualenv,
settings=module.params["settings"],
pythonpath=module.params["pythonpath"],
)
if __name__ == "__main__":
main()