cs-djutils


Namecs-djutils JSON
Version 20250113.2 PyPI version JSON
download
home_pageNone
SummaryMy collection of things for working with Django.
upload_time2025-01-13 06:33:28
maintainerNone
docs_urlNone
authorNone
requires_pythonNone
licenseNone
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 20250113.2*:
model_batches_qs: bugfix second and following QuerySet construction.

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, field_name='pk', *, chunk_size=1024, desc=False, exclude=None, filter=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.

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

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 ...

# Release Log



*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/17/94/0aab280c655ed81026bee279e5dcaf8829af70ae9443dbff52b0e2e74d19/cs_djutils-20250113.2.tar.gz",
    "platform": null,
    "description": "My collection of things for working with Django.\n\n*Latest release 20250113.2*:\nmodel_batches_qs: bugfix second and following QuerySet construction.\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, field_name='pk', *, chunk_size=1024, desc=False, exclude=None, filter=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\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\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# Release Log\n\n\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": "20250113.2",
    "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": "",
            "digests": {
                "blake2b_256": "824c49356ad0539bc6000bfea353a8c21a9a389ea174ee929ade475978372271",
                "md5": "6f9e4e0518bbd4203676f0da9e32dbe6",
                "sha256": "5b4ec75b3a325aa709f05e71f49675ef430562d85281dd87b6e347b51510d327"
            },
            "downloads": -1,
            "filename": "cs_djutils-20250113.2-py2.py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "6f9e4e0518bbd4203676f0da9e32dbe6",
            "packagetype": "bdist_wheel",
            "python_version": "py2.py3",
            "requires_python": null,
            "size": 7171,
            "upload_time": "2025-01-13T06:33:25",
            "upload_time_iso_8601": "2025-01-13T06:33:25.983128Z",
            "url": "https://files.pythonhosted.org/packages/82/4c/49356ad0539bc6000bfea353a8c21a9a389ea174ee929ade475978372271/cs_djutils-20250113.2-py2.py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "17940aab280c655ed81026bee279e5dcaf8829af70ae9443dbff52b0e2e74d19",
                "md5": "26eadc2c5e89e5f42eb07a3aec40c5a5",
                "sha256": "5cd1860482c4f2e7acb5090a3a150ca97bea8ece7861234191b5820dc0c19b16"
            },
            "downloads": -1,
            "filename": "cs_djutils-20250113.2.tar.gz",
            "has_sig": false,
            "md5_digest": "26eadc2c5e89e5f42eb07a3aec40c5a5",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 5715,
            "upload_time": "2025-01-13T06:33:28",
            "upload_time_iso_8601": "2025-01-13T06:33:28.695168Z",
            "url": "https://files.pythonhosted.org/packages/17/94/0aab280c655ed81026bee279e5dcaf8829af70ae9443dbff52b0e2e74d19/cs_djutils-20250113.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-01-13 06:33:28",
    "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"
}
        
Elapsed time: 0.69299s