Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
run: uv sync --dev

- name: Lint
run: uv run black . --check
run: uv run ruff check .

- name: Run tests
run: uv run pytest
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,4 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/

*.lock
2 changes: 1 addition & 1 deletion DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ uv run pytest
## Lint

```bash
uv run black . --check
uv run ruff check .
```

## Build
Expand Down
16 changes: 12 additions & 4 deletions cratedb_django/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ class DatabaseWrapper(BaseDatabaseWrapper):

CRATE_SQL_AUTO_BIGINT = "bigint default (random() * 2^63-1)::bigint"
CRATE_SQL_AUTO_INTEGER = "integer default (random() * 2^31-1)::integer"
CRATE_SQL_AUTO_SMALLINTEGER = "smallint default (random() * 32767)::smallint"
CRATE_SQL_AUTO_SMALLINTEGER = (
"smallint default (random() * 32767)::smallint"
)
data_types = {
# todo pgdiff - doc
"AutoField": CRATE_SQL_AUTO_INTEGER,
Expand Down Expand Up @@ -119,16 +121,22 @@ def commit(self):

def _set_autocommit(self, autocommit):
with self.wrap_database_errors:
self.connection.autocommit = False # Forcibly set autocommit to False.
self.connection.autocommit = (
False # Forcibly set autocommit to False.
)

def get_connection_params(self):
VALID_OPTIONS = {"verify_ssl_cert"}

options: Optional[dict[str, str]] = self.settings_dict.get("OPTIONS", None)
options: Optional[dict[str, str]] = self.settings_dict.get(
"OPTIONS", None
)
if options:
for key in options:
if key not in VALID_OPTIONS:
raise ImproperlyConfigured(f"Unexpected OPTIONS parameter {key}")
raise ImproperlyConfigured(
f"Unexpected OPTIONS parameter {key}"
)

if self.settings_dict.get("PORT"):
raise ImproperlyConfigured(
Expand Down
4 changes: 2 additions & 2 deletions cratedb_django/compiler.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.db.models.sql.compiler import (
SQLCompiler,
) # noqa -- Import needed for re-imports.
SQLCompiler, # noqa: F401 Import needed for re-imports.
)

from django.db.models.sql.compiler import (
SQLInsertCompiler,
Expand Down
4 changes: 3 additions & 1 deletion cratedb_django/fields/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ class PositiveIntegerField(CrateDBBaseField, fields.PositiveIntegerField):
pass


class PositiveSmallIntegerField(CrateDBBaseField, fields.PositiveSmallIntegerField):
class PositiveSmallIntegerField(
CrateDBBaseField, fields.PositiveSmallIntegerField
):
pass


Expand Down
5 changes: 4 additions & 1 deletion cratedb_django/introspection.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import crate.client.cursor
from django.db.backends.base.introspection import BaseDatabaseIntrospection, TableInfo
from django.db.backends.base.introspection import (
BaseDatabaseIntrospection,
TableInfo,
)


class DatabaseIntrospection(BaseDatabaseIntrospection):
Expand Down
4 changes: 3 additions & 1 deletion cratedb_django/models/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ class UUID(Func):
"""https://cratedb.com/docs/crate/reference/en/latest/general/builtins/scalar-functions.html#gen-random-text-uuid"""

function = "gen_random_text_uuid"
output_field = TextField(max_length=20) # the length of a CrateDB random uid.
output_field = TextField(
max_length=20
) # the length of a CrateDB random uid.
4 changes: 3 additions & 1 deletion cratedb_django/models/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ class CrateModel(models.Model, metaclass=MetaCrate):
"""

def save(self, *args, **kwargs):
super().save(*args, **kwargs) # perform the actual save (insert or update)
super().save(
*args, **kwargs
) # perform the actual save (insert or update)
auto_refresh = getattr(self._meta, "auto_refresh", False)
if auto_refresh and self.pk: # If self.pk is available, it's an insert.
table_name = self._meta.db_table
Expand Down
15 changes: 11 additions & 4 deletions cratedb_django/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ def check_field(model, field_name: str) -> None:
try:
model._meta.get_field(field_name)
except Exception as e:
raise ValueError(f"Column {field_name!r} does not exist in model") from e
raise ValueError(
f"Column {field_name!r} does not exist in model"
) from e


class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
Expand Down Expand Up @@ -63,7 +65,10 @@ def _alter_column_null_sql(self, model, old_field, new_field):
def column_sql(self, model, field, include_default=False):
if field.unique:
# todo pgdiff
if not os.getenv("SUPPRESS_UNIQUE_CONSTRAINT_WARNING", "false") == "true":
if (
not os.getenv("SUPPRESS_UNIQUE_CONSTRAINT_WARNING", "false")
== "true"
):
logging.warning(
f"CrateDB does not support unique constraints but `{model}.{field}` is set as"
f" unique=True, it will be ignored."
Expand Down Expand Up @@ -102,7 +107,7 @@ def table_sql(self, model) -> tuple:
for field in partition_by:
check_field(model, field)

sql[0] += f" PARTITIONED BY ({", ".join(partition_by)})"
sql[0] += f" PARTITIONED BY ({', '.join(partition_by)})"

clustered_by = getattr(model._meta, "clustered_by", OMITTED)
if clustered_by is not OMITTED:
Expand All @@ -125,7 +130,9 @@ def table_sql(self, model) -> tuple:
sql[0] += f" CLUSTERED BY ({clustered_by})"

if clustered_by and number_of_shards:
sql[0] += f" CLUSTERED BY ({clustered_by}) INTO {number_of_shards} shards"
sql[0] += (
f" CLUSTERED BY ({clustered_by}) INTO {number_of_shards} shards"
)

if not clustered_by and number_of_shards:
sql[0] += f" CLUSTERED INTO ({number_of_shards})"
Expand Down
6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ dependencies = [

[dependency-groups]
dev = [
"black>=25.1.0",
"django-stubs>=5.1.3",
"pytest-cratedb>=0.4.0",
"requests>=2.32.3",
"ruff<0.15",
]

[project.urls]
Expand All @@ -38,3 +38,7 @@ Documentation = "https://github.com/surister/cratedb-django"
Repository = "https://github.com/surister/cratedb-django.git"
GitHub = "https://github.com/surister/cratedb-django"
Changelog = "https://github.com/surister/cratedb-django/releases"


[tool.ruff]
line-length = 80
8 changes: 6 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import logging

import pytest
import os, django
import os
import django
from django.db import connection
from django.apps import apps

Expand Down Expand Up @@ -50,7 +51,10 @@ def clean_database(request):
]

for model in models:
if model._meta.app_label != _CRATE_TEST_APP and not model._meta.abstract:
if (
model._meta.app_label != _CRATE_TEST_APP
and not model._meta.abstract
):
with connection.cursor() as cursor:
cursor.execute(f"DELETE FROM {model._meta.db_table}")
cursor.execute(f"REFRESH TABLE {model._meta.db_table}")
Expand Down
11 changes: 8 additions & 3 deletions tests/test_app/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import datetime, uuid
import datetime
import uuid

from django.db.models import F

Expand Down Expand Up @@ -52,7 +53,9 @@ class ArraysModel(CrateModel):
field_int_not_null = fields.ArrayField(
fields.IntegerField(), null=False, default=[]
)
field_int_default = fields.ArrayField(fields.IntegerField(), default=[123, 321])
field_int_default = fields.ArrayField(
fields.IntegerField(), default=[123, 321]
)
field_float = fields.ArrayField(fields.FloatField())
field_char = fields.ArrayField(fields.CharField(max_length=100))
field_bool = fields.ArrayField(fields.BooleanField())
Expand Down Expand Up @@ -86,7 +89,9 @@ class GeneratedModel(CrateModel):
expression=F("f1") / F("f2"), output_field=fields.IntegerField()
)
ff = fields.GeneratedField(
expression=F("f1") + 1, output_field=fields.IntegerField(), db_persist=False
expression=F("f1") + 1,
output_field=fields.IntegerField(),
db_persist=False,
)
f_func = fields.GeneratedField(
expression=UUID(), output_field=fields.CharField(max_length=120)
Expand Down
10 changes: 7 additions & 3 deletions tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,16 @@ def test_get_connection_params():
opts = dict(base_opts)
opts["SERVERS"] = []
opts["HOST"] = ""
with pytest.raises(ImproperlyConfigured, match=r"Missing SERVERS parameter"):
with pytest.raises(
ImproperlyConfigured, match=r"Missing SERVERS parameter"
):
DatabaseWrapper(opts).get_connection_params()

opts = dict(base_opts)
opts["PORT"] = "4200"
with pytest.raises(ImproperlyConfigured, match=r"Unexpected 'PORT' setting"):
with pytest.raises(
ImproperlyConfigured, match=r"Unexpected 'PORT' setting"
):
DatabaseWrapper(opts).get_connection_params()

opts = dict(base_opts)
Expand All @@ -63,7 +67,7 @@ def test_get_connection_params():
opts = dict(base_opts)
opts["OPTIONS"] = {"verify_ssl_cert": False}
c = DatabaseWrapper(opts).get_connection_params()
assert c["verify_ssl_cert"] == False
assert c["verify_ssl_cert"] is False

opts = dict(base_opts)
opts["USER"] = ""
Expand Down
17 changes: 13 additions & 4 deletions tests/test_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ class Meta:
def test_field_array_creation():
class SomeModel(CrateModel):
f1 = fields.ArrayField(fields.IntegerField())
f2 = fields.ArrayField(fields.ArrayField(fields.CharField(max_length=120)))
f2 = fields.ArrayField(
fields.ArrayField(fields.CharField(max_length=120))
)
f3 = fields.ArrayField(fields.ArrayField(fields.ObjectField()))

class Meta:
Expand Down Expand Up @@ -115,7 +117,9 @@ def test_field_array_insert():

# Convert expected defaults `field_uuid` from UUID to `str`,
# which is what django returns.
expected_defaults["field_uuid"] = list(map(lambda x: str(x), d["field_uuid"]))
expected_defaults["field_uuid"] = list(
map(lambda x: str(x), d["field_uuid"])
)
assert d == expected_defaults


Expand Down Expand Up @@ -151,7 +155,9 @@ class SomeModel(CrateModel):
expression=F("f1") / F("f2"), output_field=models.IntegerField()
)
ff = fields.GeneratedField(
expression=F("f1") + 1, output_field=models.IntegerField(), db_persist=False
expression=F("f1") + 1,
output_field=models.IntegerField(),
db_persist=False,
)
f_func = fields.GeneratedField(
expression=UUID(), output_field=models.CharField(max_length=120)
Expand All @@ -172,7 +178,10 @@ class Meta:
]

sql, params = get_sql_of(SomeModel).field("f_func")
assert sql.strip() == "varchar(120) GENERATED ALWAYS AS (gen_random_text_uuid())"
assert (
sql.strip()
== "varchar(120) GENERATED ALWAYS AS (gen_random_text_uuid())"
)
assert not params


Expand Down
23 changes: 17 additions & 6 deletions tests/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from cratedb_django import fields

from django.forms.models import model_to_dict
from django.db import connection, models
from django.db import connection
from django.test.utils import CaptureQueriesContext

from tests.utils import captured_queries
Expand Down Expand Up @@ -133,7 +133,10 @@ class Meta:

# Check the combination of user-defined + default.
assert RefreshMetaOptions._meta.auto_refresh is True
assert RefreshMetaOptions._meta.partition_by is CRATE_META_OPTIONS["partition_by"]
assert (
RefreshMetaOptions._meta.partition_by
is CRATE_META_OPTIONS["partition_by"]
)


def test_model_meta_partition_by():
Expand All @@ -158,7 +161,9 @@ class Meta:
assert "PARTITIONED BY (one, two, three)" in sql

MetaOptions._meta.partition_by = []
with pytest.raises(ValueError, match="partition_by has to be a non-empty sequence"):
with pytest.raises(
ValueError, match="partition_by has to be a non-empty sequence"
):
with connection.schema_editor() as schema_editor:
schema_editor.table_sql(MetaOptions)

Expand Down Expand Up @@ -191,7 +196,10 @@ class Meta:
sql, params = schema_editor.column_sql(
SomeModel, SomeModel._meta.get_field("id")
)
assert sql == "char(20) DEFAULT (gen_random_text_uuid()) NOT NULL PRIMARY KEY"
assert (
sql
== "char(20) DEFAULT (gen_random_text_uuid()) NOT NULL PRIMARY KEY"
)


def test_model_custom_id():
Expand Down Expand Up @@ -255,7 +263,9 @@ class Meta:
assert "INTO 3 shards" not in sql
assert "CLUSTERED" not in sql

with pytest.raises(ValueError, match="Column 'nocolumn' does not exist in model"):
with pytest.raises(
ValueError, match="Column 'nocolumn' does not exist in model"
):
MetaOptions._meta.clustered_by = "nocolumn"
MetaOptions._meta.number_of_shards = OMITTED
with connection.schema_editor() as schema_editor:
Expand All @@ -278,7 +288,8 @@ class Meta:

with pytest.raises(
ValueError,
match="number_of_shards has to be an integer bigger than 0, " "not 'abcdef'",
match="number_of_shards has to be an integer bigger than 0, "
"not 'abcdef'",
):
MetaOptions._meta.clustered_by = OMITTED
MetaOptions._meta.number_of_shards = "abcdef"
Expand Down
3 changes: 2 additions & 1 deletion tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ def compile_insert_sql(model, objs, using="default") -> tuple[str, tuple]:
fields = [
f
for f in model._meta.local_concrete_fields
if not getattr(f, "generated", False) and not isinstance(f, AutoFieldMixin)
if not getattr(f, "generated", False)
and not isinstance(f, AutoFieldMixin)
]

query.insert_values(fields, objs)
Expand Down
Loading
Loading