A simple and async json database.
Project description
simple-json-db
This is a simple json database. The package provides a simple ORM between python objects and json objects with a well type-hinted schema.
This package maps your python objects to json and then you can save, get, modify or delete them using async methods.
This package is for tiny and simple projects. with a low amount of data.
Intro
Let's see how you can get started with the package.
Create Model
Step #1 is to create a model that represents something like a row in a table.
from sjd.entity.properties import IntProperty, StrProperty
from sjd.entity import TEntity
class Student(TEntity):
student_id = IntProperty(required=True)
first_name = StrProperty(required=True)
last_name = StrProperty(required=True)
- Your model should inherit from
TEntity
. - Properties can be anything that JSON supports (
int
,str
for now).
Initialize db
We have an Engine
and some Collection
s. The first one is your database, the second is your table.
from sjd.database import Engine, __Collection__
class AppEngine(Engine):
students = __Collection__(Student)
def __init__(self):
super().__init__(Path("__test_db__"))
- The engine will create a directory named
__test_db__
. - inside
__test_db__
the collections are stored. - You SHOULD pass the type of your entity (
entity_type
) to the__Collection__
. Here isStudent
. - type
__Collection__
is a descriptor! The actual thing isCollection
. - The collection name will be class variable's name (
students
here ).
Create engine instance
After setting up engine, you can create an instance of it. And you have access to the collections.
engine = AppEngine()
collection = engine.students
Construct first data
Let's create our first data.
student = Student()
student.student_id = 123456789
student.first_name = "Arash"
student.last_name = "Enzo"
Add data
Save data.
await collection.add(student)
Good job, you've saved your first data.
Better model
Personally, i prefer classes with initializers. so let's add an __init__
to the Student
.
# ---- sniff ----
class Student(TEntity):
__json_init__ = True
student_id = IntProperty(required=True)
first_name = StrProperty(required=True)
last_name = StrProperty()
def __init__(self, student_id: int, first_name: str, last_name: str):
self.student_id = student_id
self.first_name = first_name
self.last_name = last_name
- For models with
__init__
that accepts some parameters, you SHOULD include__json_init__ = True
or it will fail while deserializing the data.
Add more data
Now we can create more students quickly, so we need to add them quickly as well.
await collection.add_many(
Student(1, "John", "Doe"),
Student(2, "Jane", "Doe"),
Student(3, "Jack", "Doe"),
Student(4, "Jill", "Doe"),
)
Query data
Let's do some query stuff to get data we want.
Iterate over all.
async for student in collection:
print(student.first_name)
# John
# Jane
# Jack
# Jill
Get data with some filters.
async for student in collection.as_queryable.where(
lambda s: s.last_name == "Doe"
):
print(student.first_name)
# John
# Jane
# Jack
# Jill
Ops they were all "Doe".
async for student in collection.as_queryable.where(
lambda s: s.first_name == "John"
):
print(student.first_name)
# John
If it's going to be one, we have more options.
student = await collection.as_queryable.first(lambda s: s.first_name == "John")
print(student.first_name)
# John
And some more ...
Type hint are fully available.
Update data
I guess the name was Johnny not John 🤔, let's change the name of student John to Johnny.
async for student in collection:
if student.first_name == "John":
student.first_name = "Johnny"
collection.update(student)
Delete data
Now that i looked closer, we don't have any john or johnny at all, imma remove johnny then.
async for student in collection:
if student.first_name == "Johnny":
collection.delete(student)
Complex Properties
You can use more complex models! models that include Other models as property or a list of other models or builtin types.
Let's begin with creating another model called Grade
that includes some information about an student's grade in a course. We are going to add this to Student
later.
Since this model is an embed entity, You should inherit from EmbedEntity
.
from src.entity import TEntity, EmbedEntity
# ---- sniff ----
class Grade(EmbedEntity):
__json_init__ = True
course_id = IntProperty(required=True)
course_name = StrProperty(required=True)
score = IntProperty(required=True)
def __init__(self, course_id: int, course_name: str, score: int):
self.course_id = course_id
self.course_name = course_name
self.score = score
To add this as a new property to the Student
, we'll use ComplexProperty
. ( Or OptionalComplexProperty
for a complex property which is not required ).
Your Student
class should looks like this:
from src.entity.properties import IntProperty, StrProperty, OptionalComplexProperty
# ---- sniff ----
class Student(TEntity):
__json_init__ = True
student_id = IntProperty(required=True)
first_name = StrProperty(required=True)
last_name = StrProperty()
grade = OptionalComplexProperty(Grade)
def __init__(
self,
student_id: int,
first_name: str,
last_name: str,
grade: Optional[Grade] = None,
):
self.student_id = student_id
self.first_name = first_name
self.last_name = last_name
self.grade = grade
- Note that we passed
Grade
type as the first parameter to theOptionalComplexProperty
.
Now we can add this grade for all students
async for student in collection:
student.grade = Grade(1, "Math", 90)
await collection.update(student)
Let's check if it's working
jill = await collection.as_queryable.first(lambda s: s.first_name == "Jill")
if jill.grade:
print(jill.grade.course_name, jill.grade.score)
# Math
List Properties
As we all know, there may be more than one course per student! Then the grade property, can be grades.
from src.entity.properties import IntProperty, StrProperty, ListProperty
# ---- sniff ----
class Student(TEntity):
__json_init__ = True
student_id = IntProperty(required=True)
first_name = StrProperty(required=True)
last_name = StrProperty()
grades = ListProperty(Grade, default_factory=list)
def __init__(
self,
student_id: int,
first_name: str,
last_name: str,
grades: list[Grade] = [],
):
self.student_id = student_id
self.first_name = first_name
self.last_name = last_name
self.grades = grades
Let's update all of entities again.
async for student in collection:
student.grades = [Grade(1, "Math", 90), Grade(2, "English", 80)]
await collection.update(student)
# Math 90
# English 80
Let's change Jill's english score to 50.
jill = await collection.as_queryable.first(lambda s: s.first_name == "Jill")
if jill.grades:
jill.grades[-1].score = 50
await collection.update(jill)
Oh who's score was 50 ??!
with_50_score = await collection.as_queryable.where(
lambda s: any(x.score == 50 for x in s.grades)).single()
print(with_50_score.first_name)
That's all for now, check Collection
object for more ...
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for json_entity-0.0.0rc1-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | dda76cdabcc748301dae3bbe1523b31ce63d9f803acf91782c614cbe37cea67d |
|
MD5 | de1411cd55214ec4410a0f99d5a13a45 |
|
BLAKE2b-256 | 7fe44ccd0baba92cc2eb25770687a45f4de599b75122da0be0e064a7b7426cf7 |