{"id":15628939,"url":"https://github.com/pwwang/pymedoo","last_synced_at":"2025-04-29T09:55:53.350Z","repository":{"id":57440624,"uuid":"127347406","full_name":"pwwang/pymedoo","owner":"pwwang","description":"A lightweight database framework for python","archived":false,"fork":false,"pushed_at":"2022-11-11T06:48:58.000Z","size":204,"stargazers_count":15,"open_issues_count":0,"forks_count":4,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-29T09:55:39.836Z","etag":null,"topics":["database-framework","lightweight-database-framework","python"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pwwang.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}},"created_at":"2018-03-29T21:06:13.000Z","updated_at":"2023-11-08T09:16:29.000Z","dependencies_parsed_at":"2023-01-21T15:46:46.885Z","dependency_job_id":null,"html_url":"https://github.com/pwwang/pymedoo","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pwwang%2Fpymedoo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pwwang%2Fpymedoo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pwwang%2Fpymedoo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pwwang%2Fpymedoo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pwwang","download_url":"https://codeload.github.com/pwwang/pymedoo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251480069,"owners_count":21596016,"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":["database-framework","lightweight-database-framework","python"],"created_at":"2024-10-03T10:24:54.127Z","updated_at":"2025-04-29T09:55:53.329Z","avatar_url":"https://github.com/pwwang.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pymedoo - A lightweight database framework for python\n\nit's inspired by [Medoo][1] for PHP and [Records][7] for python.\n\n[![Pypi][8]][15]\n[![Github][9]][16]\n[![Codacy][10]][17]\n[![Codacy coverage][11]][17]\n![Building][12]\n\n## Install\n\n```bash\npip install medoo\n```\n\n## Required packages for databases\n\n| Database | Package   | Install |\n|----------|---------  | --------|\n| sqlite   | [sqlite3][2]   | `pip install medoo[sqlite]` |\n| mysql    | ~~[pymysql][3]~~(dropped) Use [mysql.connector][13] instead. See [#6][14] | `pip install medoo[mysql]` |\n| pgsql    | [psycopg2][4]  | `pip install medoo[pgsql]` |\n| mssql    | [pymssql][5]   | `pip install medoo[mssql]` |\n| oracle   | [cx_Oracle][6] | `pip install medoo[oracle]` |\n\nInstall forr all supported databases:\n\n```bash\npip install medoo[all]\n```\n\n## Get started\n\n### SELECT\n\n```python\nfrom medoo import Medoo\n\n# For other arguments, please refer to the original connect function of each client.\nme = Medoo(dbtype = 'sqlite', database = 'file:///path/to/test.sqlite')\n\n# SELECT * FROM \"Customers\"\nrs = me.select('Customers')\n\nprint(rs.export('csv', delimiter = '\\t'))\n```\n\n|CustomerID|CustomerName|ContactName|Address|City|PostalCode|Country|\n|-|-|-|-|-|-|-|\n|1|Alfreds Futterkiste|Maria Anders|Obere Str. 57|Berlin|12209|Germany|\n|2|Ana Trujillo Emparedados y helados|Ana Trujillo|Avda. de la Constitución 2222|México D.F.|5021|Mexico|\n|3|Antonio Moreno Taquería|Antonio Moreno|Mataderos 2312|México D.F.|5023|Mexico|\n|4|Around the Horn|Thomas Hardy|120 Hanover Sq.|London|WA1 1DP|UK|\n|5|Berglunds snabbköp|Christina Berglund|Berguvsvägen 8|Luleå|S-958 22|Sweden|\n\n```python\n# SELECT \"CustomerID\",\"CustomerName\" FROM \"Customers\"\nme.select('Customers', 'CustomerID, CustomerName')\nme.select('Customers', ['CustomerID', 'CustomerName'])\n\n# SELECT \"C\".\"CustomerID\" AS \"CustomerID\",\"C\".\"CustomerName\" AS \"name\" FROM \"Customers\" AS \"C\"\nme.select('Customers(C)', ['C.CustomerID(id)', 'C.CustomerName(name)'])\n\n# SELECT DISTINCT \"Country\" FROM \"Customers\"\nme.select('Customers', 'Country', distinct = True)\n\n# SELECT COUNT(\"CustomerID\") FROM \"Customers\"\nme.select('Customers', 'CustomerID|COUNT')\n\n# SELECT COUNT(DISTINCT \"CustomerID\") AS \"c\" FROM \"Customers\"\nme.select('Customers', 'CustomerID|.COUNT(c)')\n\n# SELECT \"CustomerID\"+1 FROM \"Customers\"\nfrom medoo import Field, Raw\nme.select('Customers', Field('CustomerID')+1)\n\n# SELECT 'Name: ' || CustomerName AS name FROM \"Customers\"\nrs = me.select('Customers', Raw(\"'Name: ' || CustomerName AS name\"))\nfor r in rs: print(r.name)\n```\n\n```\nName: Alfreds Futterkiste\nName: Ana Trujillo Emparedados y helados\nName: Antonio Moreno Taquería\nName: Around the Horn\nName: Berglunds snabbköp\n```\n\n### WHERE\n\n#### Single condition\n\n```python\n# SELECT * FROM \"Customers\" WHERE \"CustomerID\" = 1\nme.select('Customers', where = {'CustomerID': 1})\n\n# SELECT * FROM \"Customers\" WHERE \"CustomerID\" \u003c 3\nme.select('Customers', where = {'CustomerID[\u003c]': 3})\n\n# SELECT * FROM \"Customers\" WHERE \"CustomerID\" IN (1,2,3)\nme.select('Customers', where = {'CustomerID': (1,2,3)})\n\n# SELECT * FROM \"Customers\" WHERE \"CustomerName\" LIKE '%b%' OR \"CustomerName\" LIKE '%c%'\nme.select('Customers', where = {'CustomerName[~]': ('a', 'b')})\n\n# SELECT * FROM \"Customers\" WHERE \"CustomerID\" BETWEEN 1 AND 3\nme.select('Customers', where = {'CustomerID[\u003c\u003e]': (1,3)})\n\n# SELECT * FROM \"Customers\" WHERE NOT \"CustomerID\" BETWEEN 1 AND 3\nme.select('Customers', where = {'!CustomerID[\u003c\u003e]': (1,3)})\n\n# SELECT * FROM \"Customers\" WHERE \"CustomerID\" IS NULL\nme.select('Customers', where = {'CustomerID[is]': None}) # where = {'id[==]': None}\n\n# SELECT * FROM \"Customers\" WHERE INSTR(\"CustomerName\", 'Antonio')\nme.select('Customers', where = {Raw('INSTR(\"CustomerName\", \\'Antonio\\')'):None})\n```\n\n#### Compond\n\n```python\n# SELECT * FROM \"Customers\" WHERE \"CustomerID\" IN (1,2,3) AND \"CustomerName\" LIKE '%b%'\nme.select('Customers', where = {\n    'CustomerID': (1,2,3),\n    'CustomerName[~]': 'b'\n})\n# SELECT * FROM \"Customers\"\n# WHERE (\"CustomerID\" IN (1,2,3) AND \"CustomerName\" LIKE '%b%') AND\n# (\"CustomerName\" = 'cd' OR \"CustomerID\" = 2) AND\n# (\"CustomerID\" \u003c 3 AND NOT \"CustomerName\" = 'bc')\nme.select('Customers', where = {\n    'AND': {\n        'CustomerID': (1,2,3),\n        'CustomerName[~]': 'b'\n    },\n    'OR': {\n        'CustomerName': 'cd',\n        'CustomerID': 2\n    },\n    # you can use comment to distinguish multiple ANDs and ORs\n    'AND #2': {\n        'CustomerID[\u003c]': 3,\n        '!CustomerName': 'bc'\n    }\n})\n```\n\n#### Modifier\n\n```python\n# SELECT * FROM \"Customers\" ORDER BY \"CustomerID\" DESC, \"CustomerName\" ASC LIMIT 2 OFFSET 1\n# MSSQL:\n# SELECT * FROM \"Customers\" ORDER BY \"CustomerID\" DESC, \"CustomerName\" ASC\n# OFFSET 1 ROWS FETCH NEXT 2 ROWS ONLY\nme.select('Customers', where = {\n    'ORDER': {'CustomerID': 'desc', 'CustomerName': 'asc'},\n    'LIMIT': (2, 1)\n})\n\n# SELECT COUNT(\"CustomerID\") AS \"c\",\"CustomerName\" FROM \"Customers\" GROUP BY \"Country\" HAVING \"CustomerID\" \u003e 1\nme.select('Customers', 'CustomerID|count(c), CustomerName', where = {\n    'GROUP': 'Country',\n    'HAVING': {'CustomerID[\u003e]': 1}\n})\n```\n\n### Using subquery\n\n```python\nprint(me.select('Orders').export('csv', delimiter = '\\t'))\n```\n\n|OrderID|CustomerID|OrderDate|\n|-|-|-|\n|10308|2|1996-09-18|\n|10309|37|1996-09-19|\n|10310|77|1996-09-20|\n\n```python\n# SELECT * FROM \"Customers\" AS \"C\",(SELECT \"CustomerID\" FROM \"Orders\") AS \"O\"\n#   WHERE \"C\".\"CustomerID\" = \"O\".\"CustomerID\"\nme.select([\n    'Customers(C)', # the first table\n    me.builder.select('Orders', 'CustomerID', sub = 'O')\n], where = {\n    'C.CustomerID': Field('O.CustomerID')\n})\n\n# SELECT * FROM \"Customers\" WHERE \"CustomerID\" IN (SELECT \"CustomerID\" FROM \"Orders\")\nme.select('Customers', where = {\n    'CustomerID': me.builder.select('Orders', 'CustomerID')\n})\n```\n\n### JOIN\n\n```python\n# SELECT \"O\".\"OrderID\",\"C\".\"CustomerName\",\"O\".\"OrderDate\" FROM \"Orders\" AS \"O\"\n#   INNER JOIN \"Customers\" AS \"C\" ON \"C\".\"CustomerID\"=\"O\".\"CustomerID\"\nme.select('Orders(O)', 'O.OrderID,C.CustomerName,O.OrderDate', join = {\n    'Customers(C)': 'CustomerID'\n})\n\n# equivalent to\nme.select('Orders(O)', 'O.OrderID,C.CustomerName,O.OrderDate', join = {\n    '[\u003e\u003c]Customers(C)': 'CustomerID'\n})\n# [\u003e] LEFT JOIN, [\u003c] RIGHT JOIN [\u003c\u003e] FULL OUTER JOIN\n\n# Join on multiple columns (same in different tables)\n# join = { '[\u003e\u003c]Customers(C)': ['CustomerID', 'OtherColumn'] }\n\n# Join on different columns: JOIN \"Customers\" AS \"C\" ON \"C\".\"CustomerID\"=\"O\".\"OtherID\"\n# join = { '[\u003e\u003c]Customers(C)': {'CustomerID', 'OtherID'} }\n\n# You can join multiple tables, use OrderedDict if you want to keep the order.\n```\n\n### UNION\n\n```python\n# SELECT \"CustomerID\" FROM \"Customers\" UNION SELECT \"CustomerID\" FROM \"Orders\"\nme.union(\n    me.builder.select('Customers', 'CustomerID'),\n    me.builder.select('Orders', 'CustomerID')\n)\n\n# SELECT \"CustomerID\" FROM \"Customers\" UNION ALL SELECT \"CustomerID\" FROM \"Orders\"\nme.union(\n    me.builder.select('Customers', 'CustomerID'),\n    me.builder.select('Orders', 'CustomerID', sub = True)\n)\n```\n\n### Records\n\n`Medoo.select` and `Medoo.union` return a collection of records, which is basically a generator, but you can still get items from it, as it will consume the generate if necessary. The idea is borrowed from [Records][7].\n\n```python\nrecords = me.select('Customers', 'CustomerID(id)')\nrecord  = records.first() # \u003cRecord {'id': 1}\u003e\n\n# equivalent to\nrecord  = records[0]\n\n# you may also select other rows: records[1], records[2]\n# or return all rows:\nprint(records.all())\n\n# you can also export the records\n# this is the courtesy from tablib (https://github.com/kennethreitz/tablib)\n# check the kwargs with its documentation\nprint(records.export('csv', delimiter = '\\t'))\n\n# You can also apply tablib's other function on the data:\n# records.tldata.\u003cfunction\u003e(\u003cargs\u003e)\n\n# to get the value of each field from a record:\nprint(record[0]) # 1\nprint(record['id']) # 1\nprint(record.id) # 1\nprint(record.as_dict()) # {'id': 1}\n```\n\n### INSERT\n\n```python\n# INSERT INTO \"Orders\" (\"OrderID\",\"CustomerID\",\"OrderDate\") VALUES (1,2,'1999-09-09'),(2,8,'2001-10-12')\nme.insert(\n    'Orders', # table\n    'OrderID, CustomerID, OrderDate', # fields\n    (1,2,'1999-09-09'), # values\n    (2,8,'2001-10-12')\n    # ...\n)\n# get the last insert row id\nprint(me.id()) # 5\n\n# INSERT INTO \"Orders\" (\"OrderID\",\"CustomerID\",\"OrderDate\") VALUES (1,2,'1999-09-09'),(2,8,'2001-10,12')\nme.insert(\n    'Orders', # table\n    {'OrderID': 1, 'CustomerID': 2, 'OrderDate': '1999-09-09'}, # fields with the first value\n    (2,8,'2001-10-12')\n    # ...\n)\nme.insert(\n    'Orders', # table\n    {'OrderID': 1, 'CustomerID': 2, 'OrderDate': '1999-09-09'}, # fields with the first value\n    {'OrderID': 2, 'CustomerID': 8, 'OrderDate': '2001-10-12'}  # specify the fields as well\n    # ...\n)\n# Or if your values have all the fields\n# INSERT INTO \"Orders\" VALUES (1,2,'1999-09-09'),(2,8,'2001-10-12')\nme.insert(\n    'Orders', # table\n    (1,2,'1999-09-09')\n    (2,8,'2001-10-12')\n    # ...\n)\n\n# You may hold the changes until all data inserted\nme.insert(..., commit = False)\nme.insert(..., commit = False)\nme.insert(..., commit = False)\nme.insert(..., commit = False)\nme.commit()\n# This applies with UPDATE and DELETE as well.\n```\n\n### UPDATE\n\n```python\n# UPDATE \"Orders\" SET \"CustomerID\"=10 WHERE \"OrderID\" = 2\nme.update(\n    'Orders', # table\n    data  = {'CustomerID': 10},\n    where = {'OrderID': 2}\n)\n# UPDATE \"Orders\" SET \"CustomerID\"=\"CustomerID\"+1 WHERE \"OrderID\" = 2\nme.update(\n    'Orders', # table\n    data  = {'CustomerID[+]': 1},\n    where = {'OrderID': 2}\n)\n```\n\n### DELETE\n\n```python\n# DELETE FROM \"Orders\" WHERE \"OrderID\" = 2\nme.delete('Orders', where = {'OrderID': 2})\n```\n\n### Other functions of `Medoo`\n\n```python\n# Fetch a single value\nme.get('Customers', 'CustomerID', where = {'CustomerName': 'Around the Horn'}) # == 1\n\n# Check if a record exists\nme.has('Customers', where = {'CustomerID': 10}) # == False\n\n# Return the last query\nme.last() # SELECT * FROM \"Customers\" WHERE \"CustomerID\" = 10\n\n# Show all the queries bound with `me`\n\n# You have to passing `logging = True` to `Medoo(..., logging = True)`\nme.log()\n\n# Return the errors\nme.error()\n\n# Submit an SQL query\nme.query(sql, commit = True)\n```\n\n### Extending `pymedoo`\n\n`pymedoo` is highly extendable, including the operators in `WHERE` conditions and `UPDATE SET` clause, `JOIN` operators, and some functions such as how to quote the table names, field names and values. All of these have been defined with `Dialect` class, what you need to do is just extend this class and specify it to the `Medoo` instance.\nFor example, let's define a case-insensitive `LIKE` operator using a shortcut `~~`:\n\n```python\nfrom medoo import Medoo, Dialect\n\nclass MyDialect(Dialect):\n    OPERATOR_MAP = {\n        '~~': 'ilike'\n    }\n\n    @classmethod\n    def ilike(klass, field, value):\n        # support single value\n        if not isinstance(value, list):\n            value = [value]\n\n        terms = [\n            \"UPPER({}) LIKE UPPER({})\".format(field, klass.value(v)) # quote the value\n            for v in value\n        ]\n        # use OR to connect\n        return ' OR '.join(terms)\n\n# tell medoo to use this dialect\nme = Medoo(...)\nme.dialect(MyDialect)\n\n# SELECT * FROM \"Customers\" WHERE UPPER(\"CustomerName\") LIKE UPPER('%an%')\nrecords = me.select('Customers', where = {\n    'CustomerName[~~]': '%an%'\n})\nprint(records.export('csv', delimiter = '\\t'))\n```\n\n|CustomerID|CustomerName|ContactName|Address|City|PostalCode|Country|\n|-|-|-|-|-|-|-|\n|2|Ana Trujillo Emparedados y helados|Ana Trujillo|Avda. de la Constitución 2222|México D.F.|5021|Mexico|\n|3|Antonio Moreno Taquería|Antonio Moreno|Mataderos 2312|México D.F.|5023|Mexico|\n\n[1]: https://medoo.in/\n[2]: https://docs.python.org/2/library/sqlite3.html\n[3]: https://github.com/PyMySQL/PyMySQL\n[4]: http://initd.org/psycopg/docs/\n[5]: http://www.pymssql.org/en/stable/\n[6]: https://oracle.github.io/python-cx_Oracle/\n[7]: https://github.com/kennethreitz/records\n[8]: https://img.shields.io/pypi/v/medoo.svg?style=flat-square\n[9]: https://img.shields.io/github/tag/pwwang/pymedoo.svg?style=flat-square\n[10]: https://img.shields.io/codacy/grade/c049a2bf5ca84484b885cfbfedc27eab.svg?style=flat-square\n[11]: https://img.shields.io/codacy/coverage/c049a2bf5ca84484b885cfbfedc27eab.svg?style=flat-square\n[12]: https://img.shields.io/github/workflow/status/pwwang/pymedoo/Build%20and%20Deploy?style=flat-square\n[13]: https://dev.mysql.com/doc/connector-python/en/\n[14]: https://github.com/pwwang/pymedoo/issues/6\n[15]: https://pypi.org/project/pymedoo/\n[16]: https://github.com/pwwang/pymedoo\n[17]: https://app.codacy.com/gh/pwwang/pymedoo\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpwwang%2Fpymedoo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpwwang%2Fpymedoo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpwwang%2Fpymedoo/lists"}