diff --git a/README.md b/README.md index 1dc8a66..883f264 100644 --- a/README.md +++ b/README.md @@ -13,28 +13,115 @@ $ pip install loaf -## Sample Demo +## Examples + +### Importing Into Your Project ```python -import loaf +from loaf import Loaf +``` + + + +### Setting Up Credentials +```python # Setup your credentials with a single line. -loaf.bake(port=6969, db="pizzeria") +loaf = Loaf(port=6969, db="pizzeria") +# Or load your credentials from a file. +loaf = Loaf(file="creds.ini") +# Or use a local SQLite file instead. +loaf = Loaf(file="pizzeria.db") +``` + + + +### Executing Queries + +```python +# Make queries easily. +toppings = loaf.query("SELECT * from toppings") +# Load your quieries directly from files. +clients = loaf.query(file="getHappyClients.sql") +# Prevent disasters by executing multiple queries. +pepperoni_id, client_name = loaf.multi([ + "SELECT id FROM toppings WHERE name='Pepperoni'", + "SELECT name FROM clients WHERE id=6" +]) +``` + + + +### Printing + +```python +# Display info using built-in tables! +loaf.print(pepperoni_id) +loaf.print(client_name) +loaf.print(toppings) +``` + +```powershell +┏━━━━┓ +┃ id ┃ +┡━━━━┩ +│ 1 │ +└────┘ +┏━━━━━━━━━━━┓ +┃ name ┃ +┡━━━━━━━━━━━┩ +│ 'Alfonso' │ +└───────────┘ +┏━━━━┳━━━━━━━━━━━━━┳━━━━━━━┓ +┃ id ┃ name ┃ price ┃ +┡━━━━╇━━━━━━━━━━━━━╇━━━━━━━┩ +│ 1 │ 'Pepperoni' │ 1.49 │ +│ 2 │ 'Mushrooms' │ 1.99 │ +│ 3 │ 'Onions' │ 0.99 │ +└────┴─────────────┴───────┘ +``` + + + +### Data Manipulation + +```python +# Manipulate your data with dictionaries, as God intended. +for topping in toppings: + print(topping['name']) +``` + +````powershell +Pepperoni +Mushrooms +Onions +```` + + -# Make a query easily. -result = loaf.query("SELECT * from toppings") -print(result) +### Utilities +```python # Not lazy enough? Try some of the pre-built queires. +# Equivalent of: SELECT name FROM client WHERE name='Marco' LIMIT 1 +result = loaf.select("name", "clients", "name='Marco'", limit=1) +# Get all values from a table. result = loaf.all("toppings") -print(result) - # Got stored procedures? No problemo! result = loaf.call("ProcedureFindClient", 1) -print(result) ``` ![](https://github.com/PoshoDev/Loaf/blob/main/loaf.png?raw=true) + + +``` +⚠️ Syntax for the package has changed heavily since version 0.2.0, if your project depends on Loaf and is using an inferior version, I heavily suggest that you use the previous stable version: +``` + +``` +$ pip install loaf==0.1.30 +``` + diff --git a/loaf/__init__.py b/loaf/__init__.py index e522eeb..51b76e2 100644 --- a/loaf/__init__.py +++ b/loaf/__init__.py @@ -1,10 +1,8 @@ -# A Python module for effortless database usage. - import pymysql, psycopg2, psycopg2.extras, sqlite3, datetime, socket, configparser from rich.table import Table from rich import print as rprint -cursors = ["DEFAULT", "DICTIONARY"] +cursors = ["DICTIONARY", "DEFAULT"] modes = ["MySQL", "PostgreSQL", "SQLite"] defaults = { "host": socket.gethostbyname(socket.gethostname()), @@ -176,32 +174,25 @@ def call(self, procedure, args=[], rollback_on_error=None): # Prints the result of a query as a rich table. The 'data' argument can be a tuple or a dictionary. def print(self, data, title=None): - # If the data is a dictionary, convert it to a tuple. if type(data) == dict: data = tuple(data.items()) - # If the data is a tuple, convert it to a list. This is done because the Rich library doesn't support tuples. if type(data) == tuple: data = list(data) - # If the data is a list, convert it to a table. if type(data) == list: table = Table(title=title) - # If the data is a list of tuples, convert it to a table. if type(data[0]) == tuple: for i in range(len(data[0])): table.add_column(data[0][i]) for i in range(1, len(data)): table.add_row(*data[i]) - # If the data is a list of dictionaries, convert it to a table. elif type(data[0]) == dict: for i in data[0].keys(): table.add_column(i) for i in data: - # Parse the values to strings. Then add the rows. values = [] for j in i.values(): - values.append(parse(j)) + values.append(tParse(j)) table.add_row(*values) - # Print the table. rprint(table) else: raise Exception("Invalid data type.") @@ -326,3 +317,13 @@ def parse(value): if isinstance(value, datetime.date): return value.strftime("%Y-%m-%d") return "'" + str(value).replace("'", "''") + "'" + +# Parses a value to a colored string to be used in tables. +def tParse(value): + if value in [None, "", "NULL"]: + return "[dim]NULL[/]" + if isinstance(value, int): + return "[magenta]" + str(value) + "[/]" + if isinstance(value, datetime.date): + return "[cyan]" + value.strftime("%Y-%m-%d") + "[/]" + return "[green]'" + str(value).replace("'", "''") + "'[/]" \ No newline at end of file diff --git a/loaf/__init__OLD.py b/loaf/__init__OLD.py deleted file mode 100644 index dcc6e91..0000000 --- a/loaf/__init__OLD.py +++ /dev/null @@ -1,138 +0,0 @@ -import pymysql, psycopg2, psycopg2.extras, datetime, socket, configparser - -host_ = socket.gethostbyname(socket.gethostname()) -port_ = 80 # Default XAMPP Apache server port. -user_ = "root" -pasw_ = "" -db_ = None -curs_ = "DEFAULT" -mode_ = "MySQL" - -# Make this differently, for the love of god! -def bake(host=host_, port=port_, user=user_, pasw=pasw_, db=db_, cursor=curs_, - mode=mode_, file=None): - global host_, port_, user_, pasw_, db_, curs_, mode_ - if file is None: - if host != "": host_=host - if port != "": port_=port - if user != "": user_=user - if pasw != "": pasw_=pasw - if db != "": db_=db - if cursor != "": curs_=cursor - if mode != "": mode_=mode - else: - config = configparser.ConfigParser() - config.read(file) - sect = "DATABASE" - section = config[sect] - if config.has_option(sect, "host"): host_ = section["host"] - if config.has_option(sect, "port"): port_ = int(section["port"]) - if config.has_option(sect, "user"): user_ = section["user"] - if config.has_option(sect, "pasw"): pasw_ = section["pasw"] - if config.has_option(sect, "db"): db_ = section["db"] - if config.has_option(sect, "curs"): curs_ = section["curs"] - if config.has_option(sect, "mode"): mode_ = section["mode"] - -# A query. -def query(query): - conn = get_connection() - conn_object = get_connection_object(conn) - conn_object.execute(query) - if not mode_=="PostgreSQL" or conn_object.pgresult_ptr is not None: - response = conn_object.fetchall() - else: - response = None - conn.commit() - conn.close() - return response - -# A way to use multiple queries. -def multi(queries): - pass - -def get_connection(): - if (mode_ == "PostgreSQL"): - return psycopg2.connect(host=host_, port=port_, user=user_, - password=pasw_, database=db_) - else: # MySQL (by Default) - return pymysql.connect(host=host_, port=port_, user=user_, - passwd=pasw_, db=db_) - -def get_connection_object(conn): - # PostgreSQL - if (mode_ == "PostgreSQL"): - if curs_ == "DICTIONARY": - return conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) - else: - return conn.cursor() - # MySQL (by Default) - else: - if curs_ == "DICTIONARY": - return conn.cursor(pymysql.cursors.DictCursor) - else: - return conn.cursor() - -# Test your connection with your database. -def test(): - try: - get_connection() - print(f"Successful connection at: {host_}") - except Exception as ex: - print(f"Connection error at: {host_}") - print(ex) - -# Call a stored procedure. -def call(func, *args): - call = "CALL " + func + "(" - if len(args) > 0: - for i in range(len(args)): - call += (str(args[i]) if type(args[i])==type(1) else parseNull(args[i])) + (", " if i