@@ -120,6 +120,32 @@ def __new__(mcls, name, bases, attrs): # noqa: B902
120120 )
121121 return _cls
122122
123+ @classmethod
124+ def construct_index (cls , opts , bases ):
125+ """
126+ Override IndexMeta.construct_index so a new Index is created for each class
127+ and Index.settings, Index.analyzers, and Index.using are inherited
128+ (but not Index.name or Index.aliases)
129+ """
130+ _base_index_configs = collections .ChainMap (
131+ * (base ._index .to_dict () for base in bases if hasattr (base , "_index" ))
132+ )
133+ _index_opts = opts or type ("Index" , (), {})
134+ if not hasattr (_index_opts , "settings" ) and (
135+ _inherited_settings := _base_index_configs .get ("settings" )
136+ ):
137+ _index_opts .settings = _inherited_settings
138+ if not hasattr (_index_opts , "analyzers" ) and (
139+ _inherited_analyzers := _base_index_configs .get ("analyzers" )
140+ ):
141+ _index_opts .analyzers = _inherited_analyzers
142+ if not hasattr (_index_opts , "using" ):
143+ _inherited_using = _base_index_configs .get ("using" )
144+ if _inherited_using and (_inherited_using != "default" ):
145+ _index_opts .using = _inherited_using
146+ assert _index_opts is not None # guarantee separate index per class
147+ return super ().construct_index (_index_opts , bases )
148+
123149 def _get_meta_attr (self , attr_name : str , default : typing .Any = None ) -> typing .Any :
124150 _meta = getattr (self , "Meta" , None )
125151 return getattr (_meta , attr_name , default )
@@ -189,6 +215,7 @@ def record(
189215 to use to save, or `False` to skip saving (e.g. for use in a bulk operation)
190216 all other kwargs passed thru to the class constructor
191217 """
218+ assert not cls .is_abstract
192219 _instance = cls (** kwargs )
193220 if using is not False :
194221 _instance .save (using = using )
@@ -241,6 +268,7 @@ def save(
241268 - populate document id based on UNIQUE_TOGETHER_FIELDS
242269 - send pre_save/post_save signals
243270 """
271+ assert not self .__class__ .is_abstract
244272 self .require_been_setup (using = using ) # prevent automapped indexes
245273 self ._populate_unique_id ()
246274 signals .pre_save .send (self .__class__ , instance = self , using = using , index = index )
@@ -276,31 +304,34 @@ def __new__(mcls, name, bases, attrs): # noqa: B902
276304 """
277305 extend _DjelmeRecordMetaclass.__new__ to set index name by app label and type name
278306 """
279- _index = attrs .get ("Index" )
280- if not _index :
281- _index = type ("Index" , (), {})
282- attrs ["Index" ] = _index
283- if not getattr (_index , "name" , None ):
284- _app_label = find_app_label_for_module (attrs ["__module__" ])
285- _index .name = f"{ _app_label .lower ()} _{ name .lower ()} "
286- return super ().__new__ (mcls , name , bases , attrs )
307+ _cls = super ().__new__ (mcls , name , bases , attrs )
308+ if not _cls .is_abstract :
309+ # set `_index._name`, if not already specified
310+ _index_name = _cls ._index ._name
311+ if not _index_name or (_index_name in ("*" , "default" )):
312+ _app_label = find_app_label_for_module (attrs ["__module__" ])
313+ _index_name = f"{ _app_label .lower ()} _{ _cls .__name__ .lower ()} "
314+ _prefix = _cls .get_index_name_prefix ()
315+ if not _index_name .startswith (_prefix ):
316+ _index_name = f"{ _prefix } { _index_name } "
317+ _cls ._index ._name = _index_name
318+ # set `_index._using`, so `_index` can be used normally
319+ _backend = djelme_registry .get_backend_for_recordtype (_cls )
320+ _cls ._index ._using = _backend ._elastic8dsl_connection_name
321+ return _cls
287322
288323
289324class SimpleRecord (BaseDjelmeRecord , metaclass = _SimpleRecordMetaclass ):
290- def __init_subclass__ (cls , ** kwargs ):
291- super ().__init_subclass__ (** kwargs )
292- if not cls .is_abstract :
293- _prefix = cls .get_index_name_prefix ()
294- _name = cls ._index ._name
295- if not _name .startswith (_prefix ):
296- cls ._index ._name = f"{ _prefix } { _name } "
325+ class Meta :
326+ abstract = True
297327
298328 @classmethod
299329 def djelme_index_name (cls ) -> str :
300330 """the index name for this record type
301331
302332 for ProtoDjelmeRecord
303333 """
334+ assert not cls .is_abstract
304335 return cls ._index ._name
305336
306337 @classmethod
@@ -311,6 +342,7 @@ def check_djelme_setup(cls, using: str | None = None) -> None:
311342 :raise: IndexOutOfSyncError if mappings are out of sync.
312343 :return: if index exists and mappings are in sync.
313344 """
345+ assert not cls .is_abstract
314346 _client = cls ._get_connection (using )
315347 _index_name = cls .djelme_index_name ()
316348 try :
@@ -330,10 +362,13 @@ def check_djelme_setup(cls, using: str | None = None) -> None:
330362
331363 @classmethod
332364 def do_teardown (cls , es_client ):
365+ assert not cls .is_abstract
333366 cls ._index .delete (using = es_client , ignore_unavailable = True )
334367
335- class Meta :
336- abstract = True
368+ @classmethod
369+ def refresh (cls , using : str | None = None ) -> None :
370+ assert not cls .is_abstract
371+ cls ._index .refresh (using = using )
337372
338373
339374class TimeseriesRecord (BaseDjelmeRecord ):
@@ -366,6 +401,7 @@ def search(
366401 ) -> esdsl .Search [
367402 "typing.Self"
368403 ]: # typing.Self added in py 3.11 -- str annotation until 3.10 eol
404+ assert not cls .is_abstract
369405 return super ().search (
370406 using = using ,
371407 index = (index or cls .format_timeseries_index_pattern ()),
@@ -389,7 +425,8 @@ def search_timeseries_range(
389425 return cls .search (index = _index_pattern ).filter (_timeseries_q )
390426
391427 @classmethod
392- def refresh_timeseries_indexes (cls , using : str | None = None ) -> None :
428+ def refresh (cls , using : str | None = None ) -> None :
429+ assert not cls .is_abstract
393430 cls ._get_connection (using ).indices .refresh (
394431 index = cls .format_timeseries_index_pattern ()
395432 )
@@ -408,6 +445,7 @@ def each_timeseries_index(
408445
409446 @classmethod
410447 def do_teardown (cls , es8_client : Elastic8Client ) -> None :
448+ assert not cls .is_abstract
411449 _indexname_wildcard = cls .format_timeseries_index_pattern ()
412450 _indices = es8_client .indices .get (index = _indexname_wildcard , features = "," )
413451 for _index_name in _indices .keys ():
@@ -420,6 +458,7 @@ def do_teardown(cls, es8_client: Elastic8Client) -> None:
420458
421459 @classmethod
422460 def get_timeseries_template (cls ) -> esdsl .ComposableIndexTemplate :
461+ assert not cls .is_abstract
423462 return cls ._index .as_composable_template (
424463 template_name = cls .get_timeseries_template_name (),
425464 pattern = cls .format_timeseries_index_pattern (),
0 commit comments