SkillAgentSearch skills...

Sideral

ORM framework in Python for MySQL

Install / Use

/learn @dafine-dev/Sideral
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<h1>Introduction</h1> <p>Sideral is an ORM (Object Relational Mapper) framework for Python applications which use a MySQL database. It is designed to easily generate repositories able to retrieve row-based information and map it to objects, as well as, translate objects to rows and persist them. To achieve it, besides the needing to understand the correlation between an application’s model classes and their database tables, the framework must provide ways to manage connections and coordinate transactions occurring within them. Sideral succeeds in doing it by just adding minor changes to the code, thus, keeping it as clean as possible.</p> <h1>Setting up</h1> <p>To begin, firstly, Sideral must be installed. To do it, use this command:</p> <div class = "code"> <pre style = "text-aling: center;">pip install sideral</pre> </div> <p>Sideral was thought to be used in MVC (Model View Control) systems, so it's recommended that the application structure looks like this:</p> <div class = "code"> <pre> src: | |-- models.py |-- repository.py |-- database.conf |-- main.py </pre> </div> <p>Or like this:</p> <div class = "code"> <pre> src: | |-- models | | -- __init__.py |-- repository | | -- __init__.py |-- database.conf |-- main.py </pre> </div> <p>The <i>database.conf</i> file sets the needed information to create a connection with the desired database. This file has to be in the same directory of the one responsible for running the application. In its inner code, the data must be passed after the <i>[connection]</i> line and be written this way:<p> <div class = "code"> <pre> [connection] host = host_name database = database_name user = user_name password = password </pre> </div> <p>It isn't mandatory to have this file structure. But it's important to make sure all model classes are loaded before using any of them to Sideral work correctly.</p> <h1> How to use </h1> <p> Sideral works around its annotations, which are Python decorators intended to be used, generally, on/inside a class. When imported to a project, they modify some Sideral’s inner procedures aspects, if correctly employed. Due to the framework’s architecture, the object-relational mapping is the key process; hence, the annotations – in majority – were thought to be implemented jointly with a model class. However, it’s also noteworthy that to take advantage of this mapping, it’s necessary to build access points. Therefore, Sideral offers annotations able to generate repository classes with custom CRUD operations, besides ones responsible for managing these transactions. </p> <p> The following sections will detail all available annotations, their functionalities, where and how to implement them properly; by responsibility. </p> <br> <h2> Mapping </h2> <p> Whereas Python applications organize information in classes, objects and attributes; a database storing uses tables, rows and columns. These data structures are not always homogenously and directly represented on the opposite context, hence, these gaps must be tracked and solved. </p> <p> The mapping annotations, by decorating a model class, sets the information needed to Sideral settle these divergences and, thus, be able to convert freely through both structure patterns. </p> <br> <h3 style = "font-weight: bold"> 1. Entity</i> </h3> <p> Entity is a model class that has a direct representation in the database as a table and its attributes are mapped as columns. Mapping a class as a database entity is possible by using the <i>@entity</i> annotation on a class: </p> <div> <pre> from sideral import entity </br>@entity class Client: ... </pre> </div> <p> By default, Sideral will assume the class name is also the correspondent table's. In this case: "Client" is both class and table names; but, by adding the <i>name</i> parameter, this default assumption is avoided and a new table name is set. </p> <div> <pre> from sideral import entity </br>@entity(name = 'Clients') class Client: ... </pre> </div> <p> A table has its columns, some of them are mapped as attributes while others may not. Thus, Sideral has a <i>@column</i> annotation to manage this mapping, but it has a small catch. The <i>@column</i> works similarly to Python built-in decorator <i>@property</i>, so all to-column-mapped attributes must be private and define getter and setter functions. Consequently, an entity class should be written like this: </p> <div class = "code"> <pre> from sideral import entity, column<br> @entity class Client:<br> @column def name(self) -> str: return self._name<br> @name.setter def name(self, name: str) -> None: self._name = name </pre> </div> <p> The <i>@column</i> annotation infer the column name to be the same as the annotated attribute's. But, this denomination can be changed by passing a <i>name</i> argument through the decorator parameters: </p> <div clas = "code"> <pre> @column(name = "client_name") def name(self) -> str: return self._name </pre> </div> <p> In Sideral, there is a special type of column, thought to define which column is the primary key representation. The <i>@id</i> annotation works exactly as <i>@column</i>, but Sideral accepts only one primary key per table. <div class = "code"> <pre> from sideral import entity, id<br> @entity class Client:<br> @id def id(self) -> int: return self._id </pre> </div> </p> <br> <h3>2. Relationship</h3> <p> Model classes are associated among themselves and it's important to store and retrieve these relationships from the database. Sideral offers some annotations accountable for mapping these links and, like the previously mentioned <i>@column</i> annotation, they only work on private attributes with implemented getter and setter methods. Moreover, since Sideral saves associations through queries apart, it doesn't support mandatory relationships; also, a relationship annotation knows which class it refers to by accessing the getter function's return type hint, so it's crucial to fill it. </p> <div class = "code"> <pre> from __future__ import annotations from sideral import entity, many_to_one<br> @entity class Order:<br> @many_to_one def client(self) -> Client: return self._client<br> @client.setter def client(self, client: Client) -> None: self._client = client </pre> </div> <p> In order to connect both sides of a given association, a <i>mapping</i> parameter can be passed through decorator. The mapping values need to be a string and contain the attribute name which represents the relationship opposite side. By filling this field, class instances, retrieved from the database, will have an "loop association" with its related objects. <div class = "code"> <pre> from __future__ import annotations from sideral import one_to_many, many_to_one, entity<br> @entity class Client:<br> @one_to_many(mapping = 'client') def orders(self) -> list[Order]: return self._orders <br> @entity class Order:<br> @many_to_one(mapping = 'orders') def client(self) -> Client: return self._client </pre> </div> </p> <p> Differently from an object-oriented enviroment, in which an association link stores the whole associated information in object form (or in a list containing many of this), a database context persists these links using foreign keys. In this manner, these keys are columns inside a table, hence, they have a name. And as the <i>@entity</i> or <i>@column</i>, the relationship annotations surmise its foreign key name is equal: to the name whose table the key refers to, preffixed with "id_" characters. However, it's possible to set a non default denomination by using the <i>@join</i> decorator above any side of the relationship. </p> <div class = "code"> <pre> @join(column_name = 'client_id') # previous name: 'id_client' @many_to_one def client(self) -> Client: return self._client </pre> </div> <p> or </p> <div class = "code"> <pre> @join(column_name = 'client_id') @one_to_many def orders(self) -> list[Order]: return self._orders </pre> </div> <br> <h3 class= "sub-section">2.1. One to many / many to one</h3> <p> One to many is one of the relationship types between two tables. As precociously seen above, Sideral uses two annotations to map this association. While <i>@many_to_one</i> must be inside the class which has a single reference to the other side, the <i>@one_to_many</i> decorator has to be used in the class that has a list of objects. It's also noteworthy that its getter return hint should be an <i>List</i>/<i>list</i> embracing the associated class type variable. At last, the table, whose equivalent class has an <i>@many_to_one</i> decorator, must own the foreign key that describes the relationship. </p> <div class = "code"> <pre> from __future__ import annotations from sideral import one_to_many, many_to_one, entity<br> @entity class Client:<br> @one_to_many(mapping = 'client') def orders(self) -> list[Order]: return self._orders <br> @entity class Order:<br> @many_to_one(mapping = 'orders') def client(self) -> Client: return self._client </pre> </div> <br> <h3 class = "sub-section">2.2. One to one</h3> <p> When both sides of a relationship carry a single reference to each other, it's a use case for Sideral <i>@one_to_one</i> annotation. This decorator can be implemented in any side, thus, marking a one to one relationship type between two classes. But, differently from one to many, any table involved in the association can own the foreign key. So, the <i>owner</i> parameter (when <i>True</i>), signs which table must have the join column. At last, if none of the annotations set this parameter, Sideral will assume the firstly loaded one to be the owner. </p> <div class = "code"> <pre> from __future__ import annotations from sideral import entity, one_to_one<br> @entity class Student:<br> @one_to_one(mapping = "student")
View on GitHub
GitHub Stars10
CategoryData
Updated1y ago
Forks0

Languages

Python

Security Score

75/100

Audited on Oct 30, 2024

No findings