Examples

Example files are included that provide working examples of descriptions in the User Guide. They are found in the examples directory on Github at https://github.com/sidorof/dbbase.

  • Serialization and Relationships

  • Serialization and Self-Referential Relationships

  • Documentation Dictionaries

Serialization and Relationships

# examples/two_tables.py
"""
This example mirrors the user guide example for serialization with
two tables.
"""
import json
from dbbase import DB

db = DB(config=":memory:")


class User(db.Model):
    __tablename__ = "users"
    id = db.Column(db.Integer, primary_key=True)
    first_name = db.Column(db.String(50), nullable=False)
    last_name = db.Column(db.String(50), nullable=False)
    addresses = db.relationship("Address", backref="user", lazy="immediate")

    def full_name(self):
        return "{first} {last}".format(
            first=self.first_name, last=self.last_name
        )


class Address(db.Model):
    __tablename__ = "addresses"
    id = db.Column(db.Integer, primary_key=True)
    email_address = db.Column(db.String, nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey("users.id"))


db.create_all()

user = User(id=3, first_name="Bob", last_name="Smith")
user.save()

address1 = Address(email_address="email1@example.com", user_id=user.id)
address2 = Address(email_address="email2@example.com", user_id=user.id)

db.session.add(address1)
db.session.add(address2)
db.session.commit()

# ------------------------------------------------
print("default serialization for user")
print(user.serialize(indent=2))
input("ready")

# ------------------------------------------------
print("modified serialization: stop list for first/last names")
User.SERIAL_STOPLIST = ["first_name", "last_name"]
print(user.serialize(indent=2))
input("ready")

# ------------------------------------------------
print("now only the email_address portion of address shows")
Address.SERIAL_FIELDS = ["email_address"]
print(user.serialize(indent=2))
input("ready")

# ------------------------------------------------

print("Now ad hoc variables")
print(
    "user serial_fields is {}".format(
        "['id', 'first_name', 'last_name', 'addresses']"
    )
)
print(
    "user serial_field_relations is {}".format(
        "{'Address': ['id', 'email_address'] "
    )
)
print(
    user.serialize(
        indent=2,
        sort=True,
        serial_fields=["id", "first_name", "last_name", "addresses"],
        serial_field_relations={"Address": ["id", "email_address"]},
    )
)
input("ready")
print()
print("serialization of just an address")
print(
    address1.serialize(
        indent=2,
        sort=True,
        serial_fields=["id", "email_address"],
        serial_field_relations={"User": ["user_id", "first_name", "last_name"]},
    )
)

input("ready")

# ------------------------------------------------
print('Print table documentation')
print(json.dumps(db.doc_tables(), indent=4))

Serialization and Self-Referential Relationships

# examples/self_referential.py
"""
This example shows an example where serialization works with
self-referential models.
"""
from dbbase import DB

db = DB(config=":memory:")


class Node(db.Model):
    """self-referential table"""

    __tablename__ = "nodes"
    id = db.Column(db.Integer, primary_key=True)
    parent_id = db.Column(db.Integer, db.ForeignKey("nodes.id"))
    data = db.Column(db.String(50))
    children = db.relationship(
        "Node", lazy="joined", order_by="Node.id", join_depth=10
    )

    SERIAL_FIELDS = ["id", "parent_id", "data", "children"]


db.create_all()

node1 = Node(id=1, data="this is node1")
node2 = Node(id=2, data="this is node2")
node3 = Node(id=3, data="this is node3")
node4 = Node(id=4, data="this is node4")
node5 = Node(id=5, data="this is node5")
node6 = Node(id=6, data="this is node6")

db.session.add(node1)
db.session.commit()
node1.children.append(node2)
db.session.commit()
node2.children.append(node3)
db.session.commit()
node2.children.append(node4)
db.session.commit()
node1.children.append(node5)
db.session.commit()
node5.children.append(node6)
db.session.commit()

print("Example of self-referential serialization")
print(node1.serialize(indent=2))
input("ready")

print("Example in compact form")
print(node1.serialize())

Documentation Dictionaries

# examples/table_documentation.py
"""
This example mirrors the user guide example for documentation
dictionaries.

This is simply a table with different kinds of columns and parameter choices.
The StatusCodes class is included as a method for illustrating a selection
of choices that would be converted to a status code. There are several ways
to do this, this is one of several.

BigTable is the class is created. The function `db.doc_table` creates the
output.

For clarity, the output converted to JSON format.

`print(json.dumps(db.doc_table(BigTable), indent=4))`

"""
import json
from datetime import date, datetime
import sqlalchemy.types as types

from dbbase import DB

db = DB(config=":memory:")

status_codes = [
    [0, "New"],
    [1, "Active"],
    [2, "Suspended"],
    [3, "Inactive"],
]


class StatusCodes(types.TypeDecorator):
    """
    Status codes are entered as strings and converted to
    integers when saved to the database.
    """

    impl = types.Integer

    def __init__(self, status_codes, **kw):
        self.choices = dict(status_codes)
        super(StatusCodes, self).__init__(**kw)

    def process_bind_param(self, value, dialect):
        """called when saving to the database"""
        return [k for k, v in self.choices.items() if v == value][0]

    def process_result_value(self, value, dialect):
        """called when pulling from database"""
        return self.choices[value]


class BigTable(db.Model):
    """Test class with a variety of column types"""

    __tablename__ = "big_table"

    id = db.Column(
        db.Integer,
        primary_key=True,
        nullable=True,
        comment="Primary key with a value assigned by the database",
        info={"extra": "info here"},
    )

    status_id = db.Column(
        StatusCodes(status_codes),
        nullable=False,
        comment="Choices from a list. String descriptors "
        "change to integer upon saving. Enums without the headache.",
    )

    @db.orm.validates("status_id")
    def _validate_id(self, key, id):
        """_validate_id

        Args:
            id: (int) : id must be in cls.choices
        """
        if id not in dict(status_codes):
            raise ValueError("{} is not valid".format(id))
        return id

    # nullable / not nullable
    name1 = db.Column(
        db.String(50), nullable=False, comment="This field is required"
    )
    name2 = db.Column(
        db.String(50), nullable=True, comment="This field is not required",
    )

    # text default
    name3 = db.Column(
        db.Text,
        default="test",
        nullable=False,
        comment="This field has a default value",
        index=True,
    )

    item_length = db.Column(
        db.Float, nullable=False, comment="This field is a float value"
    )

    item_amount = db.Column(db.Numeric(17, 6), default=0.0)

    # integer and default
    some_small_int = db.Column(
        db.SmallInteger,
        default=0,
        nullable=False,
        comment="This field is a small integer",
    )
    some_int = db.Column(
        db.Integer,
        default=0,
        nullable=False,
        comment="This field is a 32 bit integer",
    )
    some_big_int = db.Column(
        db.BigInteger,
        default=0,
        nullable=False,
        comment="This field is a big integer",
    )

    fk_id = db.Column(
        db.Integer,
        db.ForeignKey("other_table.id"),
        nullable=False,
        comment="This field is constrained by a foreign key on"
        "another table",
    )

    today = db.Column(
        db.Date,
        doc="this is a test",
        info={"test": "this is"},
        comment="This field defaults to today, created at model level",
        default=date.today,
    )

    created_at1 = db.Column(
        db.DateTime,
        default=datetime.now,
        comment="This field defaults to now, created at model level",
    )
    created_at2 = db.Column(
        db.DateTime,
        server_default=db.func.now(),
        comment="This field defaults to now, created at the server" "level",
    )
    update_time1 = db.Column(
        db.DateTime,
        onupdate=datetime.now,
        comment="This field defaults only on updates",
    )
    update_time2 = db.Column(
        db.DateTime,
        server_onupdate=db.func.now(),
        comment="This field defaults only on updates, but on the" "server",
    )

    unique_col = db.Column(
        db.String(20),
        unique=True,
        comment="This must be a unique value in the database.",
    )

    # adapted from sqlalchemy docs
    abc = db.Column(
        db.String(20),
        server_default="abc",
        comment="This field defaults to text but on the server.",
    )
    index_value = db.Column(
        db.Integer,
        server_default=db.text("0"),
        comment="This field defaults to an integer on the server.",
    )

    __table_args = (db.Index("ix_name1_name2", "name1", "name2", unique=True),)


class OtherTable(db.Model):
    """
    This table is used solely to enable an option for a foreign key.
    """

    __tablename__ = "other_table"
    id = db.Column(db.Integer, primary_key=True, nullable=True)


# using JSON for a better output

print(json.dumps(db.doc_table(BigTable), indent=4))