Django 点击数据库后端。
Django clickhouse backend is a django database backend for clickhouse database. This project allows using django ORM to interact with clickhouse, the goal of the project is to operate clickhouse like operating mysql, postgresql in django.
Thanks to clickhouse driver, django clickhouse backend use it as DBAPI. Thanks to clickhouse pool, it makes clickhouse connection pool.
Read Documentation for more.
Features:
numpy objects.Notes:
ForeignKey, ManyToManyField and OneToOneField can be used with clickhouse backend, but no database level constraints will be added, so there could be some consistency problems.Requirements:
$ pip install django-clickhouse-backend
or
$ git clone https://github.com/jayvynl/django-clickhouse-backend
$ cd django-clickhouse-backend
$ python setup.py install
Only ENGINE is required in database setting, other options have default values.
clickhouse_backend.backend.default.localhost.9000.default.In the most cases, you may just use clickhouse to store some big events tables, and use some RDBMS to store other tables. Here I give an example setting for clickhouse and postgresql.
INSTALLED_APPS = [
# ...
"clickhouse_backend",
# ...
]
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"HOST": "localhost",
"USER": "postgres",
"PASSWORD": "123456",
"NAME": "postgres",
},
"clickhouse": {
"ENGINE": "clickhouse_backend.backend",
"NAME": "default",
"HOST": "localhost",
"USER": "DB_USER",
"PASSWORD": "DB_PASSWORD",
}
}
DATABASE_ROUTERS = ["dbrouters.ClickHouseRouter"]
# dbrouters.py
from clickhouse_backend.models import ClickhouseModel
def get_subclasses(class_):
classes = class_.__subclasses__()
index = 0
while index show create table django_migrations;
CREATE TABLE other.django_migrations
(
`id` Int64,
`app` FixedString(255),
`name` FixedString(255),
`applied` DateTime64(6, 'UTC')
)
ENGINE = MergeTree
ORDER BY id
SETTINGS index_granularity = 8192
we can query it with results like this
> select * from django_migrations;
┌──────────────────id─┬─app─────┬─name─────────┬────────────────────applied─┐
│ 1626937818115211264 │ testapp │ 0001_initial │ 2023-02-18 13:32:57.538472 │
└─────────────────────┴─────────┴──────────────┴────────────────────────────┘
migrate will create a table with name event as we define in the models
…
create
for i in range(10):
Event.objects.create(ip_nullable=None, port=i,
protocol="HTTP", content="test",
action=Event.Action.PASS.value)
assert Event.objects.count() == 10
query
queryset = Event.objects.filter(content="test")
for i in queryset:
print(i)
update
Event.objects.filter(port__in=[1, 2, 3]).update(protocol="TCP")
time.sleep(1)
assert Event.objects.filter(protocol="TCP").count() == 3
delete
Event.objects.filter(protocol="TCP").delete()
time.sleep(1)
assert not Event.objects.filter(protocol="TCP").exists()
Except for the model definition, all other operations are like operating relational databases such as mysql and postgresql
Writing testcase is all the same as normal django project. You can use django TestCase or pytest-django. Notice: clickhouse use mutations for deleting or updating. By default, data mutations is processed asynchronously. That is, when you update or delete a row, clickhouse will perform the action after a period of time. So you should change this default behavior in testing for deleting or updating. There are 2 ways to do that:
mutations_sync=1 at session scope.DATABASES = {
"default": {
"ENGINE": "clickhouse_backend.backend",
"OPTIONS": {
"settings": {
"mutations_sync": 1,
}
}
}
}
Event.objects.filter(protocol="UDP").settings(mutations_sync=1).delete()
Sample test case.
from django.test import TestCase
class TestEvent(TestCase):
databases = {"default", "clickhouse"}
def test_spam(self):
assert Event.objects.count() == 0
This backend support distributed DDL queries (ON CLUSTER clause) and distributed table engine.
The following example assumes that a cluster defined by docker compose in this repository is used.
This cluster name is cluster, it has 2 shards, every shard has 2 replica.
numpy objectsclickhouse-driver allows results to be returned as columns and/or deserialized into
numpy objects. This backend supports both options by using the context manager,
Cursor.set_query_execution_args().
…
…
Extra settings explanation:
"migration_cluster": "cluster"
Migration table will be created on this cluster if this setting is specified, otherwise only local migration table is created.
"mutations_sync": 2
This is suggested if you want to test data mutations on replicated table.
Don't set this in production environment.
"insert_distributed_sync": 1
This is suggested if you want to test inserting data into distributed table.
Don't set this in production environment.
"insert_quorum": 2
This is suggested if you want to test inserting data into replicated table.
The value is set to replica number.
"alter_sync": 2
This is suggested if you want to test altering or truncating replicated table.
Don't set this in production environment.
"TEST": {"cluster": "cluster", "managed": False, "DEPENDENCIES": ["default"]}
Test database will be created on this cluster.
If you have multiple database connections to the same cluster and want to run tests over all these connections,
then only one connection should set "managed": True(the default value), other connections should set "managed": False.
So that test database will not be created multiple times.
If your managed database alias is s1r2 instead default, "DEPENDENCIES": ["s1r2"] should be set to ensure the creation order for test databases.
Do not hardcode database name when you define replicated table or distributed table. Because test database name is different from deployed database name.
If your clickhouse cluster is running behind a load balancer, you can optionally set distributed_migrations to True under database OPTIONS.
Then a distributed migration table will be created on all nodes of the cluster, and all migration operations will be performed on this
distributed migrations table instead of a local migrations table. Otherwise, sequentially running migrations will have no effect on other nodes.
Configuration example:
DATABASES = {
"default": {
"HOST": "clickhouse-load-balancer",
"PORT": 9000,
"ENGINE": "clickhouse_backend.backend",
"OPTIONS": {
"migration_cluster": "cluster",
"distributed_migrations": True,
"settings": {
"mutations_sync": 2,
"insert_distributed_sync": 1,
"insert_quorum": 2,
"alter_sync": 2,
},
},
}
}
cluster in Meta class will make models being created on cluster.
…
Just like normal table, you can do whatever you like to distributed table.
students = DistributedStudent.objects.bulk_create([DistributedStudent(name=f"Student{i}", score=i * 10) for i in range(10)])
assert DistributedStudent.objects.count() == 10
DistributedStudent.objects.filter(id__in=[s.id for s in students[5:]]).update(name="lol")
DistributedStudent.objects.filter(id__in=[s.id for s in students[:5]]).delete()
If migration_cluster is not specified in database configuration. You should always run migrating on one specific cluster node.
Because other nodes do not know whether migrations have been applied by any other node.
If migration_cluster is
暂无开放 Issues,或尚未同步最近议题。