Welcome to Epure
New way to store data, smart query constructor, concise syntax
Documentation: https://epurelib.github.io/latest/
Source Code: https://github.com/nagvalhm/epure
PyPI: https://pypi.org/project/epure/
Official telegram channel: https://t.me/epuremedia/
Epure is python type-hints based ORM - you can store and retrieve data having no idea about database, table and columns. All technical details hidden from you. Care only about your business logic 🤑.
Key features:
- Convenient: Many useful and intuitive tools, like Elist, Eset, JoinResource and many more
- Fast: Create tables based on your classes in no time
- Concise: Encourages efficient building of flexible and smart queries
- Simple, yet powerful: Simple for novice, powerful for expert
Installing¶
Install and update using pip
:
Install and update using poetry
:
A Simple Epurized Class Example¶
Let's start with simple example for Epure, you will need your DataBase to be up and running. We will be using PostgresSQL for this example.
Type Hinting Required!
Note that you need to use type hints for class attributes in order to save class to DB. Class attribute without a type-hint will not be saved in DB.
If you dont know which type-hint you want to use, but still want to save your field: use object
type-hint and your instance will be saved as JSON
.
Read more about supported types for type-hinting here
Connect it¶
Connect Epure to your DB:
from epure import epure, escript, GresDB
from ..epure.generics import NotNull # (1)!
from typing import List, Dict
GresDb('postgres://postgres:postgres@localhost:5432',
log_level=3).connect() # (2)!
- Check out more about generic Epure types here: link
- Format of string to connect ('database://user:password@host:port'); Note that there is two ways to connect to db, read here:
GresDB and supported DB's
Read more about GresDB class and supported by Epure DataBases here
Define it¶
We will define four classes:
-
three of them (
Publication
,Reporter
,Publisher
) will serve as subordinate classes -
and the one main, supreme class
Article
that will accumulate instances of these other classes:
# base class for Article
@epure()
class Publication:
text_style: str = "scientific"
@epure()
class Reporter:
full_name: str = "Victor Bennet"
class Publisher:
publisher_name = "Future CodeWeave"
@epure() # (1)!
class Article(Publication):
# text_style: str = "scientific" # inherits
reporter: Reporter
title: str
times_published: NotNull[int] = 3
authors: List[str] = ["Charles Dickens", "Frank Herbert"]
publisher: object = Publisher()
def __init__(self, reporter, title):
self.reporter = reporter
self.title = title
def get_articles_using_kwargs():
articles = self.resource.read(times_published=5) # -> [<Article object at 0x0...>, <Article object at 0x2...>]
return articles[0]
@epure
is metaclass decorator function, that modifies class; more info here:
Supported by Epure type-hint types
Read more about supported types for type-hinting here
Magic
method and smart queries
Read more about magic
methods and
@escript
decorator here
object
type in type-hinting of a class
The Article
class has a field publisher
, and is initialized by instance of not epurized class, so if we dont know by which type we want to save an object like this - we can simply type hint it by object
type!
Save it¶
Create and save instances of your class as such:
my_reporter = Reporter()
article_one = Article(my_reporter, "Why Epure is the best ORM?")
article_one.save()
article_two = Article(my_reporter, "Why Eset is so magnificent?")
article_two.save()
Retrieve it¶
Now when your instances saved in DB table named public.article
, we can talk about creating queries variations:
1. Smart queries with use of @escript
magic method
¶
Bit of @escript
, Model
and Resource
theory 🧐
Method itself becomes magical after we decorated it with @escript
. Every "epurized"(1) class has its Model
and Resource
.
- "epurized" class - is
class
that was decorated by@epure()
decorator
Resource
is a field of class
that contains all saved instances of this class
.
Model
is used to get data from Resource
. With use of Model
, we can address fields of class to construct smart queries 😄
Smart query is a predicate
Smart query is a predicate (logic expression), which is used to filter objects, hence every logic expression can be used as part of smart query, e.g. filtering by True
will return all objects from Resource
We will define a magic
method
get_articles
for class Article
Imagine if we want to get all stored articles with names that are defined in list
of title names:
@escript
is method decorator for @epure decorated classes; more info here:- note that md (short for
Model
) is only available in @escript decorated method scope and only; more info here:
Here you can see example for using logical expression with smart query:
By using in
we will get same result 😃:
- read more about supported SQL operators in
Epure
likein
here:
Passing smart query then to .read()
will retrieve Reporter
object(s) with title
value either: "Why Epure is the best ORM?"
, "Why Elist is so powerfull?"
or "What is magic method?"
.
- learn more about
read()
here:
Resource
implements CRUD interface, you can create (.create()
), update (.update()
), delete (.delete()
) and read (.read()
).
And calling .smart_query_example()
, our magic
method will return your retrieved object. ✨ Viola! ✨
my_articles = article_one.get_articles() # -> [<Article object at 0x0...>, <Article object at 0x2...>]
my_articles[0].reporter # -> <Reporter object at 0x0...>
my_articles[0].title # -> "Why Epure is the best ORM?"
Magic
method and smart queries
Read more about magic
methods,
smart_queries
and advanced work with Models here
2. Shortcut read()
with **kwargs
parameters¶
More on shortcut .read()
method
Read more about retrieving data using kwargs with .read()
method here
Advanced example with smart query, for
and cat aliens 👽🐈¶
Example with creating long smart query with for
statement and other cool stuff
Check out more on such example where we create a long smart query using for
statement here
Example with join using JoinResource¶
What is join, JoinResource and how joining models works
Check out what is JoinResource and more advanced example with join : here
:
Elist and Eset: easy store, easy load collections¶
What is Elist and Eset
Elist and Eset is super convinient when: you want to store your items in just a regular list
or set
...
...but at the same time in DB 😋
Elist¶
Elist
- is simple tool to store small collections in single-user aplications.
You can look at Elist
like generic strictly typed list
with mechanism of easy saving and retrieving its contents from DB.
Elist
is a strictly typed list
because it can only store instances of subscripted (defined) for Elist
type
(e.g Elist[str]
can't store int
value)
Elist
is not multiuser-friendly
Elist
does not guarantee numerated order of big collections in multi-user aplications.
In cases of big collections with multiple users use Eset
!
Let's look at an example:
Elist
example for storing and retrieving items
We will create House
and District
classes. Both House
and District
will store a Elist
.
from epure import epure, Elist
@epure()
class House:
tenant_names:Elist[str]
house_number:int
street_name:str
def __init__(self, house_number, street_name, tenant_names):
self.house_number = house_number
self.street_name = street_name
self.tenant_names = tenant_names
@epure()
class District:
houses_list:Elist[House]
House
, add them to houses_list
(Elist
) of District
obj and save all them.
Saving object with Elist
or Eset
field
Note that saving object with Elist
field will triger saving for all Elist
or Eset
fields of this object respectivly.
house_one = House(42, "Crow Str.", Elist[str](["Mary", "Charles"]))
house_two = House(14, "Babbidge Str.", Elist[str](["Daniel"]))
district = District()
district.houses_list = Elist[House]([house_one, house_two])
district.save() # (1)!
- Saving this Epure instance with Elist field will triger saving for Elist
Then using .read()
we will get our District
object from DB (district_retrived
) using district.data_id
(unique UUID
id).
House
objects will be already present in house_list
Elist
district_retrived = District.resource.read(data_id = district.data_id)
district_retrived[0].houses_list[0] # -> [<House object at 0x0...>]
district_retrived[0].houses_list[0].tenant_names[0] # -> "Mary"
Elist
collection is really easy as you can see in this example
How to store Elist
outside of class, more examples and etc.
How to store Elist outside class field, examples on Elist here
Eset¶
Eset
is similar to Elist
, though (as much as set
differes from list
) Eset
has no order of its stored contents.
Because of the way Eset
is built, it is really convenient when working with big chunks of data! 🤤
You dont need to load whole Eset at a time: when Eset
is retrived from DB - it will be empty by default
and is easily loaded by .load()
method.
(In future Eset will support partial loading, based on specified properties of objects)
In a way, Eset
is Epure's interpretation of Many2Many field.
Eset with JSON
example for storing, loading and retrieving items
Let's declare Customer
and ShipmentCompany
.
Because ShipmentCompany.customers_set
Elist
type is object
- instances of Customer
will be stored as JSON
from epure import epure, Eset
class Customer:
name:str
adress:str
def __init__(self, name, adress):
self.name = name
self.adress = adress
@epure()
class ShipmentCompany:
customers_set:Eset[object]
offices_names:Eset[str] = Eset[str](["Charlie", "Bravo", "Alpha"])
Eset
allows to store copious amounts of data, so lets load it up!
Storing loads of instances in big_customers_set
customer_1 = Customer("Sion Mccall", "Heathfield Road, 30")
customer_2 = Customer("Darcy Montes", "Ash Street, 13")
customer_3 = Customer("Isaiah Hughes", "Grasmere Avenue, 19")
customer_4 = Customer("Jodie Sandoval", " Meadow Rise, 15")
...
...
...
customer_50 = Customer("Emily Mahoney", "Park Road, 62")
customer_51 = Customer("Inaaya Hodge", "Rectory Lane, 42")
customer_52 = Customer("Blanche Colon", "Ferndale Road, 10")
customer_54 = Customer("Lucia Davenport", "Warwick Street, 56")
customer_55 = Customer("Vivian Lin", "Carlton Road, 23")
customer_56 = Customer("Kane Flores", "Beaufort Road, 87")
big_customers_set = Eset[object]([customer_1, ..., customer_56])
We will create our shipment_company
and assign big_customers_set
to its customers_set
, then we will save it
shipment_company = ShipmentCompany()
shipment_company.customers_set = big_customers_set
shipment_company.save() # (1)!
read()
, and as you can see you need to load()
Eset
for it to be sub-loaded.
retrieved_shipment_company = EsetExample.resource.read(data_id = eset_ex.data_id)[0]
empty_eset = retrieved_shipment_company.customers_set # -> {}
loaded_set = empty_eset.load() # (2)!
loaded_set # -> {<Customer object at 0x0...>, <Customer object at 0x0...>, ...}
empty_set = retrieved_shipment_company.offices_names # -> {}
loaded_set = empty_set.load() # (2)!
" ".join(loaded_set) # -> "Alpha Charlie Bravo"
- Note that saving this will triger recursive saving for all elists and esets bounded to this object
- Eset is empty when is retrieved, you need to use .load() method of Eset to fill the eset with its content.
Read more on Eset
More info and examples on Eset here
JSON
and dict
serialization and deserialization of Epure objects¶
Epure allows to serialize your "epurized" class object
to JSON
using .to_dict()
and .to_json()
methods
As much as it allows to deserialize JSON
back to a Epure object
using .from_dict()
and .from_json()
More on JSON
and dict
serialization and deserialization
Ini File Parser¶
This section of library appeared mainly because there is no adequate solution for working with ini files in python
File Ini Parser allows to easily work with sections of .ini
file using dot (.
) notation
Using Ini File Parser
Save this as example.ini file:
Now we can easily access fields of this ini file:
Learn more
Learn more about Ini Parser here
Developers¶
Nikita Umarov (Pichugin), Pavel Pichugin
Sponsor us¶
Links¶
- Documentation: https://github.com/nagvalhm/epure/blob/main/README.md
- Changes: https://github.com/nagvalhm/epure
- PyPI Releases: https://pypi.org/project/epure/
- Source Code: https://github.com/nagvalhm/epure
- Issue Tracker: https://github.com/nagvalhm/epure/issues
- Website: https://pypi.org/project/epure/