Skip to content

Commit

Permalink
Merge pull request #4 from OCHA-DAP/reflection
Browse files Browse the repository at this point in the history
Table names in snake case
  • Loading branch information
mcarans committed Sep 1, 2023
2 parents 6b90d52 + cd3554c commit c10f0f2
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 3 deletions.
8 changes: 7 additions & 1 deletion documentation/main.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ Additional postgresql functionality is available if this library is installed wi

## Breaking changes

From 1.2.7, default table names are no longer plural. The camel case class name
is converted to snake case, for example `MyTable` becomes `my_table`.

From 1.2.3, Base must be chosen from `hdx.database.no_timezone`
(`db_has_tz=False`: the default) or `hdx.database.with_timezone`
(`db_has_tz=True`).
Expand Down Expand Up @@ -46,7 +49,10 @@ Your SQLAlchemy database tables must inherit from `Base` in

from hdx.database.no_timezone import Base
class MyTable(Base):
my_col = Column(Integer, ForeignKey(MyTable2.col2), primary_key=True)
my_col: Mapped[int] = mapped_column(Integer, ForeignKey(MyTable2.col2), primary_key=True)

A default table name is set which can be overridden: it is the camel case class
name to converted to snake case, for example `MyTable` becomes `my_table`.

Then a connection can be made to a database as follows including through an SSH
tunnel:
Expand Down
4 changes: 3 additions & 1 deletion src/hdx/database/no_timezone.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from sqlalchemy import DateTime, TypeDecorator
from sqlalchemy.orm import DeclarativeBase, declared_attr

from .utils import camel_to_snake_case


class ConversionNoTZ(TypeDecorator):
"""Convert from/to datetime with timezone from database columns that don't
Expand Down Expand Up @@ -37,4 +39,4 @@ class Base(DeclarativeBase):

@declared_attr.directive
def __tablename__(cls):
return f"{cls.__name__.lower()}s"
return camel_to_snake_case(cls.__name__)
19 changes: 19 additions & 0 deletions src/hdx/database/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""Other utilities"""

import re


def camel_to_snake_case(string: str) -> str:
"""Convert a ``CamelCase`` name to ``snake_case``.
(Taken from flask-sqlalchemy code.)
Args:
string (str): String to convert
Returns:
str: String in snake case
"""
string = re.sub(
r"((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))", r"_\1", string
)
return string.lower().lstrip("_")
4 changes: 3 additions & 1 deletion src/hdx/database/with_timezone.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from sqlalchemy.orm import DeclarativeBase, declared_attr

from .utils import camel_to_snake_case


class Base(DeclarativeBase):
@declared_attr.directive
def __tablename__(cls):
return f"{cls.__name__.lower()}s"
return camel_to_snake_case(cls.__name__)
1 change: 1 addition & 0 deletions tests/hdx/database/test_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ def close(self):
monkeypatch.setattr(Database, "get_session", get_session)

def test_get_session(self, nodatabase):
assert DBTestDate.__tablename__ == "db_test_date"
with Database(
database=TestDatabase.dbpath, port=None, dialect="sqlite"
) as dbsession:
Expand Down

0 comments on commit c10f0f2

Please sign in to comment.