├── .gitignore ├── LICENSE.md ├── README.md ├── examples.py ├── requirements.txt ├── sajilo_orm ├── __init__.py ├── exceptions.py ├── field.py ├── manager.py ├── models.py ├── query_manager.py └── settings.py ├── setup.py └── tests ├── __init__.py ├── test_db.py └── test_table.py /.gitignore: -------------------------------------------------------------------------------- 1 | env 2 | venv 3 | __pycache__/ 4 | *.py[cod] 5 | 6 | # Unit test / coverage reports 7 | htmlcov/ 8 | .tox/ 9 | .nox/ 10 | .coverage 11 | .coverage.* 12 | .cache 13 | nosetests.xml 14 | coverage.xml 15 | *.cover 16 | *.py,cover 17 | .hypothesis/ 18 | .pytest_cache/ 19 | cover/ 20 | 21 | # Distribution / packaging 22 | .Python 23 | build/ 24 | develop-eggs/ 25 | dist/ 26 | downloads/ 27 | eggs/ 28 | .eggs/ 29 | lib/ 30 | lib64/ 31 | parts/ 32 | sdist/ 33 | var/ 34 | wheels/ 35 | *.egg-info/ 36 | .installed.cfg 37 | *.egg 38 | MANIFEST.in 39 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learningnoobi/sajilo-orm/6f0eb86affc528aa888b8e8e064954fc29bf41f5/LICENSE.md -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Documentation 2 | ### Installation 3 | 4 | ```python 5 | pip install sajilo-orm 6 | ``` 7 | 8 | 9 | #### Table of Contents 10 | - [Connect Database](#connect-database) 11 | - [Create Table](#create-table) 12 | - [QuerySet API](#queryset-api) 13 | - [Add Data](#add-data) 14 | - [Filter And Get Data](#filter-and-get-data) 15 | - [Update Data](#update-data) 16 | - [Delete Data](#delete-data) 17 | - [Check If Table Exists](#check-if-table-exists) 18 | - [Types of Exception](#types-of-exception) 19 | 20 | 21 | 22 | ## Connect Database 23 | 24 | *Postgresql is the only supported Database for now(Feel free to contribute to add new database)* 25 | 26 | ```python 27 | from sajilo_orm.manager import BaseManager 28 | 29 | MY_DB_SETTINGS = { 30 | "database": "db_name", 31 | "user": "db_user", 32 | "password": "db_password", 33 | "host": "db_host", #localhost for local connection 34 | "port": "5432", 35 | } 36 | 37 | BaseManager.DB_SETTINGS = MY_DB_SETTINGS 38 | 39 | ``` 40 | ### Create Table 41 | 42 | ###### To create table , import `DamiModel` from `sajilo_orm.models` and use `Column` class to define column along with datatype which is in Nepali 43 | Note: `id serial primary key` is added automatically when creating table 44 | 45 | ```python 46 | 47 | from sajilo_orm.models import DamiModel 48 | from sajilo_orm.field import Column 49 | 50 | class Country(DamiModel): 51 | table_ko_naam = "country" # this will be the name of the table created in database 52 | 53 | name = Column("string", max_length="50") 54 | no_of_province = Column("anka") #number 55 | new_year = Column("miti", default='2000-01-01') #date 56 | today = Column("miti", default='aja_ko_date') #gives CURRENT_DATE 57 | ramro = Column("ho_ki_haina" , default="ho") #boolean 58 | data = Column("string", null=False,max_length="20") #by default null=True 59 | 60 | ``` 61 | Here's what data type type is behind the scene 62 | ``` 63 | sql_type = { 64 | "anka":"INT ", 65 | "string":"VARCHAR({max_length}) 66 | "miti":"DATE ", 67 | "ho_ki_haina":"BOOLEAN ", 68 | } 69 | (string chai nepali ma k rakhne vanera sochirako) 70 | 71 | ``` 72 | ##### Ths won't create table yet , To create table use `table_banau` method 73 | 74 | ```python 75 | Country.bata.table_banau() 76 | ``` 77 | 78 | ### QuerySet API 79 | 80 | Here's the list of api that you can use for executing query 81 | ##### Get all data 82 | 83 | ```python 84 | 85 | country_list = Country.bata.sabaideu() # Returns List of Model Objects 86 | print(country_list) 87 | 88 | #output 89 | #[] since there is no data yet 90 | ``` 91 | #### Add Data 92 | For adding data in the table , use `data_hala` method. 93 | `Table.ma.data_hala(**dataharu)` 94 | 95 | ```python 96 | 97 | Country.ma.data_hala(name="nepal",no_of_province= 8,data="nice country") # will update below 98 | Country.ma.data_hala(name="japan",no_of_province= 37,data="nice country") #default value are added 99 | 100 | # Now , if you call sabaideu 101 | 102 | country_list = Country.bata.sabaideu() 103 | #[,] 104 | 105 | # To get single object 106 | a = country_list[0] 107 | 108 | print(a.name,a.no_of_province) 109 | # nepal,8 110 | 111 | ``` 112 | Note: `Country.bata.data_hala(name="japan",no_of_province= 37) ` also works 113 | `ma` was adding cause it sounds gramatically correct than `bata` :sweat_smile: 114 | 115 | #### Filter and Get Data 116 | For filter , use `khojera` method 117 | 118 | ```python 119 | filters = Team.bata.khojera(name="PSG") 120 | ``` 121 | 122 | 123 | ###### For multiple column filter with 'AND' , add comma 124 | 125 | 126 | ```python 127 | filters = Country.bata.khojera(name="nepal" , no_of_province =20) 128 | ``` 129 | ###### For 'OR' filter , add an argument with value "or" before writing filter condition 130 | 131 | ```python 132 | filters = Country.bata.khojera("or",name="japan" , no_of_province =20) 133 | ``` 134 | 135 | #### Update Data 136 | 137 | To Update data, we need to add `id` before specifying updating column 138 | If not id is provided `IdXainaKanchha` exception will be raised 139 | 140 | ```python 141 | Country.bata.data_fera(id=1, name="Nepal", no_of_province=7,new_year='1999-01-01') 142 | # this will update Country with id 1 and rest will be updated with data provided 143 | a = country_list[0] 144 | 145 | print(a.name,a.no_of_province,a.new_year) 146 | # Nepal,7,1999-01-01 147 | ``` 148 | ##### Exceptions 149 | If nothing is provided after id , then `SyntaxBigryoKanchha` exception will be raised 150 | ```python 151 | Refree.bata.data_fera(id=1) #raises SyntaxBigryoKanchha Exception 152 | 153 | Refree.bata.data_fera(name="sinpachi") # raises IdXainaKanchha Exception 154 | ``` 155 | 156 | #### Delete Data 157 | With this , this ORM will be able to perform simple `CRUD OPERATIONS` 158 | 159 | To delete `data_fala` (lol) is used . Use this wisely . 160 | 161 | ```python 162 | Country.bata.data_fala(id=2) 163 | Country.bata.data_fala(name='japan')` 164 | Country.bata.data_fala(no_of_provinces=') 165 | ``` 166 | 167 | #### Check if table exists 168 | 169 | ```python 170 | Refree.bata.check_table_exists() 171 | ``` 172 | 173 | ###### To get the use case of this orm , read the [Test Case Here](https://github.com/learningnoobi/sajilo-orm/blob/main/tests/test_table.py) 174 | 175 | 176 | #### Types of Exeptions 177 | Below is the list of exception you might get using `sajilo orm` 178 | - ###### TableVetayenaKanchha `Database ma nai table navayesi aaune error ! ` 179 | - ###### ColumnNaiXainaKanchha `Table ma nai navako column diyesi aaune error ! ` 180 | - ###### DatabaseConnectVayenaKanchha `Database connection config namilda aaune error ! ` 181 | - ###### IdXainaKanchha `Data ferda id diyena vane aaune error ` 182 | - ###### SyntaxBigryoKanchha `Syntax nai bigrexi aaune error ! ` 183 | - ###### DateFormatMilenaKanchha `Date Format nai bigrexi aaune error ! ` 184 | - ###### NotNullMaDataVayenaKanchha `Not Null ma Data navayesi aaune error !` 185 | -------------------------------------------------------------------------------- /examples.py: -------------------------------------------------------------------------------- 1 | """ 2 | This shows example on how to use sajilo orm . 3 | """ 4 | 5 | from email.policy import default 6 | from sajilo_orm.models import DamiModel 7 | from sajilo_orm.field import Column 8 | from sajilo_orm.manager import BaseManager 9 | from sajilo_orm.settings import DB_SETTINGS 10 | 11 | 12 | BaseManager.DB_SETTINGS = DB_SETTINGS 13 | 14 | 15 | from sajilo_orm.models import DamiModel 16 | 17 | class Player(DamiModel): 18 | table_ko_naam = "boy" 19 | 20 | name = Column("string", max_length="50",null=False) 21 | name = Column("string",null=False) 22 | age = Column("anka",null=True,default=34) 23 | match_played = Column("miti") 24 | aja_date = Column("miti",default='aja_ko_date') 25 | won = Column("ho_ki_haina",default=True,max_length="34") 26 | 27 | 28 | def main(): 29 | 30 | #some operations you can perform with sajilo 31 | 32 | # create table 33 | Player.bata.table_banau() 34 | 35 | 36 | if __name__ == "__main__": 37 | main() 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | attrs==21.4.0 2 | black==22.3.0 3 | click==8.1.3 4 | colorama==0.4.4 5 | coverage==6.3.2 6 | iniconfig==1.1.1 7 | mypy-extensions==0.4.3 8 | packaging==21.3 9 | pathspec==0.9.0 10 | platformdirs==2.5.2 11 | pluggy==1.0.0 12 | psycopg2-binary==2.9.3 13 | py==1.11.0 14 | pyparsing==3.0.8 15 | pytest==7.1.2 16 | tomli==2.0.1 17 | -------------------------------------------------------------------------------- /sajilo_orm/__init__.py: -------------------------------------------------------------------------------- 1 | __title__ = 'Sajilo ORM' 2 | __version__ = '0.0.6' 3 | __author__ = 'learningnoobi (Bishal Rai)' 4 | 5 | 6 | SELECT_ALL = "SELECT * FROM {} " 7 | INSERT_INTO = " INSERT INTO {} ({}) values {} " 8 | DELETE_ALL = "DELETE FROM {} " 9 | CHECK_TABLE = "select exists(select relname from pg_class where relname = '{}' and relkind='r');" 10 | UPDATE = "Update {} SET {} WHERE {} ;" 11 | CREATE_TABLE = "CREATE TABLE IF NOT EXISTS {} ({}) " 12 | DROP_TABLE = "DROP TABLE {} " 13 | # "Update {} set {player_name='XOLOK',trophy_won = 999} where player_id=8;" 14 | 15 | 16 | -------------------------------------------------------------------------------- /sajilo_orm/exceptions.py: -------------------------------------------------------------------------------- 1 | from colorama import Fore 2 | 3 | 4 | class TableVetayenaKanchha(Exception): 5 | """ 6 | Database ma nai table navayesi aaune error ! 7 | """ 8 | def __init__(self, table_name,db_name) -> None: 9 | super().__init__(Fore.RED + f"Timle deko table '{table_name}' {db_name} vanne database ma nai vetayena ! Spelling bigryo ki herata ramro sanga !") 10 | 11 | 12 | class ColumnNaiXainaKanchha(Exception): 13 | """ 14 | Table ma nai navako column diyesi aaune error ! 15 | """ 16 | 17 | def __init__(self,table_name) -> None: 18 | super().__init__(Fore.RED + f"Timle deko column Table '{table_name}' ma nai vetayena ! Spelling bigryo ki ramro sanga herata !") 19 | 20 | 21 | class DatabaseConnectVayenaKanchha(Exception): 22 | """ 23 | Database connection config namilda aaune error ! 24 | """ 25 | 26 | def __init__(self) -> None: 27 | super().__init__(Fore.RED + f"Arrey !! Database ta ramrari connect gara paila !!") 28 | 29 | class IdXainaKanchha(Exception): 30 | """ 31 | Data ferda id diyena vane aaune error 32 | """ 33 | 34 | def __init__(self) -> None: 35 | super().__init__(Fore.RED + f"Data fernalai euta unique ID chainxa . Unique Id diyerw feri try gara !!") 36 | 37 | class SyntaxBigryoKanchha(Exception): 38 | """ 39 | Syntax nai bigrexi aaune error ! 40 | """ 41 | 42 | def __init__(self) -> None: 43 | super().__init__(Fore.RED + f"Query ko syntax bigrye xa . Yedi update garda aako vaye id bahek arko field ni chainxa !!") 44 | 45 | class DateFormatMilenaKanchha(Exception): 46 | """ 47 | Date Format nai bigrexi aaune error ! 48 | """ 49 | 50 | def __init__(self) -> None: 51 | super().__init__(Fore.RED + f"Miti ko format bigrexa . Date Format Yesto Prakar ko Xa Hai (2022-01-01) i.e (year/month/day) !!") 52 | 53 | class NotNullMaDataVayenaKanchha(Exception): 54 | """ 55 | Not Null ma Data navayesi aaune error ! 56 | """ 57 | 58 | def __init__(self) -> None: 59 | super().__init__(Fore.RED + f"Not Null vako column ma data vayena . Data halerw feri try gara !!") 60 | 61 | class MaxLengthVayenaKanchha(Exception): 62 | """ 63 | Not Null ma Data navayesi aaune error ! 64 | """ 65 | 66 | def __init__(self) -> None: 67 | super().__init__(Fore.RED + f"String ma max_length compulsary xa kanchha .max_length rakherw feri try garnu hola!!") 68 | 69 | -------------------------------------------------------------------------------- /sajilo_orm/field.py: -------------------------------------------------------------------------------- 1 | 2 | from sajilo_orm.exceptions import MaxLengthVayenaKanchha 3 | 4 | 5 | sql_type = { 6 | "anka":"INT {} {}", 7 | "string":"VARCHAR({}) {} {}", 8 | "miti":"DATE {} {}", 9 | "ho_ki_haina":"BOOLEAN {} {}", 10 | } 11 | 12 | needs_q = { 13 | 'miti':True, 14 | 'string':True, 15 | 'anka':False, 16 | 'ho_ki_haina':True 17 | } 18 | class Column: 19 | constraint = {} 20 | def __init__(self,data_type,**kwargs): 21 | self.data_type = data_type 22 | self.constraint = kwargs 23 | self.check_null() 24 | 25 | def check_null(self): 26 | if not "null" in self.constraint: 27 | self.constraint.update({"null":True}) 28 | 29 | 30 | def _is_null_or_not(self,data_type,default_val): 31 | if self.constraint["null"] and not default_val: 32 | return sql_type[data_type].format('','') 33 | 34 | if self.constraint["null"] and default_val: 35 | if needs_q[data_type]: 36 | if data_type == "miti" and default_val=="aja_ko_date": 37 | return sql_type[data_type].format('',f"DEFAULT CURRENT_DATE") 38 | elif data_type == "ho_ki_haina" and default_val=="haina": 39 | return sql_type[data_type].format('',f"DEFAULT 'f'") 40 | elif data_type == "ho_ki_haina" and default_val=="ho": 41 | return sql_type[data_type].format('',f"DEFAULT 't'") 42 | else: 43 | return sql_type[data_type].format('',f"DEFAULT '{default_val}'") 44 | return sql_type[data_type].format('',f" DEFAULT {default_val}") 45 | else: 46 | return sql_type[data_type].format('NOT NULL ','') 47 | 48 | 49 | 50 | 51 | @property 52 | def _get_sql_type(self): 53 | default_val = self.constraint.get("default") 54 | if self.data_type == "string": 55 | MAX =self.constraint.get("max_length") 56 | if not MAX: 57 | raise MaxLengthVayenaKanchha 58 | 59 | 60 | if self.constraint["null"] and not default_val: 61 | return sql_type[self.data_type].format(MAX,'','') 62 | 63 | if self.constraint["null"] and default_val: 64 | return sql_type[self.data_type].format(MAX, 'NOT NULL ',f" DEFAULT '{default_val}'") 65 | 66 | else: 67 | return sql_type[self.data_type].format(MAX,'NOT NULL' ,'') 68 | 69 | 70 | return self._is_null_or_not(self.data_type,default_val) 71 | 72 | -------------------------------------------------------------------------------- /sajilo_orm/manager.py: -------------------------------------------------------------------------------- 1 | import psycopg2 2 | # from sajilo_orm.settings import DB_SETTINGS 3 | from sajilo_orm.exceptions import TableVetayenaKanchha,DatabaseConnectVayenaKanchha 4 | 5 | 6 | class BaseManager: 7 | """ 8 | This is the base manager which has database related function (like executing query ) 9 | """ 10 | 11 | cursor = None 12 | check_table = True 13 | DB_SETTINGS=None 14 | 15 | def __init__(self) -> None: 16 | try: 17 | self.connection = psycopg2.connect(**self.DB_SETTINGS) 18 | self.cursor = self.connection.cursor() 19 | except: 20 | raise DatabaseConnectVayenaKanchha 21 | 22 | 23 | 24 | def check_table_exists(self): 25 | self.cursor.execute(self.does_table_exits) 26 | return self.cursor.fetchone()[0] 27 | 28 | def _execute_query(self, query): 29 | if not self.check_table_exists() and self.check_table: 30 | db_name = self.DB_SETTINGS["database"] 31 | raise TableVetayenaKanchha(self.table_name, db_name) 32 | self.cursor.execute(query) 33 | self.connection.commit() 34 | 35 | # def _return_queryset(self, data): 36 | # result = [r for r in data] 37 | # self.cols = [desc[0] for desc in self.cursor.description] 38 | # queryset = [dict(zip(self.cols, i)) for i in result] 39 | # return queryset 40 | -------------------------------------------------------------------------------- /sajilo_orm/models.py: -------------------------------------------------------------------------------- 1 | from sajilo_orm.field import Column 2 | from sajilo_orm.query_manager import QueryManager 3 | import inspect 4 | 5 | 6 | class MetaModel(type): 7 | """ 8 | Main Metaclass 9 | 10 | bata , ma and lai has exactly same methods and operations. 11 | It is just to make query gramatically correct in Nepali 12 | For eg: 13 | Table.bata.sabaideu() sounds correct 14 | Table.bata.data_hala(**data) doesnot sound that good 15 | But Table.ma.data_hal() sounds correct 16 | 17 | Same With: 18 | Table.bata.fala() will work and drop all table but 19 | Table.lai.fala() sounds more correct 20 | 21 | """ 22 | @property 23 | def bata(self): 24 | return QueryManager(model_class=self) 25 | 26 | @property 27 | def ma(self): 28 | return QueryManager(model_class=self) 29 | 30 | @property 31 | def lai(self): 32 | return QueryManager(model_class=self) 33 | 34 | 35 | class DamiModel(metaclass=MetaModel): 36 | table_ko_naam = None 37 | fields = ["id"] 38 | 39 | def __init__(self) -> None: 40 | for name, field in inspect.getmembers(self): 41 | if isinstance(field, Column): 42 | self.fields.append(name) 43 | 44 | def setData(self, tup): 45 | for i, j in enumerate(tup): 46 | b = self.fields[i] 47 | setattr(self, b, j) 48 | 49 | def __repr__(self) -> str: 50 | return f"<{self.table_ko_naam} : {self.id} >" 51 | -------------------------------------------------------------------------------- /sajilo_orm/query_manager.py: -------------------------------------------------------------------------------- 1 | from operator import concat 2 | from sajilo_orm.exceptions import (ColumnNaiXainaKanchha, DateFormatMilenaKanchha, IdXainaKanchha, NotNullMaDataVayenaKanchha, SyntaxBigryoKanchha) 3 | import psycopg2 4 | from sajilo_orm.manager import BaseManager 5 | import psycopg2 6 | from sajilo_orm import ( 7 | CHECK_TABLE, 8 | CREATE_TABLE, 9 | SELECT_ALL, 10 | INSERT_INTO, 11 | DELETE_ALL, 12 | UPDATE, 13 | DROP_TABLE 14 | ) 15 | 16 | 17 | class QueryManager(BaseManager): 18 | table_name = None 19 | 20 | def __init__(self, model_class) -> None: 21 | super().__init__() 22 | self.model_class = model_class 23 | self.table_name = model_class.table_ko_naam 24 | self.does_table_exits = CHECK_TABLE.format(self.table_name) 25 | 26 | def get_columns(self): 27 | import inspect 28 | from sajilo_orm.field import Column 29 | 30 | fields = [("id", "SERIAL PRIMARY KEY")] 31 | for name, field in inspect.getmembers(self.model_class): 32 | if isinstance(field, Column): 33 | fields.append((name, field._get_sql_type)) 34 | return fields 35 | 36 | def table_banau(self): 37 | self.check_table = False 38 | fields = self.get_columns() 39 | self.base_q = [" ".join(i) for i in fields] 40 | query = CREATE_TABLE.format(self.table_name, " , ".join(self.base_q)) 41 | # print(query) 42 | self._execute_query(query) 43 | 44 | 45 | def give_object_list(self, data): 46 | querysets = [] 47 | for da in data: 48 | b = self.model_class() 49 | b.setData(da) 50 | querysets.append(b) 51 | return querysets 52 | 53 | def sabaideu(self): 54 | """Gives all Data of the table""" 55 | 56 | query = SELECT_ALL.format(self.table_name) 57 | self._execute_query(query) 58 | data = self.cursor.fetchall() 59 | return self.give_object_list(data) 60 | 61 | def _give_condition_query(self, con, **condition): 62 | condition_query = "" 63 | for key, value in condition.items(): 64 | condition_query = ( 65 | condition_query + f"{key} = '{value}' {con} " 66 | if isinstance(value, str) 67 | else condition_query + f"{key} = {value} {con}" 68 | ) 69 | return condition_query 70 | 71 | def khojera(self, con="and", **condition): 72 | """Filters the queryset from the ocondition""" 73 | 74 | query = SELECT_ALL.format(self.table_name) 75 | condition_query = self._give_condition_query(con, **condition) 76 | query += f"WHERE {condition_query[:-3]};" 77 | self._execute_query(query) 78 | data = self.cursor.fetchall() 79 | return self.give_object_list(data) 80 | 81 | def data_hala(self, **data): 82 | """Insert Value In Given Table""" 83 | 84 | keys, value_data = list(), list() 85 | for key, value in data.items(): 86 | keys.append(key), value_data.append(value) 87 | 88 | keys = ",".join(keys) 89 | value_data_str = f"{tuple(value_data)}" 90 | value_data = ( 91 | value_data_str[:-2] + ")" if len(value_data) == 1 else value_data_str 92 | ) 93 | 94 | query = INSERT_INTO.format(self.table_name, keys, value_data) 95 | try: 96 | self._execute_query(query) 97 | 98 | except psycopg2.errors.UndefinedColumn: 99 | raise ColumnNaiXainaKanchha(self.table_name) 100 | 101 | except psycopg2.errors.InvalidDatetimeFormat: 102 | raise DateFormatMilenaKanchha 103 | 104 | except psycopg2.errors.NotNullViolation: 105 | raise NotNullMaDataVayenaKanchha 106 | 107 | 108 | #delete data 109 | def data_fala(self, con="and ", **condition): 110 | query = DELETE_ALL.format(self.table_name) 111 | if len(condition) > 0: 112 | condition_query = self._give_condition_query(con, **condition) 113 | query += f"WHERE {condition_query[:-4]} ;" 114 | try: 115 | self._execute_query(query) 116 | except psycopg2.errors.UndefinedColumn: 117 | raise ColumnNaiXainaKanchha(self.table_name) 118 | 119 | #update data 120 | def data_fera(self, **condition): 121 | try: 122 | pk = condition.pop("id") 123 | except KeyError: 124 | raise IdXainaKanchha 125 | condition_query = self._give_condition_query(",", **condition)[:-2] 126 | query = UPDATE.format(self.table_name,condition_query,f'id = {pk}' ) 127 | try: 128 | self._execute_query(query) 129 | except psycopg2.errors.UndefinedColumn: 130 | raise ColumnNaiXainaKanchha(self.table_name) 131 | 132 | except psycopg2.errors.SyntaxError: 133 | raise SyntaxBigryoKanchha 134 | 135 | #delete table 136 | def fala(self): 137 | query = DROP_TABLE.format(self.table_name) 138 | self._execute_query(query) 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /sajilo_orm/settings.py: -------------------------------------------------------------------------------- 1 | #for my dev settings 2 | DB_SETTINGS = { 3 | "database": "firstboi", 4 | "user": "bishal", 5 | "password": "rayon123", 6 | "host": "localhost", 7 | "port": "5432", 8 | } 9 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | from setuptools import find_packages, setup 3 | 4 | BASE_DIR = pathlib.Path(__file__).parent 5 | 6 | README = (BASE_DIR / "README.md").read_text() 7 | 8 | setup( 9 | name='sajilo-orm', 10 | version='0.0.6', 11 | description='Sarai Sajilo lightweight ORM inspired by Django ORM with nepali twist', 12 | long_description=README, 13 | long_description_content_type="text/markdown", 14 | url='https://github.com/learningnoobi/sajilo-orm', 15 | author='learningnoobi (Bishal Rai)', 16 | author_email='fanime492@gmail.com', 17 | license='MIT', 18 | packages=find_packages(exclude=['tests*']), 19 | include_package_data=True, 20 | classifiers = [ 21 | "Programming Language :: Python :: 3", 22 | "Programming Language :: Python :: 3.6", 23 | "Programming Language :: Python :: 3.7", 24 | "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", 25 | "Operating System :: OS Independent" 26 | ], 27 | install_requires=[ 28 | "colorama ~= 0.4.4", 29 | "iniconfig ~= 1.1.1", 30 | "mypy-extensions ~= 0.4.3", 31 | "packaging ~= 21.3", 32 | "pathspec ~= 0.9.0", 33 | "platformdirs ~= 2.5.2", 34 | "pluggy ~= 1.0.0", 35 | "psycopg2-binary ~= 2.9.3", 36 | "py ~= 1.11.0", 37 | "pyparsing ~= 3.0.8", 38 | "tomli ~= 2.0.1", 39 | ], 40 | python_requires=">=3.6", 41 | zip_safe=False, 42 | extras_require = { 43 | "dev": [ 44 | "coverage==6.3.2", 45 | "pytest==7.1.2" 46 | ] 47 | } 48 | ) 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | from sajilo_orm.models import DamiModel 2 | from sajilo_orm.field import Column 3 | from sajilo_orm.manager import BaseManager 4 | from sajilo_orm.exceptions import * 5 | import pytest 6 | 7 | TEST_DB_SETTINGS = { 8 | "database": "testdb", 9 | "user": "bishal", 10 | "password": "rayon123", 11 | "host": "localhost", 12 | "port": "5432", 13 | } 14 | 15 | 16 | 17 | BaseManager.DB_SETTINGS = TEST_DB_SETTINGS 18 | 19 | @pytest.fixture(scope="module") 20 | def cursor(): 21 | b = BaseManager() 22 | yield b.cursor 23 | b.cursor = b.connection.cursor() 24 | #Drop all tables after a test 25 | b.cursor.execute(f''' 26 | DO $$ DECLARE 27 | r RECORD; 28 | BEGIN 29 | FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = current_schema()) LOOP 30 | EXECUTE 'DROP TABLE IF EXISTS ' || quote_ident(r.tablename) || ' CASCADE'; 31 | END LOOP; 32 | END $$; 33 | ''') 34 | b.connection.commit() 35 | b.connection.close() 36 | 37 | -------------------------------------------------------------------------------- /tests/test_db.py: -------------------------------------------------------------------------------- 1 | from tests import BaseManager, TEST_DB_SETTINGS, DatabaseConnectVayenaKanchha 2 | import pytest 3 | 4 | 5 | 6 | 7 | def test_fail_database_connection(): 8 | BaseManager.DB_SETTINGS = {"jpt":"connection"} 9 | with pytest.raises(DatabaseConnectVayenaKanchha) as exc_info: 10 | BaseManager() 11 | 12 | assert "Arrey !! Database ta ramrari connect gara paila !!" in str(exc_info.value) 13 | 14 | 15 | def test_success_database_connection(): 16 | BaseManager.DB_SETTINGS = TEST_DB_SETTINGS 17 | BaseManager() -------------------------------------------------------------------------------- /tests/test_table.py: -------------------------------------------------------------------------------- 1 | from typing import Type 2 | from sajilo_orm.query_manager import QueryManager 3 | from tests import (Column, 4 | cursor, 5 | DamiModel, 6 | TableVetayenaKanchha, 7 | ColumnNaiXainaKanchha, 8 | IdXainaKanchha, 9 | NotNullMaDataVayenaKanchha, 10 | DateFormatMilenaKanchha, 11 | SyntaxBigryoKanchha, 12 | MaxLengthVayenaKanchha 13 | ) 14 | import pytest 15 | 16 | # Use test db since we will be dropping every table at first 17 | 18 | 19 | class Refree(DamiModel): 20 | table_ko_naam = "refree" 21 | 22 | name = Column("string", max_length="50") 23 | age = Column("anka") 24 | whens = Column("miti",default='aja_ko_date') 25 | won = Column("ho_ki_haina",default="haina",null=False) 26 | match_played = Column("anka") 27 | 28 | 29 | 30 | # T his Student model is to check field and Column section only 31 | # basically using all the field with all the case (like null=True,default="example") 32 | # to ensure every case is working 33 | class Student(DamiModel): 34 | table_ko_naam = "students" 35 | 36 | name = Column("string", max_length="50",default="hero") 37 | bio = Column("string", max_length="50",null=False ) 38 | graduate = Column("ho_ki_haina",default="ho") 39 | fail_vako = Column("ho_ki_haina",default="haina") 40 | whens = Column("miti",default='aja_ko_date') 41 | kaile = Column("miti",default='2000-01-01') 42 | match_played = Column("anka",default=69) 43 | 44 | 45 | 46 | class Random(DamiModel): 47 | table_ko_naam = "random" 48 | name = Column("string",max_length="34") 49 | 50 | class Boy(DamiModel): 51 | table_ko_naam = "random" 52 | name = Column("string") #no max_legnth then error must come 53 | 54 | 55 | def test_table_xaina(cursor): 56 | assert Refree.bata.check_table_exists() == False 57 | 58 | 59 | def test_table_banau(cursor): 60 | Refree.bata.table_banau() 61 | assert Refree.bata.check_table_exists() == True 62 | 63 | 64 | def test_table_vetayena(cursor): 65 | with pytest.raises(TableVetayenaKanchha) as exc_info: 66 | Random.bata.sabaideu() 67 | 68 | 69 | def test_crud_operations(cursor): 70 | from datetime import date 71 | 72 | today = date.today() 73 | # today = today.strftime("%Y-%m-%d") 74 | Refree.bata.table_banau() 75 | Student.bata.table_banau() 76 | 77 | # LIST 78 | assert Refree.bata.sabaideu() == [] 79 | 80 | # INSERT 81 | Refree.ma.data_hala(name="bishal", age=21, match_played=69,won=False) 82 | Refree.ma.data_hala(name="rayon", age=34, match_played=9,won=True) 83 | b = Refree.bata.sabaideu() 84 | assert len(b) == 2 85 | 86 | # filter 87 | 88 | filter = Refree.bata.khojera(id=1)[0] 89 | assert repr(filter) == "" 90 | 91 | assert filter.name == "bishal" 92 | assert filter.age != 23 93 | 94 | # RETRIEVE 95 | print(b[0].__dict__) 96 | assert b[0].__dict__ == {"id": 1, "age": 21, "match_played": 69, "name": "bishal","won":False,"whens":today} 97 | 98 | # DELETE 99 | Refree.bata.data_fala(id=2) 100 | assert len(Refree.bata.sabaideu()) == 1 101 | 102 | 103 | def test_exception_error(cursor): 104 | Refree.bata.table_banau() 105 | with pytest.raises(ColumnNaiXainaKanchha): 106 | Refree.ma.data_hala(nama="rayon", umer=34, kheleko=9) 107 | 108 | with pytest.raises(ColumnNaiXainaKanchha): 109 | Refree.ma.data_fala(nama="rayon", umer=34, kheleko=9) 110 | 111 | with pytest.raises(NotNullMaDataVayenaKanchha): 112 | Refree.bata.data_hala(age=31, match_played=999) 113 | 114 | with pytest.raises(MaxLengthVayenaKanchha): 115 | Boy.bata.table_banau() 116 | 117 | with pytest.raises(DateFormatMilenaKanchha): 118 | Refree.bata.data_hala(name='kagura' ,age=31, match_played=999,whens='12012') 119 | 120 | 121 | 122 | 123 | 124 | 125 | def test_data_update(cursor): 126 | Refree.bata.table_banau() 127 | Refree.ma.data_hala(name="bishal", age=21, match_played=69,won=True) 128 | first_ref = Refree.bata.khojera(id=1)[0] 129 | 130 | assert first_ref.id == 1 131 | assert first_ref.name == "bishal" 132 | assert first_ref.age == 21 133 | assert first_ref.match_played == 69 134 | 135 | # data ferne 136 | Refree.bata.data_fera(id=1, name="gintoki", age=31, match_played=999) 137 | first_ref = Refree.bata.khojera(id=1)[0] 138 | assert first_ref.id == 1 139 | assert first_ref.name == "gintoki" 140 | assert first_ref.age == 31 141 | assert first_ref.match_played == 999 142 | 143 | with pytest.raises(ColumnNaiXainaKanchha): 144 | Refree.bata.data_fera(id=1, jpt_column="gintoki") 145 | 146 | with pytest.raises(IdXainaKanchha): 147 | Refree.bata.data_fera(name="sinpachi") 148 | 149 | with pytest.raises(IdXainaKanchha): 150 | Refree.bata.data_fera() 151 | 152 | with pytest.raises(SyntaxBigryoKanchha): 153 | Refree.bata.data_fera(id=1) 154 | 155 | def test_table_fala(cursor): 156 | Refree.bata.table_banau() 157 | Random.bata.table_banau() 158 | assert Refree.bata.check_table_exists() == True 159 | Refree.lai.fala() 160 | assert Refree.bata.check_table_exists() == False 161 | assert Random.bata.check_table_exists() == True 162 | Random.lai.fala() 163 | assert Random.bata.check_table_exists() == False 164 | 165 | 166 | 167 | 168 | --------------------------------------------------------------------------------