593 lines
27 KiB
Python
593 lines
27 KiB
Python
# Importações de bibliotecas padrão
|
|
from typing import Generic, TypeVar, Dict, Type, Union, Any, List
|
|
from uuid import UUID
|
|
import copy
|
|
|
|
# Importações de bibliotecas de terceiros
|
|
from sqlalchemy import BinaryExpression, literal
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy.inspection import inspect
|
|
from sqlalchemy.future import select
|
|
from sqlalchemy import delete
|
|
from pydantic import BaseModel
|
|
from fastapi import HTTPException
|
|
from app.database.audit_log import audit_log
|
|
|
|
# Importações do seu próprio projeto
|
|
from app.database.RepositoryBase import (
|
|
RepositoryBase, IntegrityError, IntegrityConflictException,
|
|
SnippetException, NotFoundException
|
|
)
|
|
from app.database import models
|
|
|
|
Model = TypeVar("Model", bound=models.Base)
|
|
Schema = TypeVar("Schema", bound=BaseModel)
|
|
|
|
|
|
class RelationalTableRepository(RepositoryBase[Model], Generic[Model]):
|
|
def __init__(self, model: type[Model], session: AsyncSession) -> None:
|
|
super().__init__(model, session)
|
|
|
|
async def filter(
|
|
self,
|
|
*expressions: BinaryExpression,
|
|
) -> list[Model]:
|
|
query = select(self.model)
|
|
if expressions:
|
|
query = query.where(*expressions)
|
|
return list(await self.session.scalars(query))
|
|
|
|
async def create(self, data_one: Schema, *args: Any, **kwargs: Any) -> Model:
|
|
try:
|
|
if kwargs.get("related_info_extra_columns"):
|
|
db_model = await self.process_related_items_with_extra_columns(data_one, **kwargs)
|
|
else:
|
|
db_model = await self.process_related_items(data_one, **kwargs)
|
|
|
|
# Commit e refresh do modelo
|
|
await self.session.commit()
|
|
await self.session.refresh(db_model)
|
|
return db_model
|
|
|
|
except HTTPException as e:
|
|
# Repassa a HTTPException específica
|
|
raise e
|
|
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail="Erro ao criar relação com entidades relacionadas") from e
|
|
|
|
async def create_many(self, data: List[Schema], return_models: bool = False, *args: Any, **kwargs: Any) -> (
|
|
list[Model] | bool):
|
|
db_models = []
|
|
if kwargs.get("related_info_extra_columns"):
|
|
for single_data in data:
|
|
db_model = await self.process_related_items_with_extra_columns(single_data, **kwargs)
|
|
db_models.append(db_model)
|
|
else:
|
|
for single_data in data:
|
|
db_model = await self.process_related_items(single_data, **kwargs)
|
|
db_models.append(db_model)
|
|
|
|
try:
|
|
self.session.add_all(db_models)
|
|
await self.session.commit()
|
|
|
|
except IntegrityError:
|
|
raise IntegrityConflictException(
|
|
f"Na tabela {self.model.__tablename__} existe conflito com dados já cadastrados em campo único.",
|
|
)
|
|
except Exception as e:
|
|
raise SnippetException(f"Unknown error occurred: {e}") from e
|
|
|
|
if not return_models:
|
|
return True
|
|
|
|
for m in db_models:
|
|
await self.session.refresh(m)
|
|
|
|
return db_models
|
|
|
|
# async def update_by_id(self, update: Schema, coluna: str, *args: Any, **kwargs: Any) -> Model:
|
|
# related_info_append = kwargs.get('related_info_append', [])
|
|
# related_info_add = kwargs.get('related_info_add', [])
|
|
#
|
|
# # Prepara as variáveis para os relacionamentos simples e muitos para muitos
|
|
# simple_relationship_fields = {
|
|
# info["key"]: {"related_model": info["related_model"], "exclude_field": info["foreign_key"]}
|
|
# for info in related_info_add
|
|
# }
|
|
# many_to_many_relationship_fields = {
|
|
# info["key"]: info["related_model"] for info in related_info_append
|
|
# }
|
|
#
|
|
# uuid = str(update.uuid)
|
|
# db_model = await self.get_one_by_id(uuid, coluna, with_for_update=True)
|
|
#
|
|
# if not db_model:
|
|
# raise NotFoundException(
|
|
# f"{self.model.__tablename__} {coluna}={uuid} não encontrada."
|
|
# )
|
|
#
|
|
# values = update.model_dump(exclude_unset=True)
|
|
#
|
|
# # Atualiza campos simples
|
|
# for k, v in values.items():
|
|
# if k not in simple_relationship_fields and k not in many_to_many_relationship_fields:
|
|
# setattr(db_model, k, v)
|
|
#
|
|
# # Atualiza relacionamentos simples
|
|
# for field, details in simple_relationship_fields.items():
|
|
# related_model = details["related_model"]
|
|
# foreign_key = details["exclude_field"] # Isso é o "fk_pessoa_uuid"
|
|
#
|
|
# # Se a chave não estiver presente ou estiver com uma lista vazia, remove os itens relacionados
|
|
# if field not in values or values.get(field) == []:
|
|
# foreign_key_column = getattr(related_model, foreign_key)
|
|
#
|
|
# # Garantimos que a comparação é feita entre expressões SQL válidas
|
|
# await self.session.execute(
|
|
# delete(related_model).where(foreign_key_column == literal(db_model.uuid))
|
|
# )
|
|
# await self.session.flush()
|
|
# else:
|
|
# # Faz a Atualização de valores da lista e exclusão do que não estão na lista
|
|
# related_items = values.get(field)
|
|
# await self.update_simple_relationship(db_model=db_model, field=field, related_items=related_items,
|
|
# related_model=related_model, session=self.session,
|
|
# foreign_key=foreign_key)
|
|
#
|
|
# # Atualiza relacionamentos muitos para muitos
|
|
# for field, related_model in many_to_many_relationship_fields.items():
|
|
# # Se a chave não estiver presente ou estiver com uma lista vazia, remove os itens relacionados
|
|
# if field not in values or values.get(field) == []:
|
|
# setattr(db_model, field, [])
|
|
# else:
|
|
# related_item_ids = values.get(field)
|
|
# await self.update_many_to_many_relationship(db_model, field, related_item_ids, related_model,
|
|
# coluna=coluna)
|
|
#
|
|
# try:
|
|
# await self.session.commit()
|
|
# await self.session.refresh(db_model)
|
|
# return db_model
|
|
# except IntegrityError:
|
|
# raise IntegrityConflictException(
|
|
# f"{self.model.__tablename__} {coluna}={uuid} conflito com dados existentes."
|
|
# )
|
|
|
|
# @audit_log
|
|
async def update_by_id(self, update: Schema, coluna: str, *args: Any, **kwargs: Any) -> Model:
|
|
# Obtém as configurações dos relacionamentos
|
|
related_info_append = kwargs.get('related_info_append', [])
|
|
related_info_add = kwargs.get('related_info_add', [])
|
|
related_info_extra = kwargs.get('related_info_extra_columns', [])
|
|
|
|
# Preparação dos relacionamentos simples (one-to-many) e muitos-para-muitos simples
|
|
simple_relationship_fields = {
|
|
info["key"]: {"related_model": info["related_model"], "exclude_field": info["foreign_key"]}
|
|
for info in related_info_add
|
|
}
|
|
many_to_many_relationship_fields = {
|
|
info["key"]: info["related_model"] for info in related_info_append
|
|
}
|
|
# Para relacionamentos com extra columns, armazenamos a configuração completa
|
|
extra_relationship_fields = {info["key"]: info for info in related_info_extra}
|
|
|
|
# Busca o objeto base a ser atualizado
|
|
uuid = str(update.uuid)
|
|
db_model = await self.get_one_by_id(uuid, coluna, with_for_update=True)
|
|
if not db_model:
|
|
raise NotFoundException(f"{self.model.__tablename__} {coluna}={uuid} não encontrada.")
|
|
|
|
# Guardar o estado atual antes da modificação (cópia profunda para evitar problemas com lazy attributes)
|
|
original_model = copy.deepcopy(db_model)
|
|
|
|
values = update.model_dump(exclude_unset=True)
|
|
|
|
# Atualiza os campos simples (excluindo os campos que representam relacionamentos)
|
|
all_relationship_keys = set(simple_relationship_fields.keys()) | set(
|
|
many_to_many_relationship_fields.keys()) | set(extra_relationship_fields.keys())
|
|
for k, v in values.items():
|
|
if k not in all_relationship_keys:
|
|
setattr(db_model, k, v)
|
|
|
|
# Atualiza relacionamentos simples (one-to-many)
|
|
for field, details in simple_relationship_fields.items():
|
|
related_model = details["related_model"]
|
|
foreign_key = details["exclude_field"] # por exemplo, "fk_pessoa_uuid"
|
|
if field not in values or values.get(field) == []:
|
|
foreign_key_column = getattr(related_model, foreign_key)
|
|
await self.session.execute(
|
|
delete(related_model).where(foreign_key_column == literal(db_model.uuid))
|
|
)
|
|
await self.session.flush()
|
|
else:
|
|
related_items = values.get(field)
|
|
await self.update_simple_relationship(
|
|
db_model=db_model,
|
|
field=field,
|
|
related_items=related_items,
|
|
related_model=related_model,
|
|
session=self.session,
|
|
foreign_key=foreign_key
|
|
)
|
|
|
|
# Atualiza relacionamentos muitos-para-muitos simples
|
|
for field, related_model in many_to_many_relationship_fields.items():
|
|
if field not in values or values.get(field) == []:
|
|
setattr(db_model, field, [])
|
|
else:
|
|
related_item_ids = values.get(field)
|
|
await self.update_many_to_many_relationship(
|
|
db_model=db_model,
|
|
field=field,
|
|
related_item_ids=related_item_ids,
|
|
related_model=related_model,
|
|
coluna=coluna)
|
|
|
|
# Atualiza relacionamentos muitos-para-muitos com campos extras
|
|
for field, config in extra_relationship_fields.items():
|
|
# Se o campo não foi enviado no update, ignore a atualização desse relacionamento.
|
|
if field not in values:
|
|
continue
|
|
|
|
new_items = values.get(field) # Espera-se que seja uma lista (pode ser vazia)
|
|
await self.update_many_to_many_extra_relationship(
|
|
db_model=db_model,
|
|
field=field,
|
|
new_items=new_items,
|
|
association_model=config["association_model"],
|
|
base_foreign_key=config["base_foreign_key"],
|
|
related_foreign_key=config["related_foreign_key"],
|
|
extra_fields=config.get("extra_fields", [])
|
|
)
|
|
|
|
try:
|
|
await self.session.commit()
|
|
await self.session.refresh(db_model)
|
|
return db_model
|
|
except IntegrityError:
|
|
raise IntegrityConflictException(
|
|
f"{self.model.__tablename__} {coluna}={uuid} conflito com dados existentes."
|
|
)
|
|
|
|
async def update_many_by_ids(self, *args, **kwargs):
|
|
raise NotImplementedError("Update many não implementado para relacionamentos.")
|
|
|
|
@staticmethod
|
|
def _create_base_model(data: Schema, db_data,
|
|
related_info_append: List[Dict[str, Any]],
|
|
related_info_add: List[Dict[str, Any]],
|
|
related_info_extra_columns: List[Dict[str, Any]]) -> Model:
|
|
# Inicia com as chaves dos relacionamentos simples (append e add)
|
|
exclude_keys = {info["key"] for info in related_info_append + related_info_add + related_info_extra_columns}
|
|
|
|
return db_data(**data.model_dump(exclude=exclude_keys))
|
|
|
|
async def _collect_related_items(self, data: Schema, related_info_append: List[Dict[str, Any]]) -> (
|
|
Dict[str, List[Any]]):
|
|
related_items_to_append = {}
|
|
|
|
for info in related_info_append:
|
|
|
|
key = info["key"]
|
|
related_model = info["related_model"]
|
|
foreign_key_field = info["foreign_key_field"]
|
|
related_items_to_append[key] = []
|
|
|
|
related_data = getattr(data, key, [])
|
|
|
|
if not related_data:
|
|
continue # Pula para a próxima iteração se não houver dados para este campo
|
|
|
|
for related_item in related_data:
|
|
|
|
related_item_id = related_item[foreign_key_field] if isinstance(related_item, dict) else getattr(
|
|
related_item, foreign_key_field)
|
|
|
|
related_item_instance = await self.session.get(related_model, related_item_id)
|
|
|
|
if not related_item_instance:
|
|
raise HTTPException(status_code=400, detail=f"ID {related_item_id} inválido")
|
|
related_items_to_append[key].append(related_item_instance)
|
|
|
|
return related_items_to_append
|
|
|
|
async def process_related_items(self, data: Schema, **kwargs: Any) -> Model:
|
|
# Obtendo argumentos adicionais
|
|
db_data = kwargs.get('db_data')
|
|
related_info_append = kwargs.get('related_info_append', [])
|
|
related_info_add = kwargs.get('related_info_add', [])
|
|
related_info_extra_columns = []
|
|
# Criação do modelo base
|
|
db_model = self._create_base_model(data, db_data, related_info_append, related_info_add,
|
|
related_info_extra_columns)
|
|
# Processamento de related_info_append, se presente
|
|
if related_info_append:
|
|
related_items_to_append = await self._collect_related_items(data, related_info_append)
|
|
if related_items_to_append: # Verifica se há itens para serem relacionados
|
|
self._append_related_items(db_model, related_items_to_append)
|
|
# Adiciona o modelo à sessão
|
|
self.session.add(db_model)
|
|
await self.session.flush()
|
|
# Processamento de related_info_add, se presente
|
|
if related_info_add:
|
|
await self._add_related_items(data, db_model, related_info_add)
|
|
|
|
return db_model
|
|
|
|
async def process_related_items_with_extra_columns(self, data: Schema, **kwargs: Any) -> Model:
|
|
"""
|
|
Processa os relacionamentos onde a tabela de associação possui colunas extras.
|
|
|
|
Espera-se que em kwargs seja informado:
|
|
- db_data: a classe/modelo base a ser instanciado com os dados.
|
|
- related_info_extra_columns: uma lista de dicionários com as seguintes chaves:
|
|
* key: nome do atributo no schema contendo os dados do relacionamento.
|
|
* association_model: o modelo da tabela de associação (com colunas extras).
|
|
* base_foreign_key: nome da coluna que referencia o modelo base.
|
|
* related_foreign_key: nome da coluna que referencia a entidade relacionada.
|
|
* related_model: modelo da entidade relacionada (para validação, por exemplo).
|
|
* extra_fields: (opcional) lista de nomes dos campos extras que deverão ser extraídos.
|
|
"""
|
|
# Cria o modelo base usando os dados enviados no schema
|
|
|
|
db_data = kwargs.get('db_data')
|
|
related_info_append = []
|
|
related_info_extra_columns = kwargs.get('related_info_extra_columns', [])
|
|
related_info_add = kwargs.get('related_info_add', [])
|
|
|
|
db_model = self._create_base_model(data, db_data, related_info_append, related_info_add,
|
|
related_info_extra_columns,
|
|
)
|
|
|
|
self.session.add(db_model)
|
|
await self.session.flush() # Garante que db_model possua sua PK definida (ex.: uuid)
|
|
# Processa os relacionamentos com colunas extras
|
|
related_info_extra = kwargs.get('related_info_extra_columns', [])
|
|
|
|
for info in related_info_extra:
|
|
key = info.get("key")
|
|
association_model = info.get("association_model")
|
|
base_foreign_key = info.get("base_foreign_key")
|
|
related_foreign_key = info.get("related_foreign_key")
|
|
related_model = info.get("related_model") # Para validação do item relacionado
|
|
extra_fields = info.get("extra_fields", [])
|
|
|
|
# Obtém os dados do relacionamento a partir do schema
|
|
related_items = getattr(data, key, [])
|
|
|
|
if not related_items:
|
|
continue
|
|
|
|
for item in related_items:
|
|
|
|
# Verifica se o identificador da entidade relacionada está presente no item
|
|
if not hasattr(item, related_foreign_key):
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=f"O campo '{related_foreign_key}' é obrigatório em '{key}'."
|
|
)
|
|
related_item_id = item[related_foreign_key] if isinstance(item, dict) else getattr(item,
|
|
related_foreign_key)
|
|
|
|
# Valida se o item relacionado existe (usando o modelo relacionado)
|
|
if not related_model:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail="Não foi definido o modelo relacionado para o relacionamento."
|
|
)
|
|
related_instance = await self.session.get(related_model, related_item_id)
|
|
|
|
if not related_instance:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=f"ID {related_item_id} inválido para {related_model.__tablename__}"
|
|
)
|
|
|
|
# Extrai os valores dos campos extras, se existirem
|
|
extra_data = {field: getattr(item, field) for field in extra_fields if hasattr(item, field)}
|
|
|
|
# Cria a instância da tabela de associação, populando as FKs e os campos extras
|
|
association_instance = association_model(**{
|
|
base_foreign_key: db_model.uuid,
|
|
related_foreign_key: related_item_id,
|
|
**extra_data
|
|
})
|
|
|
|
self.session.add(association_instance)
|
|
|
|
# Realiza um flush para persistir os itens da associação antes de continuar
|
|
await self.session.flush()
|
|
|
|
# Se houver relacionamentos do tipo um-para-muitos (add), processa-os também
|
|
related_info_add = kwargs.get('related_info_add', [])
|
|
if related_info_add:
|
|
await self._add_related_items(data, db_model, related_info_add)
|
|
|
|
return db_model
|
|
|
|
@staticmethod
|
|
def _append_related_items(db_model: Model, related_items_to_append: Dict[str, List[Model]]) -> None:
|
|
for key, items in related_items_to_append.items():
|
|
getattr(db_model, key).extend(items)
|
|
|
|
async def _add_related_items(self, data: Schema, db_model: Model, related_info_add: List[Dict[str, Any]]) -> None:
|
|
|
|
for info in related_info_add:
|
|
key = info["key"]
|
|
foreign_key = info["foreign_key"]
|
|
related_model = info["related_model"]
|
|
relations = info.get("relations", []) # Pode ser uma lista de relações
|
|
|
|
related_items = getattr(data, key, [])
|
|
if not related_items:
|
|
continue # Pula para a próxima iteração se não houver dados para este campo
|
|
|
|
for related_item in related_items:
|
|
for relation in relations:
|
|
related_model_fk = relation.get("related_model_fk")
|
|
foreign_key_fk = relation.get("foreign_key_fk")
|
|
|
|
if related_model_fk and foreign_key_fk:
|
|
fk = getattr(related_item, foreign_key_fk)
|
|
relacao = await self.session.get(related_model_fk, fk)
|
|
if not relacao:
|
|
raise HTTPException(status_code=404,
|
|
detail=f"{related_model_fk.__name__} com UUID {fk} não encontrado")
|
|
|
|
new_item = related_model(**related_item.model_dump(), **{foreign_key: db_model.uuid})
|
|
self.session.add(new_item)
|
|
|
|
@staticmethod
|
|
async def update_simple_relationship(
|
|
db_model: Model,
|
|
field: str,
|
|
related_items: List[Dict[str, Any]],
|
|
related_model: Type[Model],
|
|
session: AsyncSession,
|
|
foreign_key: str
|
|
) -> None:
|
|
current_items = getattr(db_model, field, [])
|
|
|
|
# Lista de UUIDs dos itens enviados no JSON
|
|
new_item_uuids = {str(item['uuid']) for item in related_items if 'uuid' in item}
|
|
|
|
# Remover itens que não estão mais presentes no JSON
|
|
for item in current_items:
|
|
if str(item.uuid) not in new_item_uuids:
|
|
foreign_key_column = getattr(related_model, foreign_key)
|
|
await session.execute(
|
|
delete(related_model).where(foreign_key_column == literal(db_model.uuid)).where(
|
|
related_model.uuid == literal(item.uuid)
|
|
)
|
|
)
|
|
await session.flush()
|
|
|
|
# Atualizar ou adicionar novos itens
|
|
for related_item in related_items:
|
|
if 'uuid' in related_item:
|
|
item_uuid = str(related_item['uuid'])
|
|
db_item = next((item for item in current_items if str(item.uuid) == item_uuid), None)
|
|
if db_item:
|
|
for k, v in related_item.items():
|
|
setattr(db_item, k, v)
|
|
else:
|
|
raise NotFoundException(f"Related item {field} with UUID {item_uuid} not found.")
|
|
else:
|
|
new_item = related_model(**related_item)
|
|
setattr(new_item, db_model.__class__.__name__.lower() + "_uuid", db_model.uuid)
|
|
session.add(new_item)
|
|
current_items.append(new_item)
|
|
|
|
setattr(db_model, field, current_items)
|
|
await session.flush()
|
|
|
|
async def update_many_to_many_relationship(
|
|
self,
|
|
db_model: Model,
|
|
field: str,
|
|
related_item_ids: List[Union[str, UUID, Dict[str, Any]]], # Aceita "strings", UUIDs ou dicionários
|
|
related_model: Type[Model], # Adicionando o modelo relacionado
|
|
coluna: str = "uuid" # Adicionando a coluna para uso dinâmico
|
|
) -> None:
|
|
current_items = getattr(db_model, field, [])
|
|
current_item_ids = {str(getattr(item, coluna)) for item in current_items}
|
|
|
|
# Extrair "IDs" dos dicionários, se necessário
|
|
new_item_ids = set()
|
|
for item in related_item_ids:
|
|
if isinstance(item, dict) and coluna in item:
|
|
new_item_ids.add(str(item[coluna]))
|
|
else:
|
|
new_item_ids.add(str(item))
|
|
|
|
# Remover itens que não estão mais relacionados
|
|
items_to_remove = current_item_ids - new_item_ids
|
|
for item in current_items:
|
|
if str(getattr(item, coluna)) in items_to_remove:
|
|
current_items.remove(item)
|
|
|
|
# Adicionar novos itens
|
|
items_to_add = new_item_ids - current_item_ids
|
|
for item_id in items_to_add:
|
|
related_item = await self.session.get(related_model, item_id)
|
|
if related_item:
|
|
current_items.append(related_item)
|
|
else:
|
|
raise NotFoundException(f"Related item {field} with UUID {item_id} not found.")
|
|
|
|
setattr(db_model, field, current_items)
|
|
|
|
async def update_many_to_many_extra_relationship(
|
|
self,
|
|
db_model: Model,
|
|
field: str,
|
|
new_items: List[Union[Dict[str, Any], Any]], # Aceita dicionários ou objetos com .dict()
|
|
association_model: Type[Model],
|
|
base_foreign_key: str,
|
|
related_foreign_key: str,
|
|
extra_fields: List[str] = [],
|
|
) -> None:
|
|
"""
|
|
Atualiza um relacionamento muitos-para-muitos com colunas extras.
|
|
|
|
Parâmetros:
|
|
- db_model: objeto base que possui o relacionamento.
|
|
- field: nome do atributo em db_model que contém a lista de associações.
|
|
- new_items: lista de novos itens para o relacionamento (pode ser dicionário ou objeto com .dict()).
|
|
- association_model: classe mapeada da tabela de associação.
|
|
- base_foreign_key: nome do atributo da associação que referencia db_model (ex: 'fk_manutencao_uuid').
|
|
- related_foreign_key: nome do atributo da associação que referencia o objeto relacionado (ex: 'fk_itens_equipamentos_uuid').
|
|
- extra_fields: lista de campos extras que também devem ser atualizados ou definidos.
|
|
"""
|
|
current_assocs = getattr(db_model, field, [])
|
|
|
|
if not new_items:
|
|
# Se o payload enviar uma lista vazia, remova todas as associações
|
|
for assoc in current_assocs:
|
|
await self.session.delete(assoc)
|
|
setattr(db_model, field, [])
|
|
await self.session.flush()
|
|
return
|
|
|
|
new_assoc_instances = []
|
|
for new_item in new_items:
|
|
# Suporta tanto dicionários quanto objetos com método .dict()
|
|
new_item_data = new_item if isinstance(new_item, dict) else new_item.dict()
|
|
|
|
if related_foreign_key not in new_item_data:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=f"O campo '{related_foreign_key}' é obrigatório em '{field}'."
|
|
)
|
|
new_related_id = new_item_data[related_foreign_key]
|
|
|
|
# Procura uma associação existente que corresponda ao valor do foreign key
|
|
existing_assoc = next(
|
|
(assoc for assoc in current_assocs if str(getattr(assoc, related_foreign_key)) == str(new_related_id)),
|
|
None
|
|
)
|
|
|
|
if existing_assoc:
|
|
# Atualiza os campos extras se houver valores informados
|
|
for ef in extra_fields:
|
|
if ef in new_item_data:
|
|
setattr(existing_assoc, ef, new_item_data[ef])
|
|
new_assoc_instances.append(existing_assoc)
|
|
else:
|
|
# Cria uma nova associação, incluindo os campos extras
|
|
extra_data = {ef: new_item_data.get(ef) for ef in extra_fields if ef in new_item_data}
|
|
new_assoc = association_model(**{
|
|
base_foreign_key: db_model.uuid,
|
|
related_foreign_key: new_related_id,
|
|
**extra_data
|
|
})
|
|
self.session.add(new_assoc)
|
|
await self.session.flush()
|
|
new_assoc_instances.append(new_assoc)
|
|
|
|
# Mescla as associações atuais com as novas (ou atualizadas)
|
|
merged_assocs = list({*current_assocs, *new_assoc_instances})
|
|
setattr(db_model, field, merged_assocs)
|