-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
1,140 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
on: [push] | ||
|
||
name: unittest | ||
|
||
jobs: | ||
|
||
unittest: | ||
runs-on: ubuntu-latest | ||
name: Unittest with pytest | ||
services: | ||
cassandra: | ||
image: cassandra | ||
ports: | ||
- 9042:9042 | ||
env: | ||
CASS_DRIVER_NO_CYTHON: 1 | ||
|
||
steps: | ||
- uses: actions/checkout@v1 | ||
- uses: actions/setup-python@v1 | ||
with: | ||
python-version: '3.x' | ||
- run: | | ||
pip install poetry | ||
poetry install | ||
- name: Run test | ||
run: poetry run pytest |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
# aiocqlengine | ||
Async wrapper for cqlengine of cassandra python driver. | ||
|
||
This project is built on [cassandra-python-driver](https://github.com/datastax/python-driver) and [aiocassandra](https://github.com/aio-libs/aiocassandra). | ||
|
||
[![Actions Status](https://github.com/charact3/aiocqlengine/workflows/unittest/badge.svg)](https://github.com/charact3/aiocqlengine/actions) | ||
|
||
|
||
## Get Started | ||
|
||
```python | ||
import asyncio | ||
import uuid | ||
import os | ||
|
||
from aiocassandra import aiosession | ||
from aiocqlengine.models import AioModel | ||
from cassandra.cluster import Cluster | ||
from cassandra.cqlengine import columns, connection, management | ||
|
||
cluster = Cluster() | ||
session = cluster.connect() | ||
|
||
|
||
class User(AioModel): | ||
user_id = columns.UUID(primary_key=True) | ||
username = columns.Text() | ||
|
||
|
||
async def main(): | ||
aiosession(session) | ||
|
||
# Set aiosession for cqlengine | ||
session.set_keyspace('example_keyspace') | ||
connection.set_session(session) | ||
|
||
# Model.objects.create() and Model.create() in async way: | ||
user_id = uuid.uuid4() | ||
await User.objects.async_create(user_id=user_id, username='user1') | ||
# also can use: await User.async_create(user_id=user_id, username='user1) | ||
|
||
# Model.objects.all() and Model.all() in async way: | ||
print(list(await User.async_all())) | ||
print(list(await User.objects.filter(user_id=user_id).async_all())) | ||
|
||
# Model.object.update() in async way: | ||
await User.objects(user_id=user_id).async_update(username='updated-user1') | ||
|
||
# Model.objects.get() and Model.get() in async way: | ||
user = await User.objects.async_get(user_id=user_id) | ||
assert user.user_id == (await User.async_get(user_id=user_id)).user_id | ||
print(user, user.username) | ||
|
||
# obj.save() in async way: | ||
user.username = 'saved-user1' | ||
await user.async_save() | ||
|
||
# obj.delete() in async way: | ||
await user.async_delete() | ||
|
||
# Didn't break original functions | ||
print('Left users: ', len(User.objects.all())) | ||
|
||
|
||
def create_keyspace(keyspace): | ||
os.environ['CQLENG_ALLOW_SCHEMA_MANAGEMENT'] = 'true' | ||
connection.register_connection('cqlengine', session=session, default=True) | ||
management.create_keyspace_simple(keyspace, replication_factor=1) | ||
management.sync_table(User, keyspaces=[keyspace]) | ||
|
||
|
||
create_keyspace('example_keyspace') | ||
|
||
loop = asyncio.get_event_loop() | ||
loop.run_until_complete(main()) | ||
cluster.shutdown() | ||
loop.close() | ||
|
||
``` | ||
|
||
|
||
## License | ||
This project is under MIT license. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
__version__ = "0.1.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
from cassandra.cqlengine.models import Model, PolymorphicModelException | ||
from cassandra.cqlengine.query import ValidationError | ||
|
||
from aiocqlengine.query import AioDMLQuery, AioQuerySet | ||
|
||
|
||
class AioModel(Model): | ||
__abstract__ = True | ||
__dmlquery__ = AioDMLQuery | ||
__queryset__ = AioQuerySet | ||
|
||
@classmethod | ||
async def async_create(cls, **kwargs): | ||
extra_columns = set(kwargs.keys()) - set(cls._columns.keys()) | ||
if extra_columns: | ||
raise ValidationError( | ||
"Incorrect columns passed: {0}".format(extra_columns)) | ||
return await cls.objects.async_create(**kwargs) | ||
|
||
async def async_delete(self): | ||
""" | ||
Deletes the object from the database | ||
""" | ||
await self.__dmlquery__( | ||
self.__class__, | ||
self, | ||
batch=self._batch, | ||
timestamp=self._timestamp, | ||
consistency=self.__consistency__, | ||
timeout=self._timeout, | ||
conditional=self._conditional, | ||
if_exists=self._if_exists, | ||
).async_delete() | ||
|
||
@classmethod | ||
async def async_all(cls): | ||
""" | ||
Returns a queryset representing all stored objects. | ||
This is a pass-through to the model objects().async_all() | ||
""" | ||
return await cls.objects.async_all() | ||
|
||
@classmethod | ||
async def async_get(cls, *args, **kwargs): | ||
""" | ||
Returns a single object based on the passed filter constraints. | ||
This is a pass-through to the model objects(). | ||
:method:`~cqlengine.queries.get`. | ||
""" | ||
return await cls.objects.async_get(*args, **kwargs) | ||
|
||
async def async_save(self): | ||
# handle polymorphic models | ||
if self._is_polymorphic: | ||
if self._is_polymorphic_base: | ||
raise PolymorphicModelException( | ||
"cannot save polymorphic base model") | ||
else: | ||
setattr(self, self._discriminator_column_name, | ||
self.__discriminator_value__) | ||
|
||
self.validate() | ||
await self.__dmlquery__( | ||
self.__class__, | ||
self, | ||
batch=self._batch, | ||
ttl=self._ttl, | ||
timestamp=self._timestamp, | ||
consistency=self.__consistency__, | ||
if_not_exists=self._if_not_exists, | ||
conditional=self._conditional, | ||
timeout=self._timeout, | ||
if_exists=self._if_exists, | ||
).async_save() | ||
|
||
self._set_persisted() | ||
self._timestamp = None | ||
return self | ||
|
||
async def async_update(self, **values): | ||
""" | ||
Performs an update on the model instance. You can pass in values to | ||
set on the model for updating, or you can call without values to | ||
execute an update against any modified fields. | ||
If no fields on the model have been modified since loading, | ||
no query will be performed. Model validation is performed normally. | ||
Setting a value to `None` is equivalent to running a CQL `DELETE` on | ||
that column. | ||
It is possible to do a blind update, that is, to update a field without | ||
having first selected the object out of the database. | ||
See :ref:`Blind Updates <blind_updates>` | ||
""" | ||
for column_id, v in values.items(): | ||
col = self._columns.get(column_id) | ||
|
||
# check for nonexistant columns | ||
if col is None: | ||
raise ValidationError( | ||
"{0}.{1} has no column named: {2}".format( | ||
self.__module__, self.__class__.__name__, column_id)) | ||
|
||
# check for primary key update attempts | ||
if col.is_primary_key: | ||
current_value = getattr(self, column_id) | ||
if v != current_value: | ||
raise ValidationError( | ||
"Cannot apply update to primary key '{0}' for {1}.{2}". | ||
format(column_id, self.__module__, | ||
self.__class__.__name__)) | ||
|
||
setattr(self, column_id, v) | ||
|
||
# handle polymorphic models | ||
if self._is_polymorphic: | ||
if self._is_polymorphic_base: | ||
raise PolymorphicModelException( | ||
"cannot update polymorphic base model") | ||
else: | ||
setattr(self, self._discriminator_column_name, | ||
self.__discriminator_value__) | ||
|
||
self.validate() | ||
await self.__dmlquery__( | ||
self.__class__, | ||
self, | ||
batch=self._batch, | ||
ttl=self._ttl, | ||
timestamp=self._timestamp, | ||
consistency=self.__consistency__, | ||
conditional=self._conditional, | ||
timeout=self._timeout, | ||
if_exists=self._if_exists, | ||
).async_update() | ||
|
||
self._set_persisted() | ||
|
||
self._timestamp = None | ||
|
||
return self |
Oops, something went wrong.