File: runscript.rst

package info (click to toggle)
python-django-extensions 4.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,820 kB
  • sloc: python: 18,601; javascript: 7,354; makefile: 108; xml: 17
file content (184 lines) | stat: -rw-r--r-- 5,967 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
RunScript
=============

:synopsis: Runs a script in the Django context.


Introduction
------------

The runscript command lets you run an arbitrary set of python commands within
the Django context. It offers the same usability and functionality as running a
set of commands in shell accessed by::

  $ python manage.py shell


Getting Started
---------------

This example assumes you have followed the tutorial for Django 1.8+, and
created a polls app containing a ``Question`` model. We will create a script
that deletes all of the questions from the database.

To get started create a scripts directory in your project root, next to
manage.py::

  $ mkdir scripts
  $ touch scripts/__init__.py

Note: The *__init__.py* file is necessary so that the folder is picked up as a
python package.

Next, create a python file with the name of the script you want to run within
the scripts directory::

  $ touch scripts/delete_all_questions.py

This file must implement a *run()* function. This is what gets called when you
run the script. You can import any models or other parts of your django project
to use in these scripts.

For example::

  # scripts/delete_all_questions.py

  from polls.models import Question

  def run():
      # Fetch all questions
      questions = Question.objects.all()
      # Delete questions
      questions.delete()

Note: You can put a script inside a *scripts* folder in any of your apps too.

Usage
-----

To run any script you use the command *runscript* with the name of the script
that you want to run.

For example::

  $ python manage.py runscript delete_all_questions

Note: The command first checks for scripts in your apps i.e. *app_name/scripts*
folder and runs them before checking for and running scripts in the
*project_root/scripts* folder. You can have multiple scripts with the same name
and they will all be run sequentially.

Passing arguments
-----------------

You can pass arguments from the command line to your script by passing a space separated
list of values with ``--script-args``. For example::

  $ python manage.py runscript delete_all_questions --script-args staleonly

The list of argument values gets passed as arguments to your *run()* function. For
example::

  # scripts/delete_all_questions.py
  from datetime import timedelta

  from django.utils import timezone

  from polls.models import Question

  def run(*args):
      # Get all questions
      questions = Question.objects.all()
      if 'staleonly' in args:
          # Only get questions more than 100 days old
          questions = questions.filter(pub_date__lt=timezone.now() - timedelta(days=100))
      # Delete questions
      questions.delete()

Setting execution directory
---------------------------

You can set scripts execution directory using ``--chdir`` option or ``settings.RUNSCRIPT_CHDIR``.
You can also set scripts execution directory policy using ``--dir-policy`` option or ``settings.RUNSCRIPT_CHDIR_POLICY``.

It can be one of the following:

* **none** - start all scripts in current directory.
* **each** - start all scripts in their directories.
* **root** - start all scripts in ``BASE_DIR`` directory.

Assume this simplified directory structure::

    django_project_dir/
    ├-first_app/
    │ └-scripts/
    │   ├-first_script.py
    ├-second_app/
    │ └-scripts/
    │   ├-second_script.py
    ├-manage.py
    ├-other_folder/
    │ └-some_file.py

Assume you are in ``other_folder`` directory.
You can set execution directory for both scripts using this command::

  $ python ../manage.py runscript first_script second_script --chdir /django_project_dir/second_app
  # scripts will be executed from second_app directory

You can run both scripts with ``NONE`` policy using this command::

  $ python ../manage.py runscript first_script second_script --dir-policy none
    # scripts will be executed from other_folder directory

You can run both scripts with ``EACH`` policy using this command::

  $ python ../manage.py runscript first_script second_script --dir-policy each
    # first_script will be executed from first_app and second script will be executed from second_app

You can run both scripts with ``ROOT`` policy using this command::

  $ python ../manage.py runscript first_script second_script --dir-policy root
    # scripts will be executed from django_project_dir directory

Errors and exit codes
---------------------

If an exception is encountered the execution of the scripts will stop, a traceback is shown and the
command will return an exit code.

To control the exit-code you can either use `CommandError("something went terribly wrong", returncode=123)`
in your script or has the `run(...)` function return the exit_code. Where any exit code other then 0 will
indicate failure, just like regular shell commands.

This means you can use `runscript` in your CI/CD pipelines or other automated scripts and it should
behave like any other shell command.

Continue on errors
------------------

If you want runscript to continue running scripts even if errors occurs you can set `-c`::

  $ python manage.py runscript delete_all_questions another_script --continue-on-error

This will continue running 'another_script' even if an exception was raised or exit code was returned
in 'delete_all_questions'.

When all the scripts has been run `runscript` will exit with the last non-zero exit code.

Note: It is possible to do `raise CommandError(..., returncode=0)` which will lead to an exception with exit code 0.


Debugging
---------

If an exception occurs you will get a traceback by default.
You can use `CommandError` in the same way as with other custom management commands.

To get a traceback from a `CommandError` specify ``--traceback``. For example::

  $ python manage.py runscript delete_all_questions --traceback

If you do not want to see tracebacks at all you can specify::

  $ python manage.py runscript delete_all_questions --no-traceback