You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Support [MergeTree settings](https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/mergetree#settings) in creating table.
4
+
- Support [distributed DDL](https://clickhouse.com/docs/en/sql-reference/distributed-ddl) and [distributed table](https://clickhouse.com/docs/en/engines/table-engines/special/distributed).
5
+
- Support create migration table and run migrating on cluster.
6
+
- Fix bug: exception is raised when insert data with expression values.
7
+
- Fix bug: exception is raised when alter field from not null to null.
8
+
- Support escaping dict data.
4
9
5
10
### 1.1.0
6
11
- Change `AutoFiled` and `SmallAutoField` to clickhouse `Int64`, so that id worker can generate value for them.
Django clickhouse backend is a [django database backend](https://docs.djangoproject.com/en/4.1/ref/databases/) for
6
-
[clickhouse](https://clickhouse.com/docs/en/home/) database. This project allows using django ORM to interact with
5
+
Django clickhouse backend is a [django database backend](https://docs.djangoproject.com/en/4.1/ref/databases/) for
6
+
[clickhouse](https://clickhouse.com/docs/en/home/) database. This project allows using django ORM to interact with
7
7
clickhouse, the goal of the project is to operate clickhouse like operating mysql, postgresql in django.
8
8
9
9
Thanks to [clickhouse driver](https://github.com/mymarilyn/clickhouse-driver), django clickhouse backend use it as [DBAPI](https://peps.python.org/pep-0249/).
- Not tested upon all versions of clickhouse-server, clickhouse-server 22.x.y.z or over is suggested.
28
28
- Aggregation functions result in 0 or nan (Not NULL) when data set is empty. max/min/sum/count is 0, avg/STDDEV_POP/VAR_POP is nan.
29
-
- In outer join, clickhouse will set missing columns to empty values (0 for number, empty string for text, unix epoch for date/datatime) instead of NULL.
29
+
- In outer join, clickhouse will set missing columns to empty values (0 for number, empty string for text, unix epoch for date/datatime) instead of NULL.
30
30
So Count("book") resolve to 1 in a missing LEFT OUTER JOIN match, not 0.
31
31
In aggregation expression Avg("book__rating", default=2.5), default=2.5 have no effect in a missing match.
32
32
- Clickhouse does not support unique constraint and foreignkey constraint. `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.
33
33
- Clickhouse does not support transaction. If any exception occurs during migrating, then your clickhouse database will be in an untracked state. Any migration should be full tested in test environment before deployed to production environment.
34
+
- This project does not support migrations of changing table engine and settings yet.
34
35
35
36
**Requirements:**
36
37
@@ -75,28 +76,29 @@ Here I give an example setting for clickhouse and postgresql.
75
76
76
77
```python
77
78
DATABASES= {
78
-
'default': {
79
-
'ENGINE': 'django.db.backends.postgresql',
80
-
'HOST': 'localhost',
81
-
'USER': 'postgres',
82
-
'PASSWORD': '123456',
83
-
'NAME': 'postgres',
79
+
"default": {
80
+
"ENGINE": "django.db.backends.postgresql",
81
+
"HOST": "localhost",
82
+
"USER": "postgres",
83
+
"PASSWORD": "123456",
84
+
"NAME": "postgres",
84
85
},
85
-
'clickhouse': {
86
-
'ENGINE': 'clickhouse_backend.backend',
87
-
'NAME': 'default',
88
-
'HOST': 'localhost',
89
-
'USER': 'DB_USER',
90
-
'PASSWORD': 'DB_PASSWORD',
86
+
"clickhouse": {
87
+
"ENGINE": "clickhouse_backend.backend",
88
+
"NAME": "default",
89
+
"HOST": "localhost",
90
+
"USER": "DB_USER",
91
+
"PASSWORD": "DB_PASSWORD",
91
92
}
92
93
}
93
-
DATABASE_ROUTERS= ['dbrouters.ClickHouseRouter']
94
+
DATABASE_ROUTERS= ["dbrouters.ClickHouseRouter"]
94
95
```
95
96
96
97
```python
97
98
# dbrouters.py
98
99
from clickhouse_backend.models import ClickhouseModel
99
100
101
+
100
102
defget_subclasses(class_):
101
103
classes = class_.__subclasses__()
102
104
@@ -118,21 +120,21 @@ class ClickHouseRouter:
118
120
119
121
defdb_for_read(self, model, **hints):
120
122
if (model._meta.label_lower inself.route_model_names
121
-
or hints.get('clickhouse')):
122
-
return'clickhouse'
123
+
or hints.get("clickhouse")):
124
+
return"clickhouse"
123
125
returnNone
124
126
125
127
defdb_for_write(self, model, **hints):
126
128
if (model._meta.label_lower inself.route_model_names
@@ -207,7 +209,7 @@ class Event(models.ClickhouseModel):
207
209
]
208
210
constraints = (
209
211
CheckConstraint(
210
-
name='port_range',
212
+
name="port_range",
211
213
check=Q(port__gte=0, port__lte=65535),
212
214
),
213
215
)
@@ -289,7 +291,7 @@ create
289
291
```python
290
292
for i inrange(10):
291
293
Event.objects.create(ip_nullable=None, port=i,
292
-
protocol="HTTP", content="test",
294
+
protocol="HTTP", content="test",
293
295
action=Event.Action.PASS.value)
294
296
assert Event.objects.count() ==10
295
297
```
@@ -332,31 +334,205 @@ There are 2 ways to do that:
332
334
- Config database engine as follows, this sets [`mutations_sync=1`](https://clickhouse.com/docs/en/operations/settings/settings#mutations_sync) at session scope.
333
335
```python
334
336
DATABASES= {
335
-
'default': {
336
-
'ENGINE': 'clickhouse_backend.backend',
337
-
'OPTIONS': {
338
-
'settings': {
339
-
'mutations_sync': 1,
337
+
"default": {
338
+
"ENGINE": "clickhouse_backend.backend",
339
+
"OPTIONS": {
340
+
"settings": {
341
+
"mutations_sync": 1,
340
342
}
341
343
}
342
344
}
343
345
}
344
346
```
345
347
- Use [SETTINGS in SELECT Query](https://clickhouse.com/docs/en/sql-reference/statements/select/#settings-in-select-query).
This backend support [distributed DDL queries (ON CLUSTER clause)](https://clickhouse.com/docs/en/sql-reference/distributed-ddl)
367
+
and [distributed table engine](https://clickhouse.com/docs/en/engines/table-engines/special/distributed).
368
+
369
+
The following example assumes that a cluster defined by [docker compose in this repository](https://github.com/jayvynl/django-clickhouse-backend/blob/main/compose.yaml) is used.
370
+
This cluster name is `cluster`, it has 2 shards, every shard has 2 replica.
371
+
372
+
### Configuration
373
+
374
+
```python
375
+
DATABASES= {
376
+
"default": {
377
+
"ENGINE": "clickhouse_backend.backend",
378
+
"OPTIONS": {
379
+
"migration_cluster": "cluster",
380
+
"settings": {
381
+
"mutations_sync": 1,
382
+
"insert_distributed_sync": 1,
383
+
},
384
+
},
385
+
"TEST": {"cluster": "cluster"},
386
+
},
387
+
"s1r2": {
388
+
"ENGINE": "clickhouse_backend.backend",
389
+
"PORT": 9001,
390
+
"OPTIONS": {
391
+
"migration_cluster": "cluster",
392
+
"settings": {
393
+
"mutations_sync": 1,
394
+
"insert_distributed_sync": 1,
395
+
},
396
+
},
397
+
"TEST": {"cluster": "cluster", "managed": False},
398
+
},
399
+
"s2r1": {
400
+
"ENGINE": "clickhouse_backend.backend",
401
+
"PORT": 9002,
402
+
"OPTIONS": {
403
+
"migration_cluster": "cluster",
404
+
"settings": {
405
+
"mutations_sync": 1,
406
+
"insert_distributed_sync": 1,
407
+
},
408
+
},
409
+
"TEST": {"cluster": "cluster", "managed": False},
410
+
},
411
+
"s2r2": {
412
+
"ENGINE": "clickhouse_backend.backend",
413
+
"PORT": 9003,
414
+
"OPTIONS": {
415
+
"migration_cluster": "cluster",
416
+
"settings": {
417
+
"mutations_sync": 1,
418
+
"insert_distributed_sync": 1,
419
+
},
420
+
},
421
+
"TEST": {"cluster": "cluster", "managed": False},
422
+
},
423
+
}
424
+
```
425
+
426
+
Extra settings explanation:
427
+
428
+
-`"migration_cluster": "cluster"`
429
+
Migration table will be created on this cluster if this setting is specified, otherwise only local migration table is created.
430
+
-`"mutations_sync": 1`
431
+
This is suggested if you want to test [data mutations](https://clickhouse.com/docs/en/guides/developer/mutations).
432
+
-`"insert_distributed_sync": 1`
433
+
This is suggested if you want to test inserting data into distributed table.
Just like normal table, you can do whatever you like to distributed table.
481
+
482
+
```python
483
+
students = DistributedStudent.objects.bulk_create([DistributedStudent(name=f"Student{i}", score=i *10) for i inrange(10)])
484
+
assert DistributedStudent.objects.count() ==10
485
+
DistributedStudent.objects.filter(id__in=[s.id for s in students[5:]]).update(name="lol")
486
+
DistributedStudent.objects.filter(id__in=[s.id for s in students[:5]]).delete()
487
+
```
488
+
489
+
### Migrate
490
+
491
+
If `migration_cluster` is not specified in database configuration. You should always run migrating on one specific cluster node.
492
+
Because other nodes do not know whether migrations have been applied by any other node.
493
+
494
+
If `migration_cluster` is specified. Then migration table(named `django_migrations`) will be created on the specified cluster.
495
+
When applied, [migration options](https://docs.djangoproject.com/en/4.2/ref/migration-operations/) of model with cluster defined in `Meta` class
496
+
will be executed on cluster, other migration options will be executed locally.
497
+
This means distributed table will be created on all nodes as long as any node has applied the migrations.
498
+
Other local table will only be created on node which has applied the migrations.
499
+
500
+
If you want to use local table in all nodes, you should apply migrations multiple times on all nodes.
501
+
But remember, these local tables store data separately, currently this backend do not provide means to query data from other nodes.
502
+
503
+
```shell
504
+
python manage.py migrate
505
+
python manage.py migrate --database s1r2
506
+
python manage.py migrate --database s2r1
507
+
python manage.py migrate --database s2r2
508
+
```
509
+
510
+
### Update
511
+
512
+
When updated from django clickhouse backend 1.1.0 or lower, you should not add cluster related settings to your
513
+
existing project. Because:
514
+
515
+
- Migration table schema won't be changed if you add or remove `migration_on_cluster`. And `python mange.py migrate` will work abnormally.
516
+
- If you add cluster to your existing model's `Meta` class, no schema changes will occur, this project does not support this yet.
517
+
518
+
If you really want to use cluster feature with existing project, you should manage schema changes yourself.
519
+
These steps should be tested carefully in test environment.
520
+
[Clickhouse docs](https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/replication#converting-from-mergetree-to-replicatedmergetree) may be helpful.
521
+
522
+
1. Apply all your existing migrations.
523
+
2. Change your settings and model.
524
+
3. Generate new migrations.
525
+
4. Log into your clickhouse database and change table schemas to reflect your models.
526
+
5. Apply migrations with fake flag.
527
+
528
+
```shell
529
+
python manage.py migrate
530
+
# Change your settings and model
531
+
python manage.py makemigrations
532
+
# Log into your clickhouse database and change table schemas to reflect your models.
0 commit comments