mirror of
https://github.com/carrotquest/django-clickhouse.git
synced 2024-11-29 04:23:45 +03:00
Changed serializer init to concrete model fields
This commit is contained in:
parent
da755da09d
commit
e4b2958895
|
@ -65,9 +65,9 @@ class ClickHouseModel(with_metaclass(ClickHouseModelMeta, InfiModel)):
|
||||||
return db_router.db_for_read(self.__class__, instance=self)
|
return db_router.db_for_read(self.__class__, instance=self)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_django_model_serializer(cls):
|
def get_django_model_serializer(cls, writable=False): # type: (bool) -> Django2ClickHouseModelSerializer
|
||||||
serializer_cls = lazy_class_import(cls.django_model_serializer)
|
serializer_cls = lazy_class_import(cls.django_model_serializer)
|
||||||
return serializer_cls()
|
return serializer_cls(cls, writable=writable)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_sync_batch_size(cls):
|
def get_sync_batch_size(cls):
|
||||||
|
|
|
@ -26,8 +26,8 @@ class InsertOnlyEngineMixin:
|
||||||
:param objects: A list of django Model instances to sync
|
:param objects: A list of django Model instances to sync
|
||||||
:return: A list of model_cls objects
|
:return: A list of model_cls objects
|
||||||
"""
|
"""
|
||||||
serializer = model_cls.get_django_model_serializer()
|
serializer = model_cls.get_django_model_serializer(writable=True)
|
||||||
return [serializer.serialize(obj, model_cls) for obj in objects]
|
return [serializer.serialize(obj) for obj in objects]
|
||||||
|
|
||||||
|
|
||||||
class MergeTree(InsertOnlyEngineMixin, infi_engines.MergeTree):
|
class MergeTree(InsertOnlyEngineMixin, infi_engines.MergeTree):
|
||||||
|
|
|
@ -7,110 +7,3 @@ class QuerySet(InfiQuerySet):
|
||||||
|
|
||||||
class AggregateQuerySet(InfiAggregateQuerySet):
|
class AggregateQuerySet(InfiAggregateQuerySet):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# class ClickHouseModel(InfiModel):
|
|
||||||
#
|
|
||||||
# @classmethod
|
|
||||||
# def form_query(cls, select_items: Union[str, Set[str], List[str], Tuple[str]], table: Optional[str] = None,
|
|
||||||
# final: bool = False, date_filter_field: str = '', date_in_prewhere: bool = True,
|
|
||||||
# prewhere: Union[str, Set[str], List[str], Tuple[str]] = '',
|
|
||||||
# where: Union[str, Set[str], List[str], Tuple[str]] = '',
|
|
||||||
# group_fields: Union[str, Set[str], List[str], Tuple[str]] = '', group_with_totals: bool = False,
|
|
||||||
# order_by: Union[str, Set[str], List[str], Tuple[str]] = '',
|
|
||||||
# limit: Optional[int] = None, prewhere_app: bool = True):
|
|
||||||
# """
|
|
||||||
# Формирует запрос к данной таблице
|
|
||||||
# :param select_items: строка или массив строк, которые надо выбрать в запросе
|
|
||||||
# :param table: Таблица данного класса по-умолчанию
|
|
||||||
# :param final: Позволяет выбрать из CollapsingMergeTree только последнюю версию записи.
|
|
||||||
# :param date_filter_field: Поле, которое надо отфильтровать от start_date до date_end, если задано
|
|
||||||
# :param date_in_prewhere: Если флаг указан и задано поле date_filter_field,
|
|
||||||
# то условие будет помещено в секцию PREWHERE, иначе - в WHERE
|
|
||||||
# :param prewhere: Условие, которое добавляется в prewhere
|
|
||||||
# :param where: Условие, которое добавляется в where
|
|
||||||
# :param group_fields: Поля, по которым будет производится группировка
|
|
||||||
# :param group_with_totals: Позволяет добавить к группировке модификатор with_totals
|
|
||||||
# :param order_by: Поле или массив полей, по которым сортируется результат
|
|
||||||
# :param limit: Лимит на количество записей
|
|
||||||
# :param prewhere_app: Автоматически добавляет в prewhere фильтр по app_id
|
|
||||||
# :return: Запрос, в пределах приложения
|
|
||||||
# """
|
|
||||||
# assert isinstance(select_items, (str, list, tuple, set)), "select_items must be string, list, tuple or set"
|
|
||||||
# assert table is None or isinstance(table, str), "table must be string or None"
|
|
||||||
# assert isinstance(final, bool), "final must be boolean"
|
|
||||||
# assert isinstance(date_filter_field, str), "date_filter_field must be string"
|
|
||||||
# assert isinstance(date_in_prewhere, bool), "date_in_prewhere must be boolean"
|
|
||||||
# assert isinstance(prewhere, (str, list, tuple, set)), "prewhere must be string, list, tuple or set"
|
|
||||||
# assert isinstance(where, (str, list, tuple, set)), "where must be string, list, tuple or set"
|
|
||||||
# assert isinstance(group_fields, (str, list, tuple, set)), "group_fields must be string, list, tuple or set"
|
|
||||||
# assert isinstance(group_with_totals, bool), "group_with_totals must be boolean"
|
|
||||||
# assert isinstance(order_by, (str, list, tuple, set)), "group_fields must be string, list, tuple or set"
|
|
||||||
# assert limit is None or isinstance(limit, int) and limit > 0, "limit must be None or positive integer"
|
|
||||||
# assert isinstance(prewhere_app, bool), "prewhere_app must be boolean"
|
|
||||||
#
|
|
||||||
# table = table or '$db.`{0}`'.format(cls.table_name())
|
|
||||||
# final = 'FINAL' if final else ''
|
|
||||||
#
|
|
||||||
# if prewhere:
|
|
||||||
# if not isinstance(prewhere, str):
|
|
||||||
# prewhere = '(%s)' % ') AND ('.join(prewhere)
|
|
||||||
#
|
|
||||||
# if prewhere_app:
|
|
||||||
# prewhere = '`app_id`={app_id} AND (' + prewhere + ')' if prewhere else '`app_id`={app_id}'
|
|
||||||
#
|
|
||||||
# if prewhere:
|
|
||||||
# prewhere = 'PREWHERE ' + prewhere
|
|
||||||
#
|
|
||||||
# if where:
|
|
||||||
# if not isinstance(where, str):
|
|
||||||
# where = ' AND '.join(where)
|
|
||||||
# where = 'WHERE ' + where
|
|
||||||
#
|
|
||||||
# if not isinstance(select_items, str):
|
|
||||||
# # Исключим пустые строки
|
|
||||||
# select_items = [item for item in select_items if item]
|
|
||||||
# select_items = ', '.join(select_items)
|
|
||||||
#
|
|
||||||
# if group_fields:
|
|
||||||
# if not isinstance(group_fields, str):
|
|
||||||
# group_fields = ', '.join(group_fields)
|
|
||||||
#
|
|
||||||
# group_fields = 'GROUP BY %s' % group_fields
|
|
||||||
#
|
|
||||||
# if group_with_totals:
|
|
||||||
# group_fields += ' WITH TOTALS'
|
|
||||||
#
|
|
||||||
# if order_by:
|
|
||||||
# if not isinstance(order_by, str):
|
|
||||||
# order_by = ', '.join(order_by)
|
|
||||||
#
|
|
||||||
# order_by = 'ORDER BY ' + order_by
|
|
||||||
#
|
|
||||||
# if date_filter_field:
|
|
||||||
# cond = "`%s` >= '{start_date}' AND `%s` < '{end_date}'" % (date_filter_field, date_filter_field)
|
|
||||||
# if date_in_prewhere:
|
|
||||||
# prewhere += ' AND ' + cond
|
|
||||||
# elif where:
|
|
||||||
# where += ' AND ' + cond
|
|
||||||
# else:
|
|
||||||
# where = 'WHERE ' + cond
|
|
||||||
#
|
|
||||||
# limit = "LIMIT {0}".format(limit) if limit else ''
|
|
||||||
#
|
|
||||||
# query = '''
|
|
||||||
# SELECT %s
|
|
||||||
# FROM %s %s
|
|
||||||
#
|
|
||||||
# %s
|
|
||||||
# %s
|
|
||||||
# %s %s %s
|
|
||||||
# ''' % (select_items, table, final, prewhere, where, group_fields, order_by, limit)
|
|
||||||
#
|
|
||||||
# # Моя спец функция, сокращающая запись проверки даты на Null в запросах.
|
|
||||||
# # В скобках не может быть других скобок
|
|
||||||
# # (иначе надо делать сложную проверку скобочной последовательности, решил пока не заморачиваться)
|
|
||||||
# # Фактически, функция приводит выражение в скобках к timestamp и смотрит, что оно больше 0
|
|
||||||
# query = re.sub(r'\$dateIsNotNull\s*(\([^()]*\))', r'toUInt64(toDateTime(\1)) > 0', query)
|
|
||||||
#
|
|
||||||
# return re.sub(r'\s+', ' ', query.strip())
|
|
|
@ -1,21 +1,25 @@
|
||||||
from typing import Type
|
|
||||||
|
|
||||||
from django.db.models import Model as DjangoModel
|
from django.db.models import Model as DjangoModel
|
||||||
from django.forms import model_to_dict
|
from django.forms import model_to_dict
|
||||||
|
|
||||||
|
|
||||||
class Django2ClickHouseModelSerializer:
|
class Django2ClickHouseModelSerializer:
|
||||||
def __init__(self, fields=None, exclude_fields=None):
|
def __init__(self, model_cls, fields=None, exclude_fields=None, writable=False):
|
||||||
|
self._model_cls = model_cls
|
||||||
|
if fields is not None:
|
||||||
self.serialize_fields = fields
|
self.serialize_fields = fields
|
||||||
|
else:
|
||||||
|
self.serialize_fields = model_cls.fields(writable=writable).keys()
|
||||||
|
|
||||||
self.exclude_serialize_fields = exclude_fields
|
self.exclude_serialize_fields = exclude_fields
|
||||||
|
|
||||||
def serialize(self, obj, model_cls):
|
def serialize(self, obj): # type: (DjangoModel) -> 'ClickHouseModel'
|
||||||
# type: (DjangoModel, Type['ClickHouseModel']) -> 'ClickHouseModel'
|
|
||||||
data = model_to_dict(obj, self.serialize_fields, self.exclude_serialize_fields)
|
data = model_to_dict(obj, self.serialize_fields, self.exclude_serialize_fields)
|
||||||
|
|
||||||
# Remove None values, they should be initialized as defaults
|
# Remove None values, they should be initialized as defaults
|
||||||
for key, value in data.items():
|
for key, value in data.items():
|
||||||
if value is None:
|
if value is None:
|
||||||
del data[key]
|
del data[key]
|
||||||
|
elif isinstance(value, bool):
|
||||||
|
data[key] = int(value)
|
||||||
|
|
||||||
return model_cls(**data)
|
return self._model_cls(**data)
|
||||||
|
|
|
@ -14,24 +14,24 @@ class Django2ClickHouseModelSerializerTest(TestCase):
|
||||||
self.obj = TestModel.objects.get(pk=1)
|
self.obj = TestModel.objects.get(pk=1)
|
||||||
|
|
||||||
def test_all(self):
|
def test_all(self):
|
||||||
serializer = Django2ClickHouseModelSerializer()
|
serializer = Django2ClickHouseModelSerializer(ClickHouseTestModel)
|
||||||
res = serializer.serialize(self.obj, ClickHouseTestModel)
|
res = serializer.serialize(self.obj)
|
||||||
self.assertIsInstance(res, ClickHouseTestModel)
|
self.assertIsInstance(res, ClickHouseTestModel)
|
||||||
self.assertEqual(self.obj.id, res.id)
|
self.assertEqual(self.obj.id, res.id)
|
||||||
self.assertEqual(self.obj.value, res.value)
|
self.assertEqual(self.obj.value, res.value)
|
||||||
self.assertEqual(self.obj.created_date, res.created_date)
|
self.assertEqual(self.obj.created_date, res.created_date)
|
||||||
|
|
||||||
def test_fields(self):
|
def test_fields(self):
|
||||||
serializer = Django2ClickHouseModelSerializer(fields=('value'))
|
serializer = Django2ClickHouseModelSerializer(ClickHouseTestModel, fields=('value'))
|
||||||
res = serializer.serialize(self.obj, ClickHouseTestModel)
|
res = serializer.serialize(self.obj)
|
||||||
self.assertIsInstance(res, ClickHouseTestModel)
|
self.assertIsInstance(res, ClickHouseTestModel)
|
||||||
self.assertEqual(0, res.id)
|
self.assertEqual(0, res.id)
|
||||||
self.assertEqual(datetime.date(1970, 1, 1), res.created_date)
|
self.assertEqual(datetime.date(1970, 1, 1), res.created_date)
|
||||||
self.assertEqual(self.obj.value, res.value)
|
self.assertEqual(self.obj.value, res.value)
|
||||||
|
|
||||||
def test_exclude_fields(self):
|
def test_exclude_fields(self):
|
||||||
serializer = Django2ClickHouseModelSerializer(exclude_fields=('created_date',))
|
serializer = Django2ClickHouseModelSerializer(ClickHouseTestModel, exclude_fields=('created_date',))
|
||||||
res = serializer.serialize(self.obj, ClickHouseTestModel)
|
res = serializer.serialize(self.obj)
|
||||||
self.assertIsInstance(res, ClickHouseTestModel)
|
self.assertIsInstance(res, ClickHouseTestModel)
|
||||||
self.assertEqual(datetime.date(1970, 1, 1), res.created_date)
|
self.assertEqual(datetime.date(1970, 1, 1), res.created_date)
|
||||||
self.assertEqual(self.obj.id, res.id)
|
self.assertEqual(self.obj.id, res.id)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user