{"id":16608351,"url":"https://github.com/thp/minidb","last_synced_at":"2025-06-11T22:07:56.991Z","repository":{"id":23583817,"uuid":"26952117","full_name":"thp/minidb","owner":"thp","description":"Store Python objects in SQLite 3. Concise, pythonic API. Easy to write, relatively easy to read. A kind of super simple ORM, if you will. Give it a try.","archived":false,"fork":false,"pushed_at":"2024-04-24T19:00:06.000Z","size":88,"stargazers_count":70,"open_issues_count":1,"forks_count":14,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-01T14:14:58.729Z","etag":null,"topics":["orm","python3","sqlite3"],"latest_commit_sha":null,"homepage":"https://thp.io/2010/minidb/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-11-21T08:34:55.000Z","updated_at":"2025-02-10T22:01:01.000Z","dependencies_parsed_at":"2024-10-26T20:30:40.984Z","dependency_job_id":"95f4dab6-f97d-4d1b-8fb5-0c0082178914","html_url":"https://github.com/thp/minidb","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/thp/minidb","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thp%2Fminidb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thp%2Fminidb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thp%2Fminidb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thp%2Fminidb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thp","download_url":"https://codeload.github.com/thp/minidb/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thp%2Fminidb/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259352656,"owners_count":22844738,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["orm","python3","sqlite3"],"created_at":"2024-10-12T01:25:56.171Z","updated_at":"2025-06-11T22:07:56.970Z","avatar_url":"https://github.com/thp.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"minidb: simple python object store\n==================================\n\n[![PyTest](https://github.com/thp/minidb/actions/workflows/pytest.yaml/badge.svg)](https://github.com/thp/minidb/actions/workflows/pytest.yaml)\n\nStore Python objects in SQLite 3. Concise, pythonic API. Fun to use.\n\n\nTutorial\n--------\n\nLet's start by importing the minidb module in Python 3:\n\n```\n\u003e\u003e\u003e import minidb\n```\n\nTo create a store in memory, we simply instantiate a minidb.Store, optionally\ntelling it to output SQL statements as debug output:\n\n```\n\u003e\u003e\u003e db = minidb.Store(debug=True)\n```\n\nIf you want to persist into a file, simply pass in a filename as the first\nparameter when creating the minidb.Store:\n\n```\n\u003e\u003e\u003e db = minidb.Store('filename.db', debug=True)\n```\n\nNote that for persisting data into the file, you actually need to call\ndb.close() to flush the changes to disk, and optionally db.commit() if you\nwant to save the changes to disk without closing the database.\n\nBy default, `minidb` executes `VACUUM` on the SQLite database on close. You\ncan opt-out of this behaviour by passing `vacuum_on_close=False` to the\n`minidb.Store` constructor. You can manually execute a `VACUUM` by calling\n`.vacuum()` on the `minidb.Store` object, this helps reduce the file size\nin case you delete many objects at once. See the\n[SQLite VACUUM docs](https://www.sqlite.org/lang_vacuum.html) for details.\n\nTo actually store objects, we need to subclass from minidb.Model (which takes\ncare of all the behind-the-scenes magic for making your class persistable, and\nadds methods for working with the database):\n\n```\n\u003e\u003e\u003e class Person(minidb.Model):\n...     name = str\n...     email = str\n...     age = int\n```\n\nEvery subclass of minidb.Model will also have a \"id\" attribute that is None if\nan instance is not stored in the database, or an automatically assigned value\nif it is in the database. This uniquely identifies an object in the database.\n\nNow it's time to register our minidb.Model subclass with the store:\n\n```\n\u003e\u003e\u003e db.register(Person)\n```\n\nThis will check if the table exists, and create the necessary structure (this\noutput appears only when debug=True is passed to minidb.Store's constructor):\n\n```\n: PRAGMA table_info(Person)\n: CREATE TABLE Person (id INTEGER PRIMARY KEY,\n                       name TEXT,\n                       email TEXT,\n                       age INTEGER)\n```\n\nNow you can create instances of your minidb.Model subclass, optionally passing\nkeyword arguments that will be used to initialize the fields:\n\n```\n\u003e\u003e\u003e p = Person(name='Hello World', email='minidb@example.com', age=99)\n\u003e\u003e\u003e p\n\u003cPerson(id=None, name='Hello World', email='minidb@example.com', age=99)\u003e\n```\n\nTo store this object in the database, use .save() on the instance with the\nstore as sole argument:\n\n```\n\u003e\u003e\u003e p.save(db)\n```\n\nIn debug mode, we will see how it stores the object in the database:\n\n```\n: INSERT INTO Person (name, email, age) VALUES (?, ?, ?)\n  ['Hello World', 'minidb@example.com', '99']\n```\n\nAlso, it will now have its \"id\" attribute assigned:\n\n```\n\u003e\u003e\u003e p\n\u003cPerson(id=1, name='Hello World', email='minidb@example.com', age=99)\u003e\n```\n\nThe instance will remember the last minidb.Store object it was saved into or\nthe minidb.Store object from which it was loaded, so you can leave it out the\nnext time you want to save the object:\n\n```\n\u003e\u003e\u003e p.name = 'Hello Again'\n\u003e\u003e\u003e p.save()\n```\n\nAgain, the store will figure out what needs to be done:\n\n```\n: UPDATE Person SET name=?, email=?, age=? WHERE id=?\n  ['Hello Again', 'minidb@example.com', '99', 1]\n```\n\nNow, let's insert some more data, just for fun:\n\n```\n\u003e\u003e\u003e for i in range(10):\n...     Person(name='Hello', email='x@example.org', age=10+i*3).save(db)\n```\n\nNow that we have some objects in the database, let's query all elements, and\nalso let's output if any of those loaded objects is the same object as p:\n\n```\n\u003e\u003e\u003e for person in Person.load(db):\n...     print(person, person is p)\n```\n\nThe SQL query that is executed by Person.load() is:\n\n```\n: SELECT id, name, email, age FROM Person\n  []\n```\n\nThe output of the load looks like this:\n\n```\n\u003cPerson(id=1, name='Hello Again', email='minidb@example.com', age=99)\u003e True\n\u003cPerson(id=2, name='Hello', email='x@example.org', age=10)\u003e False\n\u003cPerson(id=3, name='Hello', email='x@example.org', age=13)\u003e False\n\u003cPerson(id=4, name='Hello', email='x@example.org', age=16)\u003e False\n\u003cPerson(id=5, name='Hello', email='x@example.org', age=19)\u003e False\n\u003cPerson(id=6, name='Hello', email='x@example.org', age=22)\u003e False\n\u003cPerson(id=7, name='Hello', email='x@example.org', age=25)\u003e False\n\u003cPerson(id=8, name='Hello', email='x@example.org', age=28)\u003e False\n\u003cPerson(id=9, name='Hello', email='x@example.org', age=31)\u003e False\n\u003cPerson(id=10, name='Hello', email='x@example.org', age=34)\u003e False\n\u003cPerson(id=11, name='Hello', email='x@example.org', age=37)\u003e False\n```\n\nNote that the first object retrieved is actually the object p (there's no new\nobject created, it's the same). minidb caches objects as long as you have a\nreference to them around, and will be able to retrieve those objects instead.\nThis makes sure that all objects stay in sync, let's try modifying an object\nreturned by Person.get(), a function that retrieves exactly one object:\n\n```\n\u003e\u003e\u003e print(p.name)\nHello Again\n\u003e\u003e\u003e Person.get(db, id=1).name = 'Hello'\n\u003e\u003e\u003e print(p.name)\nHello\n```\n\nNow, let's try some more fancy queries. The minidb.Model subclass has a class\nattribute called \"c\" that can be used to reference to the columns/attributes:\n\n```\n\u003e\u003e\u003e Person.c\n\u003cColumns for Person (name, email, age)\u003e\n```\n\nFor example, we can query all objects for which age is between 16 and 50\n\n```\n\u003e\u003e\u003e Person.load(db, (Person.c.age \u003e= 16) \u0026 (Person.c.age \u003c= 50))\n```\n\nThis will run the following SQL query:\n\n```\n: SELECT id, name, email, age FROM Person WHERE ( age \u003e= ? ) AND ( age \u003c= ? )\n  [16, 50]\n```\n\nInstead of querying for full objects, you can also query for columns, for\nexample, we can find out the minimum and maximum age value in the table:\n\n```\n\u003e\u003e\u003e next(Person.query(db, Person.c.age.min // Person.c.age.max))\n(10, 99)\n```\n\nThe corresponding query looks like this:\n\n```\n: SELECT min(age), max(age) FROM Person\n[]\n```\n\nNote that column1 // column2 is syntactic sugar for the more verbose syntax of\nminidb.columns(column1, column2). The .query() method returns a generator of\nrows, you can get a single row via the Python built-in next(). Each row can be\naccessed in different ways:\n\n 1. As tuple (this is also the default representation when printing a row)\n 2. As dictionary\n 3. As object with attributes\n\nFor example, as a dictionary:\n\n```\n\u003e\u003e\u003e dict(next(Person.query(db, Person.c.age.min)))\n{'min(age)': 10}\n```\n\nIf you want to have nicer names, you can give your result columns names:\n\n```\n\u003e\u003e\u003e dict(next(Person.query(db, Person.c.age.min('minimum_age'))))\n{'minimum_age': 10}\n```\n\nThe generated SQL query for renaming looks like this:\n\n```\n: SELECT min(age) AS minimum_age FROM Person\n  []\n```\n\nAnd of course, you can access the column using attribute access:\n\n```\n\u003e\u003e\u003e next(Person.query(db, Person.c.age.min('minimum_age'))).minimum_age\n10\n```\n\nThere is also support for SQL's ORDER BY, GROUP_BY and LIMIT, as optional\nkeyword arguments to .query():\n\n```\n\u003e\u003e\u003e list(Person.query(db, Person.c.name // Person.c.age,\n...                   order_by=Person.c.age.desc, limit=5))\n```\n\nTo save typing, you can do:\n\n```\n\u003e\u003e\u003e Person.c.name.query(db)\n\n\u003e\u003e\u003e (Person.c.name // Person.c.email).query(db)\n\n\u003e\u003e\u003e (Person.c.name // Person.c.age).query(db, order_by=lamdba c: c.age.desc)\n\n\u003e\u003e\u003e Person.query(db, lambda c: c.name // c.email)\n```\n\nSee [`example.py`](example.py) for more examples.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthp%2Fminidb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthp%2Fminidb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthp%2Fminidb/lists"}