def _init_relations(cls) -> None: def get_related_model(related_app_name: str, related_model_name: str) -> Type["Model"]: """ Test, if app and model really exist. Throws a ConfigurationError with a hopefully helpful message. If successfull, returns the requested model. :raises ConfigurationError: If no such app exists. """ try: return cls.apps[related_app_name][related_model_name] except KeyError: if related_app_name not in cls.apps: raise ConfigurationError(f"No app with name '{related_app_name}' registered.") raise ConfigurationError( f"No model with name '{related_model_name}' registered in" f" app '{related_app_name}'." ) def split_reference(reference: str) -> Tuple[str, str]: """ Test, if reference follow the official naming conventions. Throws a ConfigurationError with a hopefully helpful message. If successfull, returns the app and the model name. :raises ConfigurationError: If no model reference is invalid. """ items = reference.split(".") if len(items) != 2: # pragma: nocoverage raise ConfigurationError( ( "'%s' is not a valid model reference Bad Reference." " Should be something like <appname>.<modelname>." ) % reference ) return (items[0], items[1]) for app_name, app in cls.apps.items(): for model_name, model in app.items(): if model._meta._inited: continue model._meta._inited = True if not model._meta.db_table: model._meta.db_table = model.__name__.lower() # TODO: refactor to share logic between FK & O2O for field in model._meta.fk_fields: fk_object = cast(ForeignKeyFieldInstance, model._meta.fields_map[field]) reference = fk_object.model_name related_app_name, related_model_name = split_reference(reference) related_model = get_related_model(related_app_name, related_model_name) if fk_object.to_field: related_field = related_model._meta.fields_map.get(fk_object.to_field, None) if related_field: if related_field.unique: key_fk_object = deepcopy(related_field) fk_object.to_field_instance = related_field else: raise ConfigurationError( f'field "{fk_object.to_field}" in model' f' "{related_model_name}" is not unique' ) else: raise ConfigurationError( f'there is no field named "{fk_object.to_field}"' f' in model "{related_model_name}"' ) else: key_fk_object = deepcopy(related_model._meta.pk) fk_object.to_field_instance = related_model._meta.pk fk_object.to_field = related_model._meta.pk_attr key_field = f"{field}_id" key_fk_object.pk = False key_fk_object.unique = False key_fk_object.index = fk_object.index key_fk_object.default = fk_object.default key_fk_object.null = fk_object.null key_fk_object.generated = fk_object.generated key_fk_object.reference = fk_object key_fk_object.description = fk_object.description if fk_object.source_field: key_fk_object.source_field = fk_object.source_field else: key_fk_object.source_field = key_field model._meta.add_field(key_field, key_fk_object) fk_object.related_model = related_model fk_object.source_field = key_field backward_relation_name = fk_object.related_name if backward_relation_name is not False: if not backward_relation_name: backward_relation_name = f"{model._meta.db_table}s" if backward_relation_name in related_model._meta.fields: raise ConfigurationError( f'backward relation "{backward_relation_name}" duplicates in' f" model {related_model_name}" ) fk_relation = BackwardFKRelation( model, f"{field}_id", key_fk_object.source_field, fk_object.null, fk_object.description, ) fk_relation.to_field_instance = fk_object.to_field_instance related_model._meta.add_field(backward_relation_name, fk_relation) for field in model._meta.o2o_fields: o2o_object = cast(OneToOneFieldInstance, model._meta.fields_map[field]) reference = o2o_object.model_name related_app_name, related_model_name = split_reference(reference) related_model = get_related_model(related_app_name, related_model_name) if o2o_object.to_field: related_field = related_model._meta.fields_map.get( o2o_object.to_field, None ) if related_field: if related_field.unique: key_o2o_object = deepcopy(related_field) o2o_object.to_field_instance = related_field else: raise ConfigurationError( f'field "{o2o_object.to_field}" in model' f' "{related_model_name}" is not unique' ) else: raise ConfigurationError( f'there is no field named "{o2o_object.to_field}"' f' in model "{related_model_name}"' ) else: key_o2o_object = deepcopy(related_model._meta.pk) o2o_object.to_field_instance = related_model._meta.pk o2o_object.to_field = related_model._meta.pk_attr key_field = f"{field}_id" key_o2o_object.pk = o2o_object.pk key_o2o_object.index = o2o_object.index key_o2o_object.default = o2o_object.default key_o2o_object.null = o2o_object.null key_o2o_object.unique = o2o_object.unique key_o2o_object.generated = o2o_object.generated key_o2o_object.reference = o2o_object key_o2o_object.description = o2o_object.description if o2o_object.source_field: key_o2o_object.source_field = o2o_object.source_field else: key_o2o_object.source_field = key_field model._meta.add_field(key_field, key_o2o_object) o2o_object.related_model = related_model o2o_object.source_field = key_field backward_relation_name = o2o_object.related_name if backward_relation_name is not False: if not backward_relation_name: backward_relation_name = f"{model._meta.db_table}" if backward_relation_name in related_model._meta.fields: raise ConfigurationError( f'backward relation "{backward_relation_name}" duplicates in' f" model {related_model_name}" ) o2o_relation = BackwardOneToOneRelation( model, f"{field}_id", key_o2o_object.source_field, null=True, description=o2o_object.description, ) o2o_relation.to_field_instance = o2o_object.to_field_instance related_model._meta.add_field(backward_relation_name, o2o_relation) if o2o_object.pk: model._meta.pk_attr = key_field for field in list(model._meta.m2m_fields): m2m_object = cast(ManyToManyFieldInstance, model._meta.fields_map[field]) if m2m_object._generated: continue backward_key = m2m_object.backward_key if not backward_key: backward_key = f"{model._meta.db_table}_id" if backward_key == m2m_object.forward_key: backward_key = f"{model._meta.db_table}_rel_id" m2m_object.backward_key = backward_key reference = m2m_object.model_name related_app_name, related_model_name = split_reference(reference) related_model = get_related_model(related_app_name, related_model_name) m2m_object.related_model = related_model backward_relation_name = m2m_object.related_name if not backward_relation_name: backward_relation_name = ( m2m_object.related_name ) = f"{model._meta.db_table}s" if backward_relation_name in related_model._meta.fields: raise ConfigurationError( f'backward relation "{backward_relation_name}" duplicates in' f" model {related_model_name}" ) if not m2m_object.through: related_model_table_name = ( related_model._meta.db_table if related_model._meta.db_table else related_model.__name__.lower() ) m2m_object.through = f"{model._meta.db_table}_{related_model_table_name}" m2m_relation = ManyToManyFieldInstance( f"{app_name}.{model_name}", m2m_object.through, forward_key=m2m_object.backward_key, backward_key=m2m_object.forward_key, related_name=field, field_type=model, description=m2m_object.description, ) m2m_relation._generated = True model._meta.filters.update(get_m2m_filters(field, m2m_object)) related_model._meta.add_field(backward_relation_name, m2m_relation)
def _init_relations(cls) -> None: for app_name, app in cls.apps.items(): for model_name, model in app.items(): if model._meta._inited: continue else: model._meta._inited = True if not model._meta.table: model._meta.table = model.__name__.lower() for field in model._meta.fk_fields: field_object = cast(fields.ForeignKeyField, model._meta.fields_map[field]) reference = field_object.model_name related_app_name, related_model_name = reference.split('.') related_model = cls.apps[related_app_name][ related_model_name] field_object.type = related_model backward_relation_name = field_object.related_name if not backward_relation_name: backward_relation_name = '{}s'.format( model._meta.table) if backward_relation_name in related_model._meta.fields: raise ConfigurationError( 'backward relation "{}" duplicates in model {}'. format(backward_relation_name, related_model_name)) fk_relation = fields.BackwardFKRelation( model, '{}_id'.format(field)) setattr(related_model, backward_relation_name, fk_relation) related_model._meta.filters.update( get_backward_fk_filters(backward_relation_name, fk_relation)) related_model._meta.backward_fk_fields.add( backward_relation_name) related_model._meta.fetch_fields.add( backward_relation_name) related_model._meta.fields_map[ backward_relation_name] = fk_relation related_model._meta.fields.add(backward_relation_name) for field in model._meta.m2m_fields: field_mobject = cast(fields.ManyToManyField, model._meta.fields_map[field]) if field_mobject._generated: continue backward_key = field_mobject.backward_key if not backward_key: backward_key = '{}_id'.format(model._meta.table) field_mobject.backward_key = backward_key reference = field_mobject.model_name related_app_name, related_model_name = reference.split('.') related_model = cls.apps[related_app_name][ related_model_name] field_mobject.type = related_model backward_relation_name = field_mobject.related_name if not backward_relation_name: backward_relation_name = field_mobject.related_name = \ '{}_through'.format(model._meta.table) if backward_relation_name in related_model._meta.fields: raise ConfigurationError( 'backward relation "{}" duplicates in model {}'. format(backward_relation_name, related_model_name)) if not field_mobject.through: related_model_table_name = ( related_model._meta.table if related_model._meta.table else related_model.__name__.lower()) field_mobject.through = '{}_{}'.format( model._meta.table, related_model_table_name, ) m2m_relation = fields.ManyToManyField( '{}.{}'.format(app_name, model_name), field_mobject.through, forward_key=field_mobject.backward_key, backward_key=field_mobject.forward_key, related_name=field, type=model) m2m_relation._generated = True setattr( related_model, backward_relation_name, m2m_relation, ) model._meta.filters.update( get_m2m_filters(field, field_mobject)) related_model._meta.filters.update( get_m2m_filters(backward_relation_name, m2m_relation)) related_model._meta.m2m_fields.add(backward_relation_name) related_model._meta.fetch_fields.add( backward_relation_name) related_model._meta.fields_map[ backward_relation_name] = m2m_relation related_model._meta.fields.add(backward_relation_name)
def _init_relations(cls) -> None: def get_related_model(related_app_name: str, related_model_name: str): """ Test, if app and model really exist. Throws a ConfigurationError with a hopefully helpful message. If successfull, returns the requested model. """ try: return cls.apps[related_app_name][related_model_name] except KeyError: if related_app_name not in cls.apps: raise ConfigurationError( "No app with name '{}' registered.".format( related_app_name)) raise ConfigurationError( "No model with name '{}' registered in app '{}'.".format( related_model_name, related_app_name)) def split_reference(reference: str) -> Tuple[str, str]: """ Test, if reference follow the official naming conventions. Throws a ConfigurationError with a hopefully helpful message. If successfull, returns the app and the model name. """ items = reference.split(".") if len(items) != 2: # pragma: nocoverage raise ConfigurationError( ("'%s' is not a valid model reference Bad Reference." " Should be something like <appname>.<modelname>.") % reference) return (items[0], items[1]) for app_name, app in cls.apps.items(): for model_name, model in app.items(): if model._meta._inited: continue model._meta._inited = True if not model._meta.table: model._meta.table = model.__name__.lower() for field in model._meta.fk_fields: fk_object = cast(fields.ForeignKeyField, model._meta.fields_map[field]) reference = fk_object.model_name related_app_name, related_model_name = split_reference( reference) related_model = get_related_model(related_app_name, related_model_name) key_field = "{}_id".format(field) key_fk_object = deepcopy(related_model._meta.pk) key_fk_object.pk = False key_fk_object.index = fk_object.index key_fk_object.default = fk_object.default key_fk_object.null = fk_object.null key_fk_object.generated = fk_object.generated key_fk_object.reference = fk_object key_fk_object.description = fk_object.description if fk_object.source_field: key_fk_object.source_field = fk_object.source_field fk_object.source_field = key_field else: fk_object.source_field = key_field key_fk_object.source_field = key_field model._meta.add_field(key_field, key_fk_object) fk_object.type = related_model backward_relation_name = fk_object.related_name if not backward_relation_name: backward_relation_name = "{}s".format( model._meta.table) if backward_relation_name in related_model._meta.fields: raise ConfigurationError( 'backward relation "{}" duplicates in model {}'. format(backward_relation_name, related_model_name)) fk_relation = fields.BackwardFKRelation( model, "{}_id".format(field), fk_object.null, fk_object.description) related_model._meta.add_field(backward_relation_name, fk_relation) for field in list(model._meta.m2m_fields): m2m_object = cast(fields.ManyToManyField, model._meta.fields_map[field]) if m2m_object._generated: continue backward_key = m2m_object.backward_key if not backward_key: backward_key = "{}_id".format(model._meta.table) if backward_key == m2m_object.forward_key: backward_key = "{}_rel_id".format( model._meta.table) m2m_object.backward_key = backward_key reference = m2m_object.model_name related_app_name, related_model_name = split_reference( reference) related_model = get_related_model(related_app_name, related_model_name) m2m_object.type = related_model backward_relation_name = m2m_object.related_name if not backward_relation_name: backward_relation_name = m2m_object.related_name = "{}_through".format( model._meta.table) if backward_relation_name in related_model._meta.fields: raise ConfigurationError( 'backward relation "{}" duplicates in model {}'. format(backward_relation_name, related_model_name)) if not m2m_object.through: related_model_table_name = ( related_model._meta.table if related_model._meta.table else related_model.__name__.lower()) m2m_object.through = "{}_{}".format( model._meta.table, related_model_table_name) m2m_relation = fields.ManyToManyField( "{}.{}".format(app_name, model_name), m2m_object.through, forward_key=m2m_object.backward_key, backward_key=m2m_object.forward_key, related_name=field, type=model, description=m2m_object.description, ) m2m_relation._generated = True model._meta.filters.update( get_m2m_filters(field, m2m_object)) related_model._meta.add_field(backward_relation_name, m2m_relation)
def _init_relations(cls) -> None: for app_name, app in cls.apps.items(): for model_name, model in app.items(): if model._meta._inited: continue else: model._meta._inited = True if not model._meta.table: model._meta.table = model.__name__.lower() for field in model._meta.fk_fields: fk_object = cast(fields.ForeignKeyField, model._meta.fields_map[field]) reference = fk_object.model_name related_app_name, related_model_name = reference.split(".") related_model = cls.apps[related_app_name][ related_model_name] key_field = "{}_id".format(field) fk_object.source_field = key_field key_fk_object = deepcopy(related_model._meta.pk) key_fk_object.pk = False key_fk_object.index = fk_object.index key_fk_object.default = fk_object.default key_fk_object.null = fk_object.null key_fk_object.generated = fk_object.generated key_fk_object.reference = fk_object model._meta.add_field(key_field, key_fk_object) fk_object.type = related_model backward_relation_name = fk_object.related_name if not backward_relation_name: backward_relation_name = "{}s".format( model._meta.table) if backward_relation_name in related_model._meta.fields: raise ConfigurationError( 'backward relation "{}" duplicates in model {}'. format(backward_relation_name, related_model_name)) fk_relation = fields.BackwardFKRelation( model, "{}_id".format(field)) related_model._meta.add_field(backward_relation_name, fk_relation) for field in model._meta.m2m_fields: m2m_object = cast(fields.ManyToManyField, model._meta.fields_map[field]) if m2m_object._generated: continue backward_key = m2m_object.backward_key if not backward_key: backward_key = "{}_id".format(model._meta.table) m2m_object.backward_key = backward_key reference = m2m_object.model_name related_app_name, related_model_name = reference.split(".") related_model = cls.apps[related_app_name][ related_model_name] m2m_object.type = related_model backward_relation_name = m2m_object.related_name if not backward_relation_name: backward_relation_name = m2m_object.related_name = "{}_through".format( model._meta.table) if backward_relation_name in related_model._meta.fields: raise ConfigurationError( 'backward relation "{}" duplicates in model {}'. format(backward_relation_name, related_model_name)) if not m2m_object.through: related_model_table_name = ( related_model._meta.table if related_model._meta.table else related_model.__name__.lower()) m2m_object.through = "{}_{}".format( model._meta.table, related_model_table_name) m2m_relation = fields.ManyToManyField( "{}.{}".format(app_name, model_name), m2m_object.through, forward_key=m2m_object.backward_key, backward_key=m2m_object.forward_key, related_name=field, type=model, ) m2m_relation._generated = True model._meta.filters.update( get_m2m_filters(field, m2m_object)) related_model._meta.add_field(backward_relation_name, m2m_relation)