Name | cs-djutils JSON |
Version |
20250219
JSON |
| download |
home_page | None |
Summary | My collection of things for working with Django. |
upload_time | 2025-02-19 09:11:33 |
maintainer | None |
docs_url | None |
author | None |
requires_python | None |
license | None |
keywords |
python3
|
VCS |
 |
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
My collection of things for working with Django.
*Latest release 20250219*:
* model_batches_qs: accept a nonmapping for exclude= or filter= eg a Q() function.
* model_batches_qs: new optional only= parameter.
Presently this provides:
* `BaseCommand`: a drop in replacement for `django.core.management.base.BaseCommand`
which uses a `cs.cmdutils.BaseCommand` style of implementation
* `model_batches_qs`: a generator yielding `QuerySet`s for batches of a `Model`
## <a name="BaseCommand"></a>Class `BaseCommand(cs.cmdutils.BaseCommand, django.core.management.base.BaseCommand)`
A drop in class for `django.core.management.base.BaseCommand`
which subclasses `cs.cmdutils.BaseCommand`.
This lets me write management commands more easily, particularly
if there are subcommands.
This is a drop in in the sense that you still make a management command
in nearly the same way:
from cs.djutils import BaseCommand
class Command(BaseCommand):
and `manage.py` will find it and run it as normal.
But from that point on the style is as for `cs.cmdutils.BaseCommand`:
- no `argparse` setup
- direct support for subcommands as methods
- succinct option parsing, if you want additional command line options
- usage text in the subcommand method docstring
A simple command looks like this:
class Command(BaseCommand):
def main(self, argv):
""" Usage: {cmd} .......
Do the main thing.
"""
... do stuff based on the CLI args `argv` ...
A command with subcommands looks like this:
class Command(BaseCommand):
def cmd_this(self, argv):
""" Usage: {cmd} ......
Do this.
"""
... do the "this" subcommand ...
def cmd_that(self, argv):
""" Usage: {cmd} ......
Do that.
"""
... do the "that" subcommand ...
If want some kind of app/client specific "overcommand" composed
from other management commands you can import them and make
them subcommands of the overcommand:
from .other_command import Command as OtherCommand
class Command(BaseCommand):
# provide it as the "other" subcommand
cmd_other = OtherCommand
Option parsing is inline in the command. `self` comes
presupplied with a `.options` attribute which is an instance
of `cs.cmdutils.BaseCommandOptions` (or some subclass).
Parsing options is light weight and automatically updates the usage text.
This example adds command line switches to the default switches:
- `-x`: a Boolean, setting `self.options.x`
- `--thing-limit` *n*: an `int`, setting `self.options.thing_limit=`*n*
- `--mode` *blah*: a string, setting `self.options.mode=`*blah*
Code sketch:
from cs.cmdutils import popopts
class Command(BaseCommand):
@popopts(
x=None,
thing_limit_=int,
mode_='The run mode.',
)
def cmd_this(self, argv):
""" Usage: {cmd}
Do this thing.
"""
options = self.options
... now consult options.x or whatever
... argv is now the remaining arguments after the options
*`BaseCommand.Options`*
*`BaseCommand.SubCommandClass`*
*`BaseCommand.add_arguments(self, parser)`*:
Add the `Options.COMMON_OPT_SPECS` to the `argparse` parser.
This is basicly to support the Django `call_command` function.
*`BaseCommand.handle(*, argv, **options)`*:
The Django `BaseComand.handle` method.
This creates another instance for `argv` and runs it.
*`BaseCommand.run_from_argv(argv)`*:
Intercept `django.core.management.base.BaseCommand.run_from_argv`.
Construct an instance of `cs.djutils.DjangoBaseCommand` and run it.
## <a name="DjangoSpecificSubCommand"></a>Class `DjangoSpecificSubCommand(cs.cmdutils.SubCommand)`
A subclass of `cs.cmdutils.SubCOmmand` with additional support
for Django's `BaseCommand`.
*`DjangoSpecificSubCommand.__call__(self, argv: List[str])`*:
Run this `SubCommand` with `argv`.
This calls Django's `BaseCommand.run_from_argv` for pure Django commands.
*`DjangoSpecificSubCommand.is_pure_django_command`*:
Whether this subcommand is a pure Django `BaseCommand`.
*`DjangoSpecificSubCommand.usage_text(self, *, cmd=None, **kw)`*:
Return the usage text for this subcommand.
## <a name="model_batches_qs"></a>`model_batches_qs(model: django.db.models.base.Model, field_name='pk', *, chunk_size=1024, desc=False, exclude=None, filter=None, only=None) -> Iterable[django.db.models.query.QuerySet]`
A generator yielding `QuerySet`s which produce nonoverlapping
batches of `Model` instances.
Efficient behaviour requires the field to be indexed.
Correct behaviour requires the field values to be unique.
See `model_instances` for an iterable of instances wrapper
of this function, where you have no need to further amend the
`QuerySet`s or to be aware of the batches.
Parameters:
* `model`: the `Model` to query
* `field_name`: default `'pk'`, the name of the field on which
to order the batches
* `chunk_size`: the maximum size of each chunk
* `desc`: default `False`; if true then order the batches in
descending order instead of ascending order
* `exclude`: optional mapping of Django query terms to exclude by
* `filter`: optional mapping of Django query terms to filter by
* `only`: optional sequence of field names for a Django query `.only()`
Example iteration of a `Model` would look like:
from itertools import chain
from cs.djutils import model_batches_qs
for instance in chain.from_iterable(model_batches_qs(MyModel)):
... work with instance ...
By returning `QuerySet`s it is possible to further alter each query:
from cs.djutils import model_batches_qs
for batch_qs in model_batches_qs(MyModel):
for result in batch_qs.filter(
some_field__gt=10
).select_related(.......):
... work with each result in the batch ...
or:
from itertools import chain
from cs.djutils import model_batches_qs
for result in chain.from_iterable(
batch_qs.filter(
some_field__gt=10
).select_related(.......)
for batch_qs in model_batches_qs(MyModel)
):
... work with each result ...
## <a name="model_instances"></a>`model_instances(model: django.db.models.base.Model, field_name='pk', only=None, **mbqs_kw) -> Iterable[django.db.models.base.Model]`
A generator yielding Model instances.
This is a wrapper for `model_batches_qs` and accepts the same arguments.
Efficient behaviour requires the field to be indexed.
Correct behaviour requires the field values to be unique.
# Release Log
*Release 20250219*:
* model_batches_qs: accept a nonmapping for exclude= or filter= eg a Q() function.
* model_batches_qs: new optional only= parameter.
*Release 20250213*:
New model_instances() wrapper for model_batches_qs() returning an iterable of Model instances.
*Release 20250113.2*:
model_batches_qs: bugfix second and following QuerySet construction.
*Release 20250113.1*:
model_batches_qs: new exclude=dict and filter=dict optional parameters to filter before the slice.
*Release 20250113*:
model_batches_qs: improve the query which measures the current batch.
*Release 20250111.1*:
Documentation update.
*Release 20250111*:
New model_batches_qs() generator yielding QuerySets for batches of a Model.
*Release 20241222.3*:
Autocall settings.configure() if required because Django's settings object is a royal PITA.
*Release 20241222.2*:
BaseCommand.Options.settings: call settings.configure() on init if that has not already been done.
*Release 20241222.1*:
Placate the dataclass - upgrade BaseCommand.Options.settings to be a field() with a default_factory.
*Release 20241222*:
BaseCommand.Options: include .settings with the public django.conf.settings names, mostly for cmd_info and cmd_repl.
*Release 20241119*:
New DjangoSpecificSubCommand(CSBaseCommand.SubCommandClass) to include support for pure Django BaseCommands.
*Release 20241111*:
Rename DjangoBaseCommand to just BaseCommand so that we go `from cs.djutils import BaseCommand`. Less confusing.
*Release 20241110*:
Initial PyPI release with DjangoBaseCommand, cs.cmdutils.BaseCommand subclass suppplanting django.core.management.base.BaseCommand.
Raw data
{
"_id": null,
"home_page": null,
"name": "cs-djutils",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": "python3",
"author": null,
"author_email": "Cameron Simpson <cs@cskk.id.au>",
"download_url": "https://files.pythonhosted.org/packages/29/b5/7d9298d55dcee2d58554141f2b4ca6a2b6dfef84bc1ab644d9dcbfbfbcb4/cs_djutils-20250219.tar.gz",
"platform": null,
"description": "My collection of things for working with Django.\n\n*Latest release 20250219*:\n* model_batches_qs: accept a nonmapping for exclude= or filter= eg a Q() function.\n* model_batches_qs: new optional only= parameter.\n\nPresently this provides:\n* `BaseCommand`: a drop in replacement for `django.core.management.base.BaseCommand`\n which uses a `cs.cmdutils.BaseCommand` style of implementation\n* `model_batches_qs`: a generator yielding `QuerySet`s for batches of a `Model`\n\n## <a name=\"BaseCommand\"></a>Class `BaseCommand(cs.cmdutils.BaseCommand, django.core.management.base.BaseCommand)`\n\nA drop in class for `django.core.management.base.BaseCommand`\nwhich subclasses `cs.cmdutils.BaseCommand`.\n\nThis lets me write management commands more easily, particularly\nif there are subcommands.\n\nThis is a drop in in the sense that you still make a management command\nin nearly the same way:\n\n from cs.djutils import BaseCommand\n\n class Command(BaseCommand):\n\nand `manage.py` will find it and run it as normal.\nBut from that point on the style is as for `cs.cmdutils.BaseCommand`:\n- no `argparse` setup\n- direct support for subcommands as methods\n- succinct option parsing, if you want additional command line options\n- usage text in the subcommand method docstring\n\nA simple command looks like this:\n\n class Command(BaseCommand):\n\n def main(self, argv):\n \"\"\" Usage: {cmd} .......\n Do the main thing.\n \"\"\"\n ... do stuff based on the CLI args `argv` ...\n\nA command with subcommands looks like this:\n\n class Command(BaseCommand):\n\n def cmd_this(self, argv):\n \"\"\" Usage: {cmd} ......\n Do this.\n \"\"\"\n ... do the \"this\" subcommand ...\n\n def cmd_that(self, argv):\n \"\"\" Usage: {cmd} ......\n Do that.\n \"\"\"\n ... do the \"that\" subcommand ...\n\nIf want some kind of app/client specific \"overcommand\" composed\nfrom other management commands you can import them and make\nthem subcommands of the overcommand:\n\n from .other_command import Command as OtherCommand\n\n class Command(BaseCommand):\n\n # provide it as the \"other\" subcommand\n cmd_other = OtherCommand\n\nOption parsing is inline in the command. `self` comes\npresupplied with a `.options` attribute which is an instance\nof `cs.cmdutils.BaseCommandOptions` (or some subclass).\n\nParsing options is light weight and automatically updates the usage text.\nThis example adds command line switches to the default switches:\n- `-x`: a Boolean, setting `self.options.x`\n- `--thing-limit` *n*: an `int`, setting `self.options.thing_limit=`*n*\n- `--mode` *blah*: a string, setting `self.options.mode=`*blah*\n\nCode sketch:\n\n from cs.cmdutils import popopts\n\n class Command(BaseCommand):\n\n @popopts(\n x=None,\n thing_limit_=int,\n mode_='The run mode.',\n )\n def cmd_this(self, argv):\n \"\"\" Usage: {cmd}\n Do this thing.\n \"\"\"\n options = self.options\n ... now consult options.x or whatever\n ... argv is now the remaining arguments after the options\n\n*`BaseCommand.Options`*\n\n*`BaseCommand.SubCommandClass`*\n\n*`BaseCommand.add_arguments(self, parser)`*:\nAdd the `Options.COMMON_OPT_SPECS` to the `argparse` parser.\nThis is basicly to support the Django `call_command` function.\n\n*`BaseCommand.handle(*, argv, **options)`*:\nThe Django `BaseComand.handle` method.\nThis creates another instance for `argv` and runs it.\n\n*`BaseCommand.run_from_argv(argv)`*:\nIntercept `django.core.management.base.BaseCommand.run_from_argv`.\nConstruct an instance of `cs.djutils.DjangoBaseCommand` and run it.\n\n## <a name=\"DjangoSpecificSubCommand\"></a>Class `DjangoSpecificSubCommand(cs.cmdutils.SubCommand)`\n\nA subclass of `cs.cmdutils.SubCOmmand` with additional support\nfor Django's `BaseCommand`.\n\n*`DjangoSpecificSubCommand.__call__(self, argv: List[str])`*:\nRun this `SubCommand` with `argv`.\nThis calls Django's `BaseCommand.run_from_argv` for pure Django commands.\n\n*`DjangoSpecificSubCommand.is_pure_django_command`*:\nWhether this subcommand is a pure Django `BaseCommand`.\n\n*`DjangoSpecificSubCommand.usage_text(self, *, cmd=None, **kw)`*:\nReturn the usage text for this subcommand.\n\n## <a name=\"model_batches_qs\"></a>`model_batches_qs(model: django.db.models.base.Model, field_name='pk', *, chunk_size=1024, desc=False, exclude=None, filter=None, only=None) -> Iterable[django.db.models.query.QuerySet]`\n\nA generator yielding `QuerySet`s which produce nonoverlapping\nbatches of `Model` instances.\n\nEfficient behaviour requires the field to be indexed.\nCorrect behaviour requires the field values to be unique.\n\nSee `model_instances` for an iterable of instances wrapper\nof this function, where you have no need to further amend the\n`QuerySet`s or to be aware of the batches.\n\nParameters:\n* `model`: the `Model` to query\n* `field_name`: default `'pk'`, the name of the field on which\n to order the batches\n* `chunk_size`: the maximum size of each chunk\n* `desc`: default `False`; if true then order the batches in\n descending order instead of ascending order\n* `exclude`: optional mapping of Django query terms to exclude by\n* `filter`: optional mapping of Django query terms to filter by\n* `only`: optional sequence of field names for a Django query `.only()`\n\nExample iteration of a `Model` would look like:\n\n from itertools import chain\n from cs.djutils import model_batches_qs\n for instance in chain.from_iterable(model_batches_qs(MyModel)):\n ... work with instance ...\n\nBy returning `QuerySet`s it is possible to further alter each query:\n\n from cs.djutils import model_batches_qs\n for batch_qs in model_batches_qs(MyModel):\n for result in batch_qs.filter(\n some_field__gt=10\n ).select_related(.......):\n ... work with each result in the batch ...\n\nor:\n\n from itertools import chain\n from cs.djutils import model_batches_qs\n for result in chain.from_iterable(\n batch_qs.filter(\n some_field__gt=10\n ).select_related(.......)\n for batch_qs in model_batches_qs(MyModel)\n ):\n ... work with each result ...\n\n## <a name=\"model_instances\"></a>`model_instances(model: django.db.models.base.Model, field_name='pk', only=None, **mbqs_kw) -> Iterable[django.db.models.base.Model]`\n\nA generator yielding Model instances.\nThis is a wrapper for `model_batches_qs` and accepts the same arguments.\n\nEfficient behaviour requires the field to be indexed.\nCorrect behaviour requires the field values to be unique.\n\n# Release Log\n\n\n\n*Release 20250219*:\n* model_batches_qs: accept a nonmapping for exclude= or filter= eg a Q() function.\n* model_batches_qs: new optional only= parameter.\n\n*Release 20250213*:\nNew model_instances() wrapper for model_batches_qs() returning an iterable of Model instances.\n\n*Release 20250113.2*:\nmodel_batches_qs: bugfix second and following QuerySet construction.\n\n*Release 20250113.1*:\nmodel_batches_qs: new exclude=dict and filter=dict optional parameters to filter before the slice.\n\n*Release 20250113*:\nmodel_batches_qs: improve the query which measures the current batch.\n\n*Release 20250111.1*:\nDocumentation update.\n\n*Release 20250111*:\nNew model_batches_qs() generator yielding QuerySets for batches of a Model.\n\n*Release 20241222.3*:\nAutocall settings.configure() if required because Django's settings object is a royal PITA.\n\n*Release 20241222.2*:\nBaseCommand.Options.settings: call settings.configure() on init if that has not already been done.\n\n*Release 20241222.1*:\nPlacate the dataclass - upgrade BaseCommand.Options.settings to be a field() with a default_factory.\n\n*Release 20241222*:\nBaseCommand.Options: include .settings with the public django.conf.settings names, mostly for cmd_info and cmd_repl.\n\n*Release 20241119*:\nNew DjangoSpecificSubCommand(CSBaseCommand.SubCommandClass) to include support for pure Django BaseCommands.\n\n*Release 20241111*:\nRename DjangoBaseCommand to just BaseCommand so that we go `from cs.djutils import BaseCommand`. Less confusing.\n\n*Release 20241110*:\nInitial PyPI release with DjangoBaseCommand, cs.cmdutils.BaseCommand subclass suppplanting django.core.management.base.BaseCommand.\n",
"bugtrack_url": null,
"license": null,
"summary": "My collection of things for working with Django.",
"version": "20250219",
"project_urls": {
"MonoRepo Commits": "https://bitbucket.org/cameron_simpson/css/commits/branch/main",
"Monorepo Git Mirror": "https://github.com/cameron-simpson/css",
"Monorepo Hg/Mercurial Mirror": "https://hg.sr.ht/~cameron-simpson/css",
"Source": "https://github.com/cameron-simpson/css/blob/main/lib/python/cs/djutils.py"
},
"split_keywords": [
"python3"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "42db82d64d65c1a265257bd4f8fc55e1508d560804205c9964e0298a1059e745",
"md5": "aa6f69df33a012579bb50f248628f070",
"sha256": "80579a660344238773645d706af2c1794ec49fd81df1c560ae6b4e7c7b14e53d"
},
"downloads": -1,
"filename": "cs_djutils-20250219-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "aa6f69df33a012579bb50f248628f070",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": null,
"size": 7653,
"upload_time": "2025-02-19T09:11:30",
"upload_time_iso_8601": "2025-02-19T09:11:30.899380Z",
"url": "https://files.pythonhosted.org/packages/42/db/82d64d65c1a265257bd4f8fc55e1508d560804205c9964e0298a1059e745/cs_djutils-20250219-py2.py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "29b57d9298d55dcee2d58554141f2b4ca6a2b6dfef84bc1ab644d9dcbfbfbcb4",
"md5": "71039463e55ee87ac97cfc03f7bbd4f5",
"sha256": "5ebc8c84183de576ab142c220e5b264204c3c02fe34a73c99e09e6ca96f7210f"
},
"downloads": -1,
"filename": "cs_djutils-20250219.tar.gz",
"has_sig": false,
"md5_digest": "71039463e55ee87ac97cfc03f7bbd4f5",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 6120,
"upload_time": "2025-02-19T09:11:33",
"upload_time_iso_8601": "2025-02-19T09:11:33.087199Z",
"url": "https://files.pythonhosted.org/packages/29/b5/7d9298d55dcee2d58554141f2b4ca6a2b6dfef84bc1ab644d9dcbfbfbcb4/cs_djutils-20250219.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-02-19 09:11:33",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "cameron-simpson",
"github_project": "css",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "cs-djutils"
}