Skip to content

Commit

Permalink
Merge pull request #103 from bckohan/v2.x.x
Browse files Browse the repository at this point in the history
V2.2.0
  • Loading branch information
bckohan committed Jul 27, 2024
2 parents 40b7d48 + 28cb58e commit 4642ee2
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 73 deletions.
2 changes: 1 addition & 1 deletion django_typer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
model_parser_completer, # noqa: F401
)

VERSION = (2, 1, 3)
VERSION = (2, 2, 0)

__title__ = "Django Typer"
__version__ = ".".join(str(i) for i in VERSION)
Expand Down
37 changes: 29 additions & 8 deletions django_typer/completers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

# pylint: disable=line-too-long

import inspect
import os
import pkgutil
import sys
Expand All @@ -38,12 +39,15 @@
FloatField,
GenericIPAddressField,
IntegerField,
Manager,
Max,
Model,
Q,
TextField,
UUIDField,
)
from django.db.models.query import QuerySet
from django.utils.translation import gettext as _

Completer = t.Callable[[Context, Parameter, str], t.List[CompletionItem]]
Strings = t.Union[t.Sequence[str], t.KeysView[str], t.Generator[str, None, None]]
Expand Down Expand Up @@ -107,7 +111,7 @@ def handle(
function that returns a configured parser and completer for a model object
and helps reduce boilerplate.
:param model_cls: The Django model class to query.
:param model_or_qry: The Django model class or a queryset to filter against.
:param lookup_field: The name of the model field to use for lookup.
:param help_field: The name of the model field to use for help text or None if
no help text should be provided.
Expand All @@ -130,6 +134,7 @@ def handle(
QueryBuilder = t.Callable[["ModelObjectCompleter", Context, Parameter, str], Q]

model_cls: t.Type[Model]
_queryset: t.Optional[QuerySet] = None
lookup_field: str
help_field: t.Optional[str] = None
query: t.Callable[[Context, Parameter, str], Q]
Expand All @@ -144,6 +149,10 @@ def handle(

_field: Field

@property
def queryset(self) -> t.Union[QuerySet, Manager[Model]]:
return self._queryset or self.model_cls.objects

def to_str(self, obj: t.Any) -> str:
return str(obj)

Expand Down Expand Up @@ -253,7 +262,11 @@ def uuid_query(self, context: Context, parameter: Parameter, incomplete: str) ->
self._offset += 1

if len(uuid) > 32:
raise ValueError(f"Too many UUID characters: {incomplete}")
raise ValueError(
_("Too many UUID characters: {incomplete}").format(
incomplete=incomplete
)
)
min_uuid = UUID(uuid + "0" * (32 - len(uuid)))
max_uuid = UUID(uuid + "f" * (32 - len(uuid)))
return Q(**{f"{self.lookup_field}__gte": min_uuid}) & Q(
Expand All @@ -262,15 +275,23 @@ def uuid_query(self, context: Context, parameter: Parameter, incomplete: str) ->

def __init__(
self,
model_cls: t.Type[Model],
model_or_qry: t.Union[t.Type[Model], QuerySet],
lookup_field: t.Optional[str] = None,
help_field: t.Optional[str] = help_field,
query: t.Optional[QueryBuilder] = None,
limit: t.Optional[int] = limit,
case_insensitive: bool = case_insensitive,
distinct: bool = distinct,
):
self.model_cls = model_cls
if inspect.isclass(model_or_qry) and issubclass(model_or_qry, Model):
self.model_cls = model_or_qry
elif isinstance(model_or_qry, QuerySet): # type: ignore
self.model_cls = model_or_qry.model
self._queryset = model_or_qry
else:
raise ValueError(
_("ModelObjectCompleter requires a Django model class or queryset.")
)
self.lookup_field = str(
lookup_field or getattr(self.model_cls._meta.pk, "name", "id")
)
Expand All @@ -295,7 +316,9 @@ def __init__(
self.query = self.float_query
else:
raise ValueError(
f"Unsupported lookup field class: {self._field.__class__.__name__}"
_("Unsupported lookup field class: {cls}").format(
cls=self._field.__class__.__name__
)
)

def __call__(
Expand Down Expand Up @@ -343,9 +366,7 @@ def __call__(
],
help=getattr(obj, self.help_field, None) if self.help_field else "",
)
for obj in getattr(self.model_cls, "objects")
.filter(completion_qry)
.distinct()[0 : self.limit]
for obj in self.queryset.filter(completion_qry).distinct()[0 : self.limit]
if (
getattr(obj, self.lookup_field) is not None
and self.to_str(getattr(obj, self.lookup_field))
Expand Down
9 changes: 5 additions & 4 deletions django_typer/management/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from django.core.management.base import OutputWrapper as BaseOutputWrapper
from django.core.management.color import Style as ColorStyle
from django.db.models import Model
from django.db.models.query import QuerySet
from django.utils.functional import Promise, classproperty
from django.utils.translation import gettext as _

Expand Down Expand Up @@ -109,7 +110,7 @@ def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:


def model_parser_completer(
model_cls: t.Type[Model],
model_or_qry: t.Union[t.Type[Model], QuerySet],
lookup_field: t.Optional[str] = None,
case_insensitive: bool = False,
help_field: t.Optional[str] = ModelObjectCompleter.help_field,
Expand Down Expand Up @@ -139,7 +140,7 @@ def handle(
...
:param model_cls: the model class to use for lookup
:param model_or_qry: the model class or QuerySet to use for lookup
:param lookup_field: the field to use for lookup, by default the primary key
:param case_insensitive: whether to perform case insensitive lookups and
completions, default: False
Expand All @@ -155,13 +156,13 @@ def handle(
"""
return {
"parser": ModelObjectParser(
model_cls,
model_or_qry if inspect.isclass(model_or_qry) else model_or_qry.model, # type: ignore
lookup_field,
case_insensitive=case_insensitive,
on_error=on_error,
),
"shell_complete": ModelObjectCompleter(
model_cls,
model_or_qry,
lookup_field,
case_insensitive=case_insensitive,
help_field=help_field,
Expand Down
109 changes: 57 additions & 52 deletions doc/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,29 @@
Change Log
==========

v2.1.3
======
v2.2.0 (26-JUL-2024)
====================

* Implemented `ModelObjectCompleter should optionally accept a QuerySet in place of a Model class. <https://github.com/bckohan/django-typer/issues/96>`_

v2.1.3 (15-JUL-2024)
====================

* Fixed `Move from django_typer to django_typer.management broke doc reference links. <https://github.com/bckohan/django-typer/issues/98>`_
* Implemented `Support Django 5.1 <https://github.com/bckohan/django-typer/issues/97>`_

v2.1.2
======
v2.1.2 (07-JUN-2024)
====================

* Fixed `Type hint kwargs to silence pylance warnings about partially unknown types <https://github.com/bckohan/django-typer/issues/93>`_

v2.1.1
======
v2.1.1 (06-JUN-2024)
====================

* Fixed `handle = None does not work for mypy to silence type checkers <https://github.com/bckohan/django-typer/issues/90>`_

v2.1.0
======
v2.1.0 (05-JUN-2024)
====================

.. warning::

Expand All @@ -39,19 +44,19 @@ v2.1.0
* Implemented `Move core code out of __init__.py into management/__init__.py <https://github.com/bckohan/django-typer/issues/81>`_
* Fixed `Typer(help="") doesnt work. <https://github.com/bckohan/django-typer/issues/78>`_

v2.0.2
======
v2.0.2 (03-JUN-2024)
====================

* Fixed `class help attribute should be type hinted to allow a lazy translation string. <https://github.com/bckohan/django-typer/issues/85>`_


v2.0.1
======
v2.0.1 (31-MAY-2024)
====================

* Fixed `Readme images are broken. <https://github.com/bckohan/django-typer/issues/77>`_

v2.0.0
======
v2.0.0 (31-MAY-2024)
====================

This major version release, includes an extensive internal refactor, numerous bug fixes and the
addition of a plugin-based extension pattern.
Expand All @@ -75,19 +80,19 @@ addition of a plugin-based extension pattern.
* Implemented `Add completer/parser for GenericIPAddressField. <https://github.com/bckohan/django-typer/issues/12>`_


v1.1.2
======
v1.1.2 (22-APR-2024)
====================

* Fixed `Overridden common Django arguments fail to pass through when passed through call_command <https://github.com/bckohan/django-typer/issues/54>`_

v1.1.1
======
v1.1.1 (11-APR-2024)
====================

* Implemented `Fix pyright type checking and add to CI <https://github.com/bckohan/django-typer/issues/51>`_
* Implemented `Convert CONTRIBUTING.rst to markdown <https://github.com/bckohan/django-typer/issues/50>`_

v1.1.0
======
v1.1.0 (03-APR-2024)
====================

* Implemented `Convert readme to markdown. <https://github.com/bckohan/django-typer/issues/48>`_
* Fixed `typer 0.12.0 breaks django_typer 1.0.9 <https://github.com/bckohan/django-typer/issues/47>`_
Expand All @@ -98,41 +103,41 @@ v1.0.9 (yanked)

* Fixed `Support typer 0.12.0 <https://github.com/bckohan/django-typer/issues/46>`_

v1.0.8
======
v1.0.8 (26-MAR-2024)
====================

* Fixed `Support typer 0.10 and 0.11 <https://github.com/bckohan/django-typer/issues/45>`_

v1.0.7
======
v1.0.7 (17-MAR-2024)
====================

* Fixed `Helps throw an exception when invoked from an absolute path that is not relative to the getcwd() <https://github.com/bckohan/django-typer/issues/44>`_

v1.0.6
======
v1.0.6 (14-MAR-2024)
====================

* Fixed `prompt options on groups still prompt when given as named parameters on call_command <https://github.com/bckohan/django-typer/issues/43>`_


v1.0.5
======
v1.0.5 (14-MAR-2024)
====================

* Fixed `Options with prompt=True are prompted twice <https://github.com/bckohan/django-typer/issues/42>`_


v1.0.4
======
v1.0.4 (13-MAR-2024)
====================

* Fixed `Help sometimes shows full script path in Usage: when it shouldnt. <https://github.com/bckohan/django-typer/issues/40>`_
* Fixed `METAVAR when ModelObjectParser supplied should default to model name <https://github.com/bckohan/django-typer/issues/39>`_

v1.0.3
======
v1.0.3 (08-MAR-2024)
====================

* Fixed `Incomplete typing info for @command decorator <https://github.com/bckohan/django-typer/issues/33>`_

v1.0.2
======
v1.0.2 (05-MAR-2024)
====================

* Fixed `name property on TyperCommand is too generic and should be private. <https://github.com/bckohan/django-typer/issues/37>`_
* Fixed `When usage errors are thrown the help output should be that of the subcommand invoked not the parent group. <https://github.com/bckohan/django-typer/issues/36>`_
Expand All @@ -143,50 +148,50 @@ v1.0.2
* Fixed `Missing subcommand produces stack trace without --traceback. <https://github.com/bckohan/django-typer/issues/27>`_
* Fixed `Allow handle() to be an initializer. <https://github.com/bckohan/django-typer/issues/24>`_

v1.0.1
======
v1.0.1 (29-FEB-2024)
====================

* Fixed `shell_completion broken for click < 8.1 <https://github.com/bckohan/django-typer/issues/21>`_

v1.0.0
======
v1.0.0 (26-FEB-2024)
====================

* Initial production/stable release.

v0.6.1b
=======
v0.6.1b (24-FEB-2024)
=====================

* Incremental beta release - this is also the second release candidate for version 1.
* Peg typer version to 0.9.x

v0.6.0b
=======
v0.6.0b (23-FEB-2024)
=====================

* Incremental beta release - this is also the first release candidate for version 1.


v0.5.0b
=======
v0.5.0b (31-JAN-2024)
=====================

* Incremental Beta Release

v0.4.0b
=======
v0.4.0b (08-JAN-2024)
=====================

* Incremental Beta Release

v0.3.0b
=======
v0.3.0b (06-JAN-2024)
=====================

* Incremental Beta Release

v0.2.0b
=======
v0.2.0b (04-JAN-2024)
=====================

* Incremental Beta Release


v0.1.0b
=======
v0.1.0b (05-DEC-2023)
=====================

* Initial Release (Beta)
4 changes: 2 additions & 2 deletions doc/source/shell_completion.rst
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ Model Objects

* completer: :class:`~django_typer.completers.ModelObjectCompleter`
* parser: :class:`~django_typer.parsers.ModelObjectParser`
* convenience: :func:`~django_typer.model_parser_completer`
* convenience: :func:`~django_typer.management.model_parser_completer`

This completer/parser pairing provides the ability to fetch a model object from one of its fields.
Most field types are supported. Additionally any other field can be set as the help text that some
Expand All @@ -419,7 +419,7 @@ shells support. Refer to the reference documentation and the
ModelClass,
typer.Argument(
**model_parser_completer(
ModelClass,
ModelClass, # may also accept a QuerySet for pre-filtering
'field_name', # the field that should be matched (defaults to id)
help_field='other_field' # optionally provide some additional help text
),
Expand Down
Loading

0 comments on commit 4642ee2

Please sign in to comment.