Skip to main content

Idea

One of Python's most significant advantages is that we can make applications fast. Boilerplate is low, but we can focus on business logic, quickly deliver value, and make projects production-ready. But even if, with current powerful libraries, "time-to-market" is low, it can be lowered even more.

The idea of RedBeanPython ORM is to decrease the time to market for applications by providing an absolutely zero configuration ORM and, simultaneously, heaving the possibility of zero effort transition to advanced ORM when needed.

RedBeanPython idea is inspired by the matured RedBeanPHP ORM (since 2009 and still running).

RedBeanPython ORM's idea is not to replace the more advanced ORMs (we should not reinvent the wheel) but to cooperate with them. Initially, it can be used as only ORM. When projects evolve, both code bases (RedBeanPython and SQLAlchemy) can coexist and, finally, can be swiftly replaced by SQLAlchemy (if needed).

Tests on PostgreSQL Tests on MySQL Tests on SQLite Coverage

Quality Gate Status Vulnerabilities Security Rating

Installation

RedBeanPython is available on PyPI and can be installed with pip:

pip install redbeanpython

RedBeanPython on PyPI

Documentation

Documentation is available on redbeanpython.org.

Requirements

RedBeanPython requires

  • Python 3.11 or newer.
  • SQLAlchemy 2.0 or newer.
  • alembic 1.7 or newer.

Database requirements

For PostgreSQL support RedBeanPython requires:

  • psycopg 3.0.18 or newer.

For MySQL/MariaDB support RedBeanPython requires:

  • PyMySQL 1.0.2 or newer.

How it works? Let's see an example of a usual project lifecycle.

Stage 1. Development

We are starting a fresh project. We have an Idea, and we want to deliver MVP to production as soon as possible.

We have an empty database (or no database at all at that moment).

Let's store some data:

from datetime import datetime
from decimal import Decimal

from redbeanpython import redbean, Bean

redbean.setup(dsn=False) #(1)

user = Bean('user')
user.name = 'Adam'
user.age = 42
user.subscription_price = Decimal("12.20")
user.subscription_end = datetime.fromisoformat('2024-10-08T16:20:00')

redbean.store(user)
  1. For a temporary SQLite database call redbean.setup(dsn=False). For a production database, DSN consistent with SQLAlchemy should be provided. See connection for more details. For example, you may run a docker container with PostgreSQL:
    docker run -p 5432:5432 -e POSTGRES_PASSWORD=pass -d postgres
    
    And use it:
    redbean.setup('postgresql+psycopg://postgres:pass@localhost/postgres')
    

And that's all.

No schema is need to be created, no migrations need to be run, no config files to update.

Just run the code, and RedBeanPython will create everything automatically.

If we take a look into db, we will see that table was created:

create table user
(
    age                BIGINT,
    id                 VARCHAR(255) not null primary key,
    name               TEXT,
    subscription_end   DATETIME,
    subscription_price NUMERIC(30, 10)
);

In the background, the SQLAlchemy model was created:

from sqlalchemy import Column, BigInteger, String, Text, DateTime, Numeric

from redbeanpython import Model


class User(Model):
    __tablename__ = 'user'
    __table_args__ = {'extend_existing': True}

    age = Column(BigInteger, nullable=True)
    id = Column(String(255), primary_key=True)
    name = Column(Text, nullable=True)
    subscription_end = Column(DateTime, nullable=True)
    subscription_price = Column(Numeric(precision=30, scale=10), nullable=True)

    def __json__(self):
        return self.as_dict()

    def as_dict(self):
        return {
            'age': self.age,
            'id': self.id,
            'name': self.name,
            'subscription_end': self.subscription_end,
            'subscription_price': self.subscription_price,
        }

We do not need this information now, but it might be helpful later. (See section: hybrid mode)

Let's load it from the database, and as we see, the types are preserved:

>>> from redbeanpython import redbean
>>> redbean.setup(dsn=False)
>>> users = list(redbean.find('user'))
>>> user = users[0]
>>> print(dict(user))
{
    'age': 42, 
    'id': '1f0e6e46-6e47-404f-9547-9dc3e9741ec6', 
    'name': 'Adam', 
    'subscription_end': datetime.datetime(2024, 10, 8, 16, 20), 
    'subscription_price': Decimal('12.2000000000')
}
>>> print(type(user.subscription_end), user.subscription_end)
<class 'datetime.datetime'> 2024-10-08 16:20:00

Let's go on; development continues.

In the early stage of the project, life changes are widespread. Often, we even rewrite the first ideas.

With RedBeanPython, this is no problem; we can change the code and run it again. RedBeanPython will adjust everything automatically.

For example, we decided that the user should have a birthday. We can add in code:

# ...
user.birthday = date.fromisoformat('1981-12-01')
# ...

And rerun it. RedBeanPython will adequately adjust the database schema.

The database schema has been adjusted.

create table user
(
    age                BIGINT,
    id                 VARCHAR(255) not null primary key,
    name               TEXT,
    subscription_end   DATETIME,
    subscription_price NUMERIC(30, 10),
    birthday           DATE  -- (1)
);
  1. A new column was added.

Same as model:

class User(Model):
    # (...)
    name = Column(Text, nullable=True)
    subscription_end = Column(DateTime, nullable=True) # (1)
    subscription_price = Column(Numeric(precision=30, scale=10), nullable=True)
    # (...)

    def as_dict(self):
        return {
            # (...)
            'name': self.name,
            'subscription_end': self.subscription_end, # (2)
            'subscription_price': self.subscription_price,
        }
  1. A new property was added.
  2. A new property was added.

And alembic migration was created automatically:

def upgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    with op.batch_alter_table('user', schema=None) as batch_op:
        batch_op.add_column(sa.Column('birthday', sa.Date(), nullable=True))

def downgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    with op.batch_alter_table('user', schema=None) as batch_op:
        batch_op.drop_column('birthday')

:information: Migrations will be useful later when the project is production-ready.

Now, when we have MVP ready, we can go to production.

Stage 2: Production

When the project stays production-ready, we would like to freeze production database to avoid potential unexpected changes. No automatic changes will be made to production since that moment.

To freeze the database, we just need to add one parameter to redbean.setup() call:

redbean.setup('...', frozen=True)

Or use REDBEAN_DB_FROZEN=1 environment variable.

And, from that time on production, we can use alembic migrations to manage production database schema changes safely. We fully control how and when the database schema will be changed.

Because alembic migrations are created automatically by RedBeanPython, we can just run:

cd redbean/migrations 
DB_DSN="sqlite:////tmp/redbean.sqlite" alembic upgrade head

on production to apply all changes.

At the same time, everything will work as before in the development environment, and we still have all the benefits from automatic creation.

Stage 3: Project growth

When the project grows, we may need more performance or more sophisticated database features. As RedBeanPython is not intended to be a replacement for SQLAlchemy, instead of extending RedBeanPython forever, we can switch to SQLAlchemy ORM and take full advantage of the giants.

Moreover, we can use both ORMs simultaneously and switch to more advanced ORMs with zero effort for specific cases only.

As SQLAlchemy Models has already been created automatically, we can use it.

Let's see how it works.

from redbean.models.customer import Customer
from redbean.models.invoice import Invoice

redbean.setup(dsn=False)

# redbeanpython code
customer = Bean('customer')
customer.name = 'John'

invoice = Bean('invoice')
invoice.amount = Decimal("12.20")
invoice.customer_id = customer.id

redbean.store_many([invoice, customer])

# sqlalchemy code
session = redbean.session_maker()
query = session.query(Customer, Invoice)
query = query.filter(Customer.id == Invoice.customer_id)
query = query.filter(Invoice.amount > 10)
result = query.all()
for customer, invoice in result:
    print(customer.name, invoice.amount)

query = session.query(
    Invoice.customer_id,
    func.count('*').label('amount'),
    func.sum(Invoice.amount).label('amount'),
)
query = query.group_by(Invoice.customer_id)
for customer_id, invoices_count, invoices_total, in query.all():
    print(f"{customer_id} has: {invoices_count} invoices with a sum: {invoices_total}")

    # redbeanpython code again
    print(customer_id)
    customer = redbean.load('customer', customer_id)
    print(dict(customer))

We can freely mix both ORMs in the same code base. This is a hybrid mode.

Finally, when our project succeeds, it will be on the market for months or years. Development team grows. Codebase enlarges - we can want to switch to SQLAlchemy ORM entirely.

Stage 4: Maturity

When the project matures and we decide to switch entirely to SQLAlchemy ORM, we can say goodbye to RedBeanPython methods and start to use SQLAlchemy only.

Starting with previously automatically generated Models, we will continue to extend them and work with them in a standard SQLAlchemy way.

And then you can let RedBeanPython go. RedBeanPython did its job. It's time for retirement.

It was a beautiful journey!

Check more on redbeanpython.org.

Licence

Licence: MIT

Issue tracker

Issue tracker is available on GitHub.

Contact

Adam Puza: adampuza@redbeanpython.org

___

Metadata

Release files for redbeanpython 1.0.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for redbeanpython 1.0.2
File Size Uploaded Provenance
redbeanpython-1.0.2.tar.gz 25.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for redbeanpython 1.0.2
File Interpreter ABI Platform
redbeanpython-1.0.2-py3-none-any.whl Python 3 none any Details

Total release size: 56.3 kB

Release files / redbeanpython-1.0.2.tar.gz

Download URL redbeanpython-1.0.2.tar.gz
Size 25.1 kB
Tags Source
SHA-256 checksum
How to use checksums
7f9b6e02d9fccd2f965c99cae98cc08820210c12ec8969d158bcc777c2cd16e8
BLAKE2b-256 checksum
How to use checksums
208981f33b656f00ced9b3ae0e7ac656af9e4b1238d06b618b167719709c33cb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.12.0

Release files / redbeanpython-1.0.2-py3-none-any.whl

Download URL redbeanpython-1.0.2-py3-none-any.whl
Size 31.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c4db3b15bc9e962f0bd0f627d26af379297bff2b6474375137542e5dd21e990c
BLAKE2b-256 checksum
How to use checksums
0db510b17884d0cda73f6f4f6d0b6aec8b1639e627560e4ddece330138759ed7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.12.0

Security for redbeanpython 1.0.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page