Skip to content

Commit

Permalink
make version2 - postgresql support & ordering and more fields
Browse files Browse the repository at this point in the history
  • Loading branch information
ProdByGodfather committed Aug 19, 2024
1 parent c5246d7 commit c05267a
Show file tree
Hide file tree
Showing 15 changed files with 217 additions and 121 deletions.
24 changes: 18 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# abarorm

| ![abarorm Logo](https://prodbygodfather.github.io/abarorm/images/logo.png) | **abarorm** is a lightweight and easy-to-use Object-Relational Mapping (ORM) library for both SQLite and MySQL databases in Python. It provides a simple and intuitive interface for managing database models and interactions. |
| ![abarorm Logo](https://prodbygodfather.github.io/abarorm/images/logo.png) | **abarorm** is a lightweight and easy-to-use Object-Relational Mapping (ORM) library for SQLite, MySQL, and PostgreSQL databases in Python. It provides a simple and intuitive interface for managing database models and interactions. |
|----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

## Features
Expand All @@ -11,6 +11,9 @@
- Foreign key relationships
- Custom field types with validation and constraints
- **New in v1.0.0**: Automatic table creation and updates without needing explicit `create_table()` calls
- **New in v2.0.0**: Added support for PostgreSQL databases
- **New in v2.0.0**: Ordering by fields on `all()` method


## Installation

Expand All @@ -20,19 +23,27 @@ You can install **abarorm** from PyPI using pip:
pip install abarorm
```
For MySQL support, you also need to install `mysql-connector-python`:
```python

```bash
pip install mysql-connector-python
```
For PostgreSQL support, you need to install `psycopg2-binary`:
```bash
pip install psycopg2-binary
```


## Basic Usage
Here’s a quick overview of how to use **abarorm** to define models and interact with an SQLite or MySQL database.

## Documentation
For detailed documentation, examples, and advanced usage, please visit the [official abarorm documentation website](https://prodbygodfather.github.io/abarorm/).

## Version 1.0.0 Notes
**Automatic Table Management:** Tables are now automatically created or updated based on model definitions without manual intervention.
**Important for Developers:** During development, when adding new fields to models, they will default to `NULL`. It's recommended to recreate the database schema after development is complete to ensure fields have appropriate constraints and default values.
## Version 2.0.0 Notes
PostgreSQL Support: abarorm now supports PostgreSQL databases in addition to SQLite and MySQL.
Automatic Table Management: Tables are created or updated automatically based on model definitions without manual intervention.
Important for Developers: When adding new fields to models, they will default to `NULL`. It’s recommended to recreate the database schema after development is complete to ensure fields have appropriate constraints and default values.


## Contributing
Contributions are welcome! If you find any issues or have suggestions for improvements, please open an issue or submit a pull request on GitHub.
Expand All @@ -43,4 +54,5 @@ This project is licensed under the Apache-2.0 [License](https://github.com/ProdB
## Acknowledgements
**Python:** The language used for this project
**SQLite & MySQL:** The databases supported by this project
**setuptools:** The tool used for packaging and distributing the library
**setuptools:** The tool used for packaging and distributing the library
**psycopg2-binary:** The PostgreSQL adapter used for connecting to PostgreSQL databases
36 changes: 24 additions & 12 deletions abarorm.egg-info/PKG-INFO
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
Metadata-Version: 2.1
Name: abarorm
Version: 1.0.0
Summary: A simple ORM library
Version: 2.0.0
Summary: abarorm is a lightweight and easy-to-use Object-Relational Mapping (ORM) library for SQLite & PostgreSQL and MySQL databases in Python. It aims to provide a simple and intuitive interface for managing database models and interactions.
Home-page: https://github.com/prodbygodfather/abarorm
Author: Mahdi Ghasemi
Author-email: prodbygodfather@gmail.com
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.6
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: mysql-connector-python
Provides-Extra: mysql
Requires-Dist: mysql-connector-python>=8.0.0; extra == "mysql"
Provides-Extra: postgresql
Requires-Dist: psycopg2-binary>=2.9.0; extra == "postgresql"

# abarorm

| ![abarorm Logo](https://prodbygodfather.github.io/abarorm/images/logo.png) | **abarorm** is a lightweight and easy-to-use Object-Relational Mapping (ORM) library for both SQLite and MySQL databases in Python. It provides a simple and intuitive interface for managing database models and interactions. |
| ![abarorm Logo](https://prodbygodfather.github.io/abarorm/images/logo.png) | **abarorm** is a lightweight and easy-to-use Object-Relational Mapping (ORM) library for SQLite, MySQL, and PostgreSQL databases in Python. It provides a simple and intuitive interface for managing database models and interactions. |
|----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

## Features
Expand All @@ -33,6 +33,9 @@ Requires-Dist: mysql-connector-python
- Foreign key relationships
- Custom field types with validation and constraints
- **New in v1.0.0**: Automatic table creation and updates without needing explicit `create_table()` calls
- **New in v2.0.0**: Added support for PostgreSQL databases
- **New in v2.0.0**: Ordering by fields on `all()` method


## Installation

Expand All @@ -42,19 +45,27 @@ You can install **abarorm** from PyPI using pip:
pip install abarorm
```
For MySQL support, you also need to install `mysql-connector-python`:
```python

```bash
pip install mysql-connector-python
```
For PostgreSQL support, you need to install `psycopg2-binary`:
```bash
pip install psycopg2-binary
```


## Basic Usage
Here’s a quick overview of how to use **abarorm** to define models and interact with an SQLite or MySQL database.

## Documentation
For detailed documentation, examples, and advanced usage, please visit the [official abarorm documentation website](https://prodbygodfather.github.io/abarorm/).

## Version 1.0.0 Notes
**Automatic Table Management:** Tables are now automatically created or updated based on model definitions without manual intervention.
**Important for Developers:** During development, when adding new fields to models, they will default to `NULL`. It's recommended to recreate the database schema after development is complete to ensure fields have appropriate constraints and default values.
## Version 2.0.0 Notes
PostgreSQL Support: abarorm now supports PostgreSQL databases in addition to SQLite and MySQL.
Automatic Table Management: Tables are created or updated automatically based on model definitions without manual intervention.
Important for Developers: When adding new fields to models, they will default to `NULL`. It’s recommended to recreate the database schema after development is complete to ensure fields have appropriate constraints and default values.


## Contributing
Contributions are welcome! If you find any issues or have suggestions for improvements, please open an issue or submit a pull request on GitHub.
Expand All @@ -66,3 +77,4 @@ This project is licensed under the Apache-2.0 [License](https://github.com/ProdB
**Python:** The language used for this project
**SQLite & MySQL:** The databases supported by this project
**setuptools:** The tool used for packaging and distributing the library
**psycopg2-binary:** The PostgreSQL adapter used for connecting to PostgreSQL databases
1 change: 1 addition & 0 deletions abarorm.egg-info/SOURCES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ abarorm/__init__.py
abarorm/fields.py
abarorm/mysql.py
abarorm/orm.py
abarorm/psql.py
abarorm.egg-info/PKG-INFO
abarorm.egg-info/SOURCES.txt
abarorm.egg-info/dependency_links.txt
Expand Down
7 changes: 6 additions & 1 deletion abarorm.egg-info/requires.txt
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
mysql-connector-python

[mysql]
mysql-connector-python>=8.0.0

[postgresql]
psycopg2-binary>=2.9.0
4 changes: 3 additions & 1 deletion abarorm/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
__all__ = [
'SQLiteModel',
'MySQLModel'
'MySQLModel',
'PostgreSQLModel'
]

from .orm import SQLiteModel
from .mysql import MySQLModel
from .psql import PostgreSQLModel
34 changes: 33 additions & 1 deletion abarorm/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,45 @@ def __init__(self, default: bool = False, **kwargs):
super().__init__(field_type='BOOLEAN', default=default, **kwargs)

class DateTimeField(Field):
def __init__(self, auto_now: bool = False, **kwargs):
def __init__(self, auto_now: bool = False, auto_now_add: Optional[bool] = None, **kwargs):
super().__init__(field_type='DATETIME', **kwargs)
self.auto_now = auto_now
self.auto_now_add = auto_now_add

class DateField(Field):
def __init__(self, auto_now: bool = False, auto_now_add: Optional[bool] = None, **kwargs):
super().__init__(field_type='DATE', **kwargs)
self.auto_now = auto_now
self.auto_now_add = auto_now_add

class TimeField(Field):
def __init__(self, **kwargs):
super().__init__(field_type='TIME', **kwargs)

class ForeignKey(Field):
def __init__(self, to: Type['BaseModel'], on_delete: str = 'CASCADE', **kwargs):
super().__init__(field_type='INTEGER', **kwargs)
self.to = to
self.on_delete = on_delete

class FloatField(Field):
def __init__(self, **kwargs):
super().__init__(field_type='FLOAT', **kwargs)

class DecimalField(Field):
def __init__(self, max_digits: int, decimal_places: int, **kwargs):
super().__init__(field_type='DECIMAL', **kwargs)
self.max_digits = max_digits
self.decimal_places = decimal_places

class TextField(Field):
def __init__(self, **kwargs):
super().__init__(field_type='TEXT', **kwargs)

class EmailField(CharField):
def __init__(self, **kwargs):
super().__init__(max_length=254, **kwargs)

class URLField(CharField):
def __init__(self, **kwargs):
super().__init__(max_length=1000, **kwargs)
39 changes: 28 additions & 11 deletions abarorm/mysql.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import mysql.connector
from typing import List, Optional, Dict
from typing import List, Optional, Dict, Type, Union
import datetime
from .fields import Field, DateTimeField
from .fields import Field, DateTimeField, DecimalField, TimeField, DateField

class ModelMeta(type):
def __new__(cls, name, bases, dct):
new_cls = super().__new__(cls, name, bases, dct)
if 'table_name' in dct and dct['table_name']: # اگر table_name تعریف شده بود
new_cls.create_table() # به صورت اتوماتیک جدول ایجاد می‌شود
if 'table_name' in dct and dct['table_name']: # Check if table_name is defined
new_cls.create_table() # Automatically create the table
return new_cls

class BaseModel(metaclass=ModelMeta):
Expand All @@ -19,18 +19,18 @@ def __init__(self, **kwargs):

@classmethod
def connect(cls):
raise NotImplementedError("متد Connect باید پیاده‌سازی شود.")
raise NotImplementedError("Connect method must be implemented.")

@classmethod
def create_table(cls):
conn = cls.connect()
cursor = conn.cursor()
columns = cls._get_column_definitions(cursor)

# ایجاد جدول اگر وجود ندارد
# Create table if it does not exist
cursor.execute(f"CREATE TABLE IF NOT EXISTS {cls.table_name} (id INT AUTO_INCREMENT PRIMARY KEY, {', '.join(columns)})")

# به روزرسانی ساختار جدول در صورت نیاز
# Update table structure if needed
cls._update_table_structure(cursor)

conn.commit()
Expand All @@ -42,6 +42,8 @@ def _get_column_definitions(cls, cursor):
for attr, field in cls.__dict__.items():
if isinstance(field, Field):
col_type = field.field_type
if isinstance(field, DecimalField):
col_type += f"({field.max_digits}, {field.decimal_places})"
column_definition = f"{attr} {col_type}"
if field.unique:
column_definition += " UNIQUE"
Expand All @@ -55,7 +57,7 @@ def _get_column_definitions(cls, cursor):
else:
column_definition += f" DEFAULT {field.default}"
else:
column_definition += " DEFAULT NULL"
column_definition += " DEFAULT NULL" # Allow NULL by default to avoid errors
columns.append(column_definition)
return columns

Expand All @@ -67,6 +69,8 @@ def _update_table_structure(cls, cursor):
for column in new_columns:
field = cls.__dict__[column]
col_type = field.field_type
if isinstance(field, DecimalField):
col_type += f"({field.max_digits}, {field.decimal_places})"
column_definition = f"ALTER TABLE {cls.table_name} ADD COLUMN {column} {col_type}"
if field.unique:
column_definition += " UNIQUE"
Expand All @@ -80,7 +84,7 @@ def _update_table_structure(cls, cursor):
else:
column_definition += f" DEFAULT {field.default}"
else:
column_definition += " DEFAULT NULL"
column_definition += " DEFAULT NULL" # Allow NULL by default
cursor.execute(column_definition)

@classmethod
Expand All @@ -89,10 +93,13 @@ def _get_existing_columns(cls, cursor):
return {row[0] for row in cursor.fetchall()}

@classmethod
def all(cls) -> List['BaseModel']:
def all(cls, order_by: Optional[str] = None) -> List['BaseModel']:
conn = cls.connect()
cursor = conn.cursor(dictionary=True)
cursor.execute(f"SELECT * FROM {cls.table_name}")
query = f"SELECT * FROM {cls.table_name}"
if order_by:
query += f" ORDER BY {order_by}"
cursor.execute(query)
results = cursor.fetchall()
conn.close()
return [cls(**row) for row in results]
Expand Down Expand Up @@ -132,6 +139,14 @@ def create(cls, **kwargs) -> None:
columns.append(attr)
placeholders.append('%s')
values.append(kwargs[attr])
elif isinstance(field, DateTimeField) and field.auto_now_add:
columns.append(attr)
placeholders.append('%s')
values.append(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
elif isinstance(field, DateField) and field.auto_now_add:
columns.append(attr)
placeholders.append('%s')
values.append(datetime.datetime.now().strftime('%Y-%m-%d'))
elif isinstance(field, DateTimeField) and field.auto_now:
columns.append(attr)
placeholders.append('%s')
Expand All @@ -150,6 +165,8 @@ def update(cls, id: int, **kwargs) -> None:
field = getattr(cls, key)
if isinstance(field, DateTimeField) and field.auto_now:
value = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
if isinstance(field, DateField) and field.auto_now:
value = datetime.datetime.now().strftime('%Y-%m-%d')
values.append(value)
cursor.execute(f"UPDATE {cls.table_name} SET {set_clause} WHERE id = %s", (*values, id))
conn.commit()
Expand Down
Loading

0 comments on commit c05267a

Please sign in to comment.