Skip to content

Commit

Permalink
Add namespaced command (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
shosca committed Jul 5, 2018
1 parent d7cad7a commit b45eb0c
Show file tree
Hide file tree
Showing 16 changed files with 271 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,4 @@ docs/_build
.pytest_cache
db.sqlite3
.mypy_cache
*.sqlite3
10 changes: 10 additions & 0 deletions django_sorcery/db/sqlalchemy.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ def registry(self):

return self._registry

@property
def inspector(self):
return self.inspect(self.engine)

def session(self, **kwargs):
"""
Return the current session, creating it if necessary using session_factory for the current scope
Expand Down Expand Up @@ -284,3 +288,9 @@ def create_all(self):
Create the schema in db
"""
self.metadata.create_all(bind=self.engine)

def drop_all(self):
"""
Drop the schema in db
"""
self.metadata.drop_all(bind=self.engine)
4 changes: 4 additions & 0 deletions django_sorcery/management/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals

from .base import NamespacedCommand # noqa
52 changes: 52 additions & 0 deletions django_sorcery/management/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
import inspect
import os

from django.core.management.base import BaseCommand, CommandParser


class NamespacedCommand(BaseCommand):
@property
def commands(self):
if not hasattr(self, "_commands"):
self._commands = {}
for cls in reversed(self.__class__.mro()):
self._commands.update(
{
name: cmd_cls
for name, cmd_cls in vars(self.__class__).items()
if inspect.isclass(cmd_cls) and hasattr(cmd_cls, "mro") and BaseCommand in cmd_cls.mro()
}
)

return self._commands

def run_command_from_argv(self, command, argv):
command.style = self.style

cmd_args = argv[:]
cmd_args[0] = " ".join([cmd_args[0], cmd_args.pop(1)])
command.run_from_argv(cmd_args)

def run_from_argv(self, argv):
self._called_from_command_line = True

if len(argv) > 2 and not argv[2].startswith("-") and argv[2] in self.commands:
command = self.commands.get(argv[2])(stdout=self.stdout, stderr=self.stderr)
self.run_command_from_argv(command, argv)
else:
self.print_help(argv[0], argv[1])

def create_parser(self, prog_name, subcommand):
parser = CommandParser(
self, prog="%s %s" % (os.path.basename(prog_name), subcommand), description=self.help or None
)
for name, command_cls in self.commands.items():
parser.add_argument(name, help=command_cls.help)

return parser

def print_help(self, prog_name, subcommand):
parser = self.create_parser(prog_name, subcommand)
parser.print_help(self.stdout)
2 changes: 2 additions & 0 deletions django_sorcery/management/commands/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
16 changes: 16 additions & 0 deletions django_sorcery/management/commands/sorcery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals

from .. import NamespacedCommand
from .sorcery_createall import CreateAll
from .sorcery_dropall import DropAll


class Command(NamespacedCommand):
help = "django-sorcery management commands"

createall = CreateAll
dropall = DropAll

class Meta:
namespace = "sorcery"
29 changes: 29 additions & 0 deletions django_sorcery/management/commands/sorcery_createall.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals

from django.core.management.base import BaseCommand

from django_sorcery.db import databases


class CreateAll(BaseCommand):
help = "Creates SQLAlchemy database schemas"

def add_arguments(self, parser):
parser.add_argument(
"--database",
"-d",
action="append",
dest="databases",
type=str,
help="Nominates a database to synchronize. By default will synchronize all.",
)

def handle(self, *args, **kwargs):
dbs = kwargs.get("databases") or databases.keys()
for key in dbs:
databases[key].create_all()
self.stdout.write(self.style.SUCCESS('Successfully ran create_all() for "%s"' % key))


Command = CreateAll
29 changes: 29 additions & 0 deletions django_sorcery/management/commands/sorcery_dropall.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals

from django.core.management.base import BaseCommand

from django_sorcery.db import databases


class DropAll(BaseCommand):
help = "Drops SQLAlchemy database schemas"

def add_arguments(self, parser):
parser.add_argument(
"--database",
"-d",
action="append",
dest="databases",
type=str,
help="Nominates a database to drop. By default will drop all.",
)

def handle(self, *args, **kwargs):
dbs = kwargs.get("databases") or databases.keys()
for key in dbs:
databases[key].drop_all()
self.stdout.write(self.style.SUCCESS('Successfully ran drop_all() for "%s"' % key))


Command = DropAll
2 changes: 2 additions & 0 deletions tests/management/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
2 changes: 2 additions & 0 deletions tests/management/commands/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
55 changes: 55 additions & 0 deletions tests/management/commands/test_sorcery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals

from django.test import TestCase

from django_sorcery.db import databases
from django_sorcery.management.commands.sorcery import Command


db = databases.get("default")
other_db = databases.get("fromdbs")


class Foo(db.Model):

id = db.Column(db.BigInteger(), primary_key=True)


class Bar(other_db.Model):

id = other_db.Column(other_db.BigInteger(), primary_key=True)


class TestCreateAllDropAll(TestCase):
def setUp(self):
super(TestCreateAllDropAll, self).setUp()
db.drop_all()
other_db.drop_all()

def tearDown(self):
super(TestCreateAllDropAll, self).setUp()
db.create_all()
other_db.create_all()

def test_create_drop_commands(self):

cmd = Command()

cmd.run_from_argv(["./manage.py", "sorcery", "createall", "-d", other_db.alias])
cmd.run_from_argv(["./manage.py", "sorcery", "dropall", "-d", db.alias])

self.assertEqual(db.inspector.get_table_names(), [])
self.assertEqual(other_db.inspector.get_table_names(), ["bar"])

cmd.run_from_argv(["./manage.py", "sorcery", "dropall", "-d", other_db.alias])
cmd.run_from_argv(["./manage.py", "sorcery", "createall", "-d", db.alias])

self.assertEqual(db.inspector.get_table_names(), ["foo"])
self.assertEqual(other_db.inspector.get_table_names(), [])

cmd.run_from_argv(["./manage.py", "sorcery", "createall", "-d", other_db.alias])
cmd.run_from_argv(["./manage.py", "sorcery", "dropall", "-d", db.alias])

self.assertEqual(db.inspector.get_table_names(), [])
self.assertEqual(other_db.inspector.get_table_names(), ["bar"])
65 changes: 65 additions & 0 deletions tests/management/test_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals

from django.core.management.base import BaseCommand
from django.utils.six import StringIO

from django_sorcery.management import NamespacedCommand

from ..base import TestCase


class SubCommand(BaseCommand):
help = "subcommand"

def add_arguments(self, parser):
parser.add_argument("-d", action="append", dest="dummy", type=str, help="subcommand help message")

def handle(self, *args, **kwargs):
return "subcommand triggered"


class DummyCommand(NamespacedCommand):

sub1 = SubCommand

class Meta:
namespace = "dummy"


class TestNamespacedCommand(TestCase):
def setUp(self):
super(TestNamespacedCommand, self).setUp()
self.stdout = StringIO()
self.stderr = StringIO()

def test_help_message(self):

cmd = DummyCommand(stdout=self.stdout, stderr=self.stderr, no_color=True)

cmd.run_from_argv(["./manage.py", "dummy", "-h"])

self.stdout.seek(0)
self.maxDiff = None
self.assertEqual(
"\n".join([l.strip() for l in self.stdout.readlines()]),
"\n".join(
[
"usage: manage.py dummy [-h] sub1",
"",
"positional arguments:",
"sub1 subcommand",
"",
"optional arguments:",
"-h, --help show this help message and exit",
]
),
)

def test_trigger_subcommand(self):
cmd = DummyCommand(stdout=self.stdout, stderr=self.stderr, no_color=True)

cmd.run_from_argv(["./manage.py", "dummy", "sub1"])

self.stdout.seek(0)
self.assertEqual("".join([l.strip() for l in self.stdout.readlines()]), "subcommand triggered")
1 change: 1 addition & 0 deletions tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,4 +265,5 @@ class ClassicModel(object):


db.configure_mappers()
db.drop_all()
db.create_all()
1 change: 1 addition & 0 deletions tests/models_backpop.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,5 @@ class Order(db.Model):


db.configure_mappers()
db.drop_all()
db.create_all()
1 change: 1 addition & 0 deletions tests/models_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,5 @@ class Order(db.Model):


db.configure_mappers()
db.drop_all()
db.create_all()
2 changes: 1 addition & 1 deletion tests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def absjoin(*args):
"minimal_backpop": {"DIALECT": "sqlite"},
}

INSTALLED_APPS = ["tests.apps.TestConfig", "django.contrib.staticfiles"]
INSTALLED_APPS = ["tests.apps.TestConfig", "django_sorcery", "django.contrib.staticfiles"]

ROOT_URLCONF = "tests.urls"

Expand Down

0 comments on commit b45eb0c

Please sign in to comment.