Skip to content

Commit

Permalink
Add facilities for managing dev Postgres instances (furthemore#354)
Browse files Browse the repository at this point in the history
This helps the project move away from sqlite and in more of a
"we use postgres" mindset with easy to use commands to make
onboarding for new devs without docker as easy as possible.
  • Loading branch information
staticfox authored Feb 20, 2025
1 parent a4fb660 commit 8c37cd4
Show file tree
Hide file tree
Showing 8 changed files with 396 additions and 4 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ venv3/
.env
htmlcov/
.DS_Store
pgdb/
node_modules/
.python-version
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,13 @@ The following was tested on a fresh installation of Ubuntu 20.04.
source venv/bin/activate
pip install -r requirements.txt

# Review your settings
# Create a development database server
python manage.py make_db

# Start the development database server
python manage.py start_db

# Review your settings, including the database settings from the output from make_db.
cp fm_eventmanager/settings.py.devel fm_eventmanager/settings.py

python manage.py migrate
Expand Down
13 changes: 10 additions & 3 deletions fm_eventmanager/settings.py.devel
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,19 @@ WSGI_APPLICATION = 'fm_eventmanager.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases

# Remove this class and replace 'HOST' in DATABASES['default']
# with the output from `python manage.py make_db` or replace
# with your own Postgres server configuration.
class _ReadTheREADME:
pass

DATABASES = {
'default': {
'ENGINE' : 'django.db.backends.sqlite3',
'NAME' : os.path.join(BASE_DIR, 'db.sqlite3'),
'ENGINE': 'django.db.backends.postgresql',
'HOST': _ReadTheREADME,
'NAME' : 'apis_dev',
'TEST' : {
'NAME' : os.path.join(BASE_DIR, 'test-db.sqlite3'),
'NAME' : 'apis_test',
}
}
}
Expand Down
111 changes: 111 additions & 0 deletions registration/management/commands/make_db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
from pathlib import Path

from django.conf import settings
from django.core.management.base import BaseCommand

from registration.utils.database import DatabaseStatus, Postgres


config_block = """
DATABASES = {{
'default': {{
'ENGINE': 'django.db.backends.postgresql',
'HOST': '{db_path}',
'NAME' : '{db_name}',
}}
}}
"""

config_block_with_test = """
DATABASES = {{
'default': {{
'ENGINE': 'django.db.backends.postgresql',
'HOST': '{db_path}',
'NAME' : '{db_name}',
'TEST' : {{
'NAME' : '{test_db_name}',
}}
}}
}}
"""


class Command(BaseCommand):
help = "Creates a development postgresql database."

def add_arguments(self, parser):
base_dir = Path(settings.BASE_DIR)
db_dir = (base_dir / "pgdb").absolute()

parser.add_argument(
"--db-path",
type=str,
default=str(db_dir),
help=f"Path where the postgresql database will be created, default {db_dir}.",
)
parser.add_argument(
"--db-name",
type=str,
default="apis_dev",
help="The database name to create, default apis_dev.",
)
parser.add_argument(
"--test-db-name",
type=str,
default="apis_test",
help="The test database name to create, default apis_test.",
)
parser.add_argument(
"--skip-test-db",
type=bool,
default=False,
help="Whether or not to skip creating the test database, default False.",
)
parser.add_argument(
"--silent",
type=bool,
default=False,
help="Whether to suppress configuration information, default False."
)

def handle(self, *args, **options):
postgres = Postgres(options["db_path"])

status = postgres.get_status()

if status in (DatabaseStatus.UNKNOWN, DatabaseStatus.INVALID_DIRECTORY):
postgres.delete()

status = DatabaseStatus.DOES_NOT_EXIST

if status in (DatabaseStatus.DOES_NOT_EXIST, DatabaseStatus.DIRECTORY_EMPTY):
postgres.init()

status = DatabaseStatus.STOPPED

db_name = options["db_name"]
postgres.create_db(db_name)

skip_test_db = options["skip_test_db"]
test_db_name = options["test_db_name"]
if test_db_name and not skip_test_db:
postgres.create_db(test_db_name)

if options["silent"]:
return

if test_db_name and not skip_test_db:
config_text = config_block_with_test.format(
db_path=postgres.db_path,
db_name=db_name,
test_db_name=test_db_name,
)
else:
config_text = config_block.format(
db_path=postgres.db_path,
db_name=db_name,
)

print(f"Created database {db_name} at {postgres.db_path}")
print("Add the following to your settings.py")
print(config_text)
35 changes: 35 additions & 0 deletions registration/management/commands/start_db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from pathlib import Path
import sys

from django.conf import settings
from django.core.management.base import BaseCommand

from registration.utils.database import DatabaseStatus, Postgres


class Command(BaseCommand):
help = "Starts the development postgresql database."

def add_arguments(self, parser):
base_dir = Path(settings.BASE_DIR)
db_dir = (base_dir / "pgdb").absolute()

parser.add_argument(
"--db-path",
type=str,
default=str(db_dir),
help=f"Path where the postgresql database will be stopped, default {db_dir}.",
)

def handle(self, *args, **options):
postgres = Postgres(options["db_path"])

status = postgres.get_status()
if status == DatabaseStatus.STOPPED:
postgres.start()

print("Postgres server started")
elif status == DatabaseStatus.RUNNING:
print("Postgres server is already running")
else:
print(f"Cannot start Postgres server, invalid status: {status}", file=sys.stderr)
37 changes: 37 additions & 0 deletions registration/management/commands/stop_db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from pathlib import Path

from django.conf import settings
from django.core.management.base import BaseCommand

from registration.utils.database import DatabaseStatus, Postgres


class Command(BaseCommand):
help = "Stops the development postgresql database."

def add_arguments(self, parser):
base_dir = Path(settings.BASE_DIR)
db_dir = (base_dir / "pgdb").absolute()

parser.add_argument(
"--db-path",
type=str,
default=str(db_dir),
help=f"Path where the postgresql database will be stopped, default {db_dir}.",
)
parser.add_argument(
"--delete",
type=bool,
default=False,
help="Whether or not to delete the database after it has been stopped, default False."
)

def handle(self, *args, **options):
postgres = Postgres(options["db_path"])

status = postgres.get_status()
if status == DatabaseStatus.RUNNING:
postgres.stop()

if options["delete"]:
postgres.delete()
Empty file added registration/utils/__init__.py
Empty file.
Loading

0 comments on commit 8c37cd4

Please sign in to comment.