This library is designed to be used as an Active Record style Object Relational Mapper for EdgeDB. A ClassRegistry
object is built from a combination of EdgeDB schema and the @register
family of decorators, which allows for merging class definition and database schema. This means you're able to add an overlay of your own class methods and properties that otherwise wouldn't exist. New instances can be created by calling edgewise.new_doc('Object')
. Existing objects can be fetched with edgewise.get_doc('Object', filters)
using either the object's UUID or a dictionary of filters filtering on a property ({'name': 'Magic'}
)
- Getting Started
- Prerequisites
- Why?
- Installing
- Using Edgewise
- How it Works
- Basic Usage - Objects
- Basic Usage - Enums and Custom Scalars
- Basic Usage - Tuples and Named Tuples
- Async is Awesome!
- EdgeDB Configuration
- Environment Variables
- Extras
- Timestamping
- Password Scalar
- Developing with Edgewise
- Examples
- Quart example
- Dependencies and Tooling
- About the Test Suite
- Contributing
- Versioning
- Author(s)
- License
- Examples
This library uses python >3.8 and requires that EdgeDB be installed. Follow the installation instructions given in the EdgeDB documentation.
This library asyncio by default and generally defers to it whenever possible. See the async is awesome and Quart example for more details.
The EdgeDB python library is intended to be relatively low-level and high performance. This library is designed to be intuitive and help you access and manipulate documents quickly in a object oriented style. If you're doing a large application with EdgeDB you should be prepared to use both libraries.
In my experience, EdgeDB SDL is one of the most intuitive ways to define schema, especially one with nested objects/tables and types. This library takes the approach that you should design your schema in its native language (EdgeDB SDL or DDL) and access it in the way that's easiest for you
This library is not yet on pypi
python -m pip install edgewise
If you've read this far you know that you should use a virtual environment and you should do that however you like. But you should do it. For development this project uses Poetry.
This library first inspects your EdgeDB with a schema query and builds classes and scalars from that with the help of the attrs library. In formal computer science this is called an Inversion of Control pattern. You can create a new instance of a class by calling edgewise.new_doc('Object')
where 'Object' is the name of your EdgeDB object. All edgewise classes inherit from Document
which provides several useful abstractions for you.
Python 3.8 allows for async/await in the repl, which can be activated thusly, as noted on the Python Software Foundation's Developing with asyncio
page.
python -X dev
Now you can try out Edgewise. The schema for these examples is available in the test suite.
>>> import edgewise
>>> doc = edgewise.get_doc('Company', {'name':'Fancy Business'})
>>> doc # let's see it in the repl
Company(id=UUID('fbc7933e-49da-11ea-9634-efbf1f5f7fea'), __edbmodule__='example',
name='Fancy Business', country='Monaco') # that is fancy!
>>> doc.country = 'Cayman Islands' # assign a new value to the 'country' attribute
>>> doc.save() # saves to the database, no fuss
>>> doc.delete() # gone forever
>>> new_company = edgewise.new_doc("Company")
>>> new_company
Company(id=None,__edbmodule__='example', name=None, country=None)
If you'd like to extend a database object, you can do that with the help of some decorators.
Note: @attrs
is required for inheritance to work correctly. To ensure version compatibility import attrs
from edgewise
.
from edgewise import attrs, Document, register, register_with_schema
@attrs
@register_with_schema(module='example')
class Company(Document):
def your_class_method(self) -> str:
return "A class method method!"
register_with_schema(module)
allows you add your own class methods to the object.
>>> new_company = edgewise.new_doc("Company")
>>> new_company
Company(_id=None, __edbmodule__='example', name=None, country=None)
>>> new_company.test_this_class_method()
'A class method!'
You can also make classes that don't need database access available in the ClassRegistry:
@attrs
@register
class DocumentNotInDatabase(Document):
def connect_to_filesystem(self) -> typing.NoReturn:
pass
In this case, you will probably want to provide the edgewise
APIs you'd expect to see: _load
(called by get_doc
), save
and delete
; only new_doc
will work out of the box.
EdgeDB allows you define custom scalars and enums, Edgewise supports mapping these to python representations. Enums will use the Edgewise DefaultEnum
class. Scalars will use the CustomScalar class. CustomScalars are extensible with a decorator. You can also put a custom object into the registry with the @register_scalar
decorator.
@attrs
@register_scalar
class RandomScalar(CustomScalar):
def print_somthing(self):
return f"Something!"
@attrs
@register_scalar_with_schema(module='example')
class Password(CustomScalar):
def print_password(self):
return f"Password(******)"
Using this example enum definition will yield a python enum object with the ability to also store a default value.
scalar type Color extending enum<'black', 'white', 'red'>;
>>> import edgewise
>>> colors = edgewise.new_scalar('Color')
>>> colors
<enum 'Color'> # The default enum __repr__ is useless
>>> colors.black
Color(name='black', value=1, default=True) # this repr is provided by DefaultEnum
>>> colors.white
Color(name='white', value=2, default=False)
>>> colors.default()
'black'
If edgewise encounters a tuple or named tuple scalar in one of your objects, it checks to see if you have a custom definition for that in the scalar registry. This can be accessed with tuple(object, property) as the key.
Schema example:
WIP
You'll want to configure a user with administrative permissions. For example:
It is generally considered good practice to save database passwords as environmental variables.
Have a look at the roadmap, which is about as useful as a streetmap of Boston.
Examples are available in the examples folder. Some of these will be covered in the tutorial documentation.
If you're considering integrating this library into a framework or project, please consider contributing your changes and/or add an example implementation to the test suite so that breaking changes can be discovered before they mess up your project.
For production code this project notably leverages attrs
and the EdgeDB python client. You will want to become familiar with how to use and what's underneath attrs
in order to get the most out of edgewise.
For testing and development, please refer to pyproject.toml for more information.
Why DepHell? It's agnostic, feature-rich and lets you choose the formats you want. It could be named better, but that's one the two hardest things in programming [insert meme]. I've had mostly good experiences with Pipenv, though it seems like I might be in the minority there. I've had mostly frustrating experiences with Poetry and again, I think I'm in the minority there as well. If you're considering contributing, I will have an article about my approach to environments and dependency management so you can understand where I'm coming from.
- What's tested and why (to do)
- Using the test suite database to get started (to do)
- Save your schema, class registrations and scalars in an organized way
- See Projects
Contributions are welcome, please refer to the wiki regarding:
- Opening Issues
- Requesting support
This project uses will use SemVer for versioning. For the versions available, see the tags on this repository.
- Tyler Matteson - Initial work - AgriTheory
This project uses Apache License Version 2.0. See license.md file for details