Metadata-Version: 2.1
Name: flask-quick-sql
Version: 1.0.2
Summary: A quick way to run SQL in your Flask app.
Home-page: https://github.com/le717/flask-quick-sql
License: Public domain
Author: Caleb
Author-email: le717@users.noreply.github.com
Requires-Python: >=3.10,<4.0
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Plugins
Classifier: Framework :: Flask
Classifier: License :: Other/Proprietary License
Classifier: License :: Public Domain
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Topic :: Database
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Dist: flask (>=2.2.3,<3.0.0)
Requires-Dist: flask-sqlalchemy (>=3.0.3,<4.0.0)
Project-URL: Repository, https://github.com/le717/flask-quick-sql
Description-Content-Type: text/markdown

# flask-quick-sql

> A quick way to run SQL in your Flask app.

## Info

A long time ago, I used the [`records`](https://pypi.org/project/records/) library to query a database
with raw SQL. It was great, [until it wasn't](https://github.com/kennethreitz/records/issues/208).

Fast-forward many years and I was working on replacing the code that used `records`, but I needed to keep
the existing code working. I also needed to remove `records` from my dependencies in order to update
literally _everything_, but alas, I could not.

Enter this little wrapper code I wrote. It _kinda_ keeps API compat but also kinda not.
That wasn't my goal. My goal was to keep _enough_ compatibility so I wouldn't have to
change much of my code while also keeping it nice to use.

This Flask extension exists solely because I liked my wrapper code and will
almost certainly will have a use for it again, and I didn't want it to just go away
when I deleted it from my app.

## Usage

* Python 3.10+
* Flask 2.2+
* Flask-SQLAlchemy 3.0+
* SQLAlchemy 2.0+

If a SQLAlchemy instance already exists in your app, it will be used. Otherwise, an instance
will be created for you.

```python
from flask import Flask
from flask_quick_sql import QuickSQL


def create_app():
    app = Flask(__name__)

    # You must set this
    app.config["SQLALCHEMY_DATABASE_URI"] = ...
    db = QuickSQL(app)

    # A very wasteful yet all too common query, especially in PHP land
    all_users = db.query("SELECT * FROM users").all()
    print(all_users[0]["username"])

    return app
```

The immediate result of `query()` isn't very useful. You'll want to chain a call to `.all()`, `.first()`,
or `.one()`. If there's no data, `.first()` and `.one()` will return `None`.

You don't get property and key access like `records` gave you. You get one or the other.
By default, you get a dictionary. Breaking API change from `records`? Yes. I don't care.

To get a `sqlalchemy.engine.Row` (basically `collections.namedtuple`) object instead,
pass `as_nt=True` as a parameter to any method.

You can also iterate over the whole result set, with each dictionary record being `yield`ed
(you cannot get a named tuple when doing this):

```python
[User(**r) for r in quick_sql.query("SELECT * FROM users")]
```

Because SQLAlchemy is used under the hood, prepared statements work as expected:

```python
sql = "SELECT * FROM users WHERE is_active = :is_active"
[User(**r) for r in db.query(sql, is_active=False)]
```

Have fun running your [SQuirreL](http://www.squirrelsql.org/) queries.

## License

[Public domain](LICENSE)

