accrete 0.0.23__py3-none-any.whl → 0.0.25__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,7 +11,4 @@ from .components import (
11
11
  TableFieldAlignment,
12
12
  TableFieldType
13
13
  )
14
- from .querystring import (
15
- parse_querystring,
16
- build_querystring
17
- )
14
+ from .querystring import load_querystring, build_url_params
@@ -22,12 +22,14 @@ class TableField:
22
22
 
23
23
  label: str
24
24
  name: str
25
- alignment: type[TableFieldAlignment] = TableFieldAlignment.LEFT
26
- header_alignment: type[TableFieldAlignment] = None
27
- field_type: type[TableFieldType] = TableFieldType.NONE
25
+ alignment: TableFieldAlignment | Enum = TableFieldAlignment.LEFT
26
+ header_alignment: TableFieldAlignment | Enum = None
27
+ header_info: str = None
28
+ field_type: TableFieldType | Enum = TableFieldType.NONE
28
29
  prefix: str = ''
29
30
  suffix: str = ''
30
31
  truncate_after: int = 0
32
+ template: str = None
31
33
 
32
34
 
33
35
  @dataclass
@@ -43,7 +45,7 @@ class ClientAction:
43
45
  name: str
44
46
  url: str = ''
45
47
  query_params: str = ''
46
- attrs: list[str] = field(default_factory=list)
48
+ attrs: list[tuple[str, str]] = field(default_factory=list)
47
49
  submit: bool = False
48
50
  form_id: str = 'form'
49
- class_list: list = field(default_factory=list)
51
+ class_list: list[str] = field(default_factory=list)
@@ -4,8 +4,9 @@ from dataclasses import dataclass, field
4
4
  from django.utils.translation import gettext_lazy as _
5
5
  from django.db.models import Model, QuerySet, Q
6
6
  from django.core.paginator import Paginator
7
+ from accrete.querystring import parse_querystring
7
8
 
8
- from .querystring import parse_querystring, build_querystring
9
+ from .querystring import load_querystring, build_url_params
9
10
  from .components import ClientAction, BreadCrumb, TableField
10
11
  from .filter import Filter
11
12
 
@@ -17,7 +18,7 @@ DEFAULT_PAGINATE_BY = 40
17
18
  @dataclass
18
19
  class ListContext:
19
20
 
20
- model: Model
21
+ model: type[Model]
21
22
  get_params: dict
22
23
  title: str = None
23
24
  context: dict = field(default_factory=dict)
@@ -31,7 +32,7 @@ class ListContext:
31
32
  default_filter_term: str = ''
32
33
  actions: list[ClientAction] = field(default_factory=list)
33
34
  breadcrumbs: list[BreadCrumb] = field(default_factory=list)
34
- obj_label: str = None
35
+ object_label: str = None
35
36
  fields: list[TableField] = field(default_factory=list)
36
37
  unselect_button: bool = False
37
38
 
@@ -103,12 +104,13 @@ class ListContext:
103
104
  'page': page,
104
105
  'list_pagination': True,
105
106
  'title': self.title or self.model._meta.verbose_name_plural,
107
+ 'object_label': self.object_label or self.model._meta.verbose_name or _('Name'),
106
108
  'filter_terms': Filter(self.model, self.filter_relation_depth).get_query_terms(),
107
109
  'default_filter_term': self.default_filter_term,
108
110
  'breadcrumbs': self.breadcrumbs,
109
- 'querystring': build_querystring(self.get_params),
111
+ 'querystring': load_querystring(self.get_params),
112
+ 'url_params': build_url_params(self.get_params),
110
113
  'actions': self.actions,
111
- 'obj_label': self.obj_label or _('Name'),
112
114
  'fields': self.fields
113
115
  }
114
116
  context.update(self.context)
@@ -118,7 +120,7 @@ class ListContext:
118
120
  @dataclass
119
121
  class DetailContext:
120
122
 
121
- obj: Model
123
+ obj: Model | type[Model]
122
124
  get_params: dict
123
125
  order_by: str = None
124
126
  paginate_by: int = DEFAULT_PAGINATE_BY
@@ -205,9 +207,8 @@ class DetailContext:
205
207
  'order_by': self.get_params.get('order_by', self.obj._meta.model._meta.ordering),
206
208
  'paginate_by': paginate_by,
207
209
  'detail_pagination': False,
208
- 'view_type': 'detail',
209
210
  'breadcrumbs': self.breadcrumbs,
210
- 'querystring': build_querystring(self.get_params, ['page']),
211
+ 'url_params': build_url_params(self.get_params, ['page']),
211
212
  'actions': self.actions
212
213
  }
213
214
  if self.paginate_by > 0:
@@ -238,7 +239,7 @@ class FormContext:
238
239
  )
239
240
  ]
240
241
  try:
241
- url = self.discard_url or self.model.get_absolute_url()
242
+ url = self.discard_url or (self.model.pk and self.model.get_absolute_url())
242
243
  except TypeError:
243
244
  raise TypeError(
244
245
  'Supply the discard_url parameter if FormContext is called '
@@ -272,9 +273,8 @@ class FormContext:
272
273
  def dict(self):
273
274
  ctx = {
274
275
  'title': self.get_title(),
275
- 'view_type': 'form',
276
276
  'form_id': self.form_id,
277
- 'querystring': build_querystring(self.get_params, ['page']),
277
+ 'url_params': build_url_params(self.get_params, ['page']),
278
278
  'actions': []
279
279
  }
280
280
  if self.add_default_actions:
@@ -7,7 +7,6 @@ from django.utils.translation import gettext_lazy as _
7
7
  class Filter:
8
8
 
9
9
  query_relation_depth = 4
10
- exclude_from_terms = []
11
10
  query_float_fields = ['DecimalField', 'FloatField']
12
11
  query_char_fields = ['CharField', 'TextField']
13
12
  query_boolean_fields = ['BooleanField']
@@ -27,14 +26,9 @@ class Filter:
27
26
  LABEL_SET = _('Is Set')
28
27
  LABEL_NOT_SET = _('Is Not Set')
29
28
 
30
- def __init__(
31
- self, model,
32
- query_relation_depth: int = 4,
33
- exclude_from_terms: list = None,
34
- ):
29
+ def __init__(self, model, query_relation_depth: int = 4):
35
30
  self.model = model
36
31
  self.query_relation_depth = query_relation_depth
37
- self.exclude_from_terms = exclude_from_terms or []
38
32
 
39
33
  def get_choice_char_query_term(self, label, param, choices):
40
34
  choices = [(choice[0], str(choice[1])) for choice in choices]
@@ -289,12 +283,10 @@ class Filter:
289
283
 
290
284
  def get_relation_query_terms(self, model, path):
291
285
  terms = []
292
- if 'tenant' not in self.exclude_from_terms:
293
- self.exclude_from_terms.append('tenant')
286
+ filter_exclude = getattr(model, 'filter_exclude', [])
287
+ filter_exclude.append('tenant')
294
288
  fields = filter(
295
- lambda x:
296
- x.is_relation and
297
- x.name not in self.exclude_from_terms,
289
+ lambda x: x.is_relation and x.name not in filter_exclude,
298
290
  sorted(model._meta.get_fields(), key=lambda x: x.name.lower())
299
291
  )
300
292
  cpath = path.copy()
@@ -317,10 +309,11 @@ class Filter:
317
309
 
318
310
  def get_local_query_terms(self, model):
319
311
  terms = []
320
- fields = filter(lambda x: not x.is_relation, model._meta.get_fields())
321
- searchable_methods = []
322
- if hasattr(model, 'searchable_methods'):
323
- searchable_methods = model.searchable_methods
312
+ filter_exclude = getattr(model, 'filter_exclude', [])
313
+ fields = filter(
314
+ lambda x: not x.is_relation and x.name not in filter_exclude,
315
+ model._meta.get_fields()
316
+ )
324
317
  for field in fields:
325
318
  term = self.get_query_term(field)
326
319
  if field.null is True and term.get('params'):
@@ -1,85 +1,11 @@
1
1
  import json
2
- import logging
3
- import operator
4
- from django.db.models import Model, Q
5
2
 
6
- _logger = logging.getLogger(__name__)
7
3
 
4
+ def load_querystring(get_params: dict) -> list:
5
+ return json.loads(get_params.get('q', '[]'))
8
6
 
9
- def parse_querystring(model: type[Model], query_string: str) -> Q:
10
7
 
11
- def get_expression(term: str, value) -> Q:
12
- invert = False
13
- if term.startswith('~'):
14
- invert = True
15
- term = term[1:]
16
-
17
- parts = term.split('_a_')
18
- if len(parts) == 1:
19
- expression = Q(**{term: value})
20
- return ~expression if invert else expression
21
-
22
- rel_path = parts[0].rstrip('__')
23
- term = parts[1]
24
- rel_model = get_related_model(rel_path) if rel_path else model
25
- objects = rel_model.objects.annotate(**{
26
- annotation['name']: annotation['func']
27
- for annotation in rel_model.annotations
28
- }).filter(Q(**{term: value}))
29
- expression = Q(**{
30
- f'{rel_path}{"__" if rel_path else ""}id__in': objects.values_list('id', flat=True)
31
- })
32
-
33
- return ~expression if invert else expression
34
-
35
- def get_related_model(rel_path: str):
36
- related_model = model
37
- for part in rel_path.split('__'):
38
- try:
39
- related_model = related_model._meta.fields_map[part].related_model
40
- except (AttributeError, KeyError):
41
- try:
42
- related_model = getattr(related_model, part).field.related_model
43
- except AttributeError:
44
- break
45
- return related_model
46
-
47
- def parse_query_block(sub_item) -> Q:
48
- op = ops['&']
49
- parsed_query = Q()
50
- for item in sub_item:
51
- if isinstance(item, list):
52
- parsed_query = op(parsed_query, parse_query_block(item))
53
- elif isinstance(item, dict):
54
- dict_query = Q()
55
- for term, value in item.items():
56
- dict_query = ops['&'](dict_query, get_expression(term, value))
57
- parsed_query = op(parsed_query, dict_query)
58
- elif isinstance(item, str):
59
- try:
60
- op = ops[item]
61
- except KeyError as e:
62
- _logger.exception(e)
63
- raise ValueError(
64
- f'Invalid operator in querystring: {item}.'
65
- f'Operator must be one of &, |, ^'
66
- )
67
- else:
68
- raise ValueError(
69
- f'Unsupported item in querystring: {item}'
70
- )
71
- return parsed_query
72
-
73
- query_data = json.loads(query_string)
74
- if isinstance(query_data, dict):
75
- query_data = [query_data]
76
-
77
- ops = {'&': operator.and_, '|': operator.or_, '^': operator.xor}
78
- query = parse_query_block(query_data)
79
- return query
80
-
81
-
82
- def build_querystring(get_params: dict, extra_params: list[str] = None) -> str:
8
+ def build_url_params(get_params: dict, extra_params: list[str] = None) -> str:
83
9
  querystring = f'?q={get_params.get("q", "[]")}'
84
10
  if paginate_by := get_params.get('paginate_by', False):
85
11
  querystring += f'&paginate_by={paginate_by}'