diff --git a/docs/src/piccolo/authentication/baseuser.rst b/docs/src/piccolo/authentication/baseuser.rst index ae687ad5b..1919f5cec 100644 --- a/docs/src/piccolo/authentication/baseuser.rst +++ b/docs/src/piccolo/authentication/baseuser.rst @@ -44,6 +44,11 @@ script), you can pass all of the arguments in as follows: If you choose this approach then be careful, as the password will be in the shell's history. +list +~~~~ + +List existing users. + change_password ~~~~~~~~~~~~~~~ diff --git a/piccolo/apps/user/commands/list.py b/piccolo/apps/user/commands/list.py new file mode 100644 index 000000000..de57c3614 --- /dev/null +++ b/piccolo/apps/user/commands/list.py @@ -0,0 +1,15 @@ +from piccolo.apps.user.tables import BaseUser +from piccolo.utils.printing import print_dict_table + + +def list(): + """ + List existing users. + """ + users = ( + BaseUser.select(BaseUser.all_columns(exclude=[BaseUser.password])) + .order_by(BaseUser.username) + .run_sync() + ) + + print_dict_table(users) diff --git a/piccolo/apps/user/piccolo_app.py b/piccolo/apps/user/piccolo_app.py index b523b1b72..635f65143 100644 --- a/piccolo/apps/user/piccolo_app.py +++ b/piccolo/apps/user/piccolo_app.py @@ -5,6 +5,7 @@ from .commands.change_password import change_password from .commands.change_permissions import change_permissions from .commands.create import create +from .commands.list import list from .tables import BaseUser CURRENT_DIRECTORY = os.path.dirname(os.path.abspath(__file__)) @@ -19,6 +20,7 @@ migration_dependencies=[], commands=[ Command(callable=create, aliases=["new"]), + Command(callable=list, aliases=["ls"]), Command(callable=change_password, aliases=["password", "pass"]), Command(callable=change_permissions, aliases=["perm", "perms"]), ], diff --git a/piccolo/utils/printing.py b/piccolo/utils/printing.py index c6adc76dd..03972b32d 100644 --- a/piccolo/utils/printing.py +++ b/piccolo/utils/printing.py @@ -1,3 +1,6 @@ +from typing import List + + def get_fixed_length_string(string: str, length=20) -> str: """ Add spacing to the end of the string so it's a fixed length, or truncate @@ -16,3 +19,36 @@ def print_heading(string: str, width: int = 64) -> None: """ print(f"\n{string.upper():^{width}}") print("-" * width) + + +def print_dict_table(data: List[dict], header_separator: bool = False) -> None: + """ + Prints out a list of dictionaries in tabular form. + Uses the first list element to extract the + column names and their order within the row. + """ + + if len(data) < 1: + print("No data") + return + + ref_order = [column for column in data[0]] + width = {column: len(str(column)) for column in ref_order} + + for item in data: + for column in ref_order: + if len(str(item[column])) > width[column]: + width[column] = len(str(item[column])) + + format_string = " | ".join([f"{{:<{width[w]}}}" for w in ref_order]) + + print(format_string.format(*[str(w) for w in ref_order])) + + if header_separator: + format_string_sep = "-+-".join( + [f"{{:<{width[w]}}}" for w in ref_order] + ) + print(format_string_sep.format(*["-" * width[w] for w in ref_order])) + + for item in data: + print(format_string.format(*[str(item[w]) for w in ref_order]))