Я пытаюсь сохранить некоторые данные, которые у меня есть в формате CSV, в базе данных SQLite, используя SQLAlchemy в Jupyter Notebook. При попытке очистить или объединить данные сеанса для соединительных таблиц я получаю ошибку IntegrityError, сообщающую, что существует дублированная строка. Я не верю, что это правда, поскольку я проверил источник данных и не нашел дубликатов, а также проверил каждую строку перед их добавлением и также не нашел дубликатов. Таким образом, я не понимаю, почему происходит эта ошибка.
Проблема
Во-первых, CSV импортируются в фреймы данных, например:
Код: Выделить всё
recipe_df = pd.read_csv("csv/recipes.csv").replace({np.nan: None})
material_df = pd.read_csv("csv/materials.csv").replace({np.nan: None})
crystal_df = pd.read_csv("csv/crystals.csv").replace({np.nan: None})
Код: Выделить всё
class Recipe(Base):
__tablename__ = "recipe"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String)
# ...
# Relationships
materials = relationship("Material", secondary="recipe_to_material", back_populates="recipes")
crystals = relationship("Crystal", secondary="recipe_to_crystal", back_populates="recipes")
class Material(Base):
__tablename__ = "material"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String)
icon = Column(String)
is_recipe = Column(Boolean)
# Relationships
recipes = relationship("Recipe", secondary="recipe_to_material", back_populates="materials")
class Crystal(Base):
__tablename__ = "crystal"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String)
icon = Column(String)
# Relationships
recipes = relationship("Recipe", secondary="recipe_to_crystal", back_populates="crystals")
class RecipeToMaterial(Base):
__tablename__ = "recipe_to_material"
recipe_id = Column(Integer, ForeignKey("recipe.id"), primary_key=True)
material_id = Column(Integer, ForeignKey("material.id"), primary_key=True)
amount = Column(Integer)
class RecipeToCrystal(Base):
__tablename__ = "recipe_to_crystal"
recipe_id = Column(Integer, ForeignKey("recipe.id"), primary_key=True)
crystal_id = Column(Integer, ForeignKey("crystal.id"), primary_key=True)
amount = Column(Integer)
class MaterialHasRecipe(Base):
__tablename__ = "material_has_recipe"
material_id = Column(Integer, ForeignKey("material.id"), primary_key=True)
recipe_id = Column(Integer, ForeignKey("recipe.id"), primary_key=True)
Код: Выделить всё
def add_recipe_crystals():
recipe_ids:list[int] = []
crystal_ids:list[int] = []
amounts:list[int] = []
# Get all recipe and crystal ids
for _, row in recipe_df.iterrows():
recipe_id = session.query(Recipe.id).filter(Recipe.name == row["name"]).first()[0]
for i in range(1, 3):
crystal_name = row[f"crys_{i}_name"]
crystal_id = session.query(Crystal.id).filter(Crystal.name == crystal_name).first()
# if crystal_i doesn't exist, just skip this one
if crystal_id is None:
continue
crystal_id = crystal_id[0]
recipe_ids.append(recipe_id)
crystal_ids.append(crystal_id)
amounts.append(int(row[f"crys_{i}_count"]))
recipe_to_crystal_df = pd.DataFrame({
"recipe_id": recipe_ids,
"crystal_id": crystal_ids,
"amount": amounts,
})
# Create RecipeToCrystal instances
for i, row in recipe_to_crystal_df.iterrows():
rec_2_crys = RecipeToCrystal(
recipe_id = row["recipe_id"],
crystal_id = row["crystal_id"],
amount = row["amount"]
)
session.add(rec_2_crys)
Код: Выделить всё
IntegrityError: (sqlite3.IntegrityError) UNIQUE constraint failed: recipe_to_crystal.recipe_id, recipe_to_crystal.crystal_id
[SQL: INSERT INTO recipe_to_crystal (recipe_id, crystal_id, amount) VALUES (?, ?, ?)]
Подумав, что это может быть проблема с автоматической очисткой, я обернул функции для добавления в соединительные таблицы в функцию add_junction_tables() и обернул их в контекстный менеджер session.no_autoflush, например:
Код: Выделить всё
with session.no_autoflush:
add_junction_tables()
session.commit()
Код: Выделить всё
count = 0
for i, row in recipe_df.iterrows():
crys_1 = row["crys_1_name"]
crys_2 = row["crys_2_name"]
if crys_2 is None:
continue
if crys_1 in crys_2:
count += 1
print(f"In recipe {row["name"]}, the crystals {crys_1} and {crys_2} are the same")
if count == 0:
print("No duplicates found")
Я также сделал функцию проверки, например:
Код: Выделить всё
def recipe_crystal_exists(rec_id:int, crys_id:int):
existing_entry = session.query(RecipeToCrystal).filter(
RecipeToCrystal.recipe_id == rec_id,
RecipeToCrystal.crystal_id == crys_id
).first()
if existing_entry:
return True
return False
Код: Выделить всё
if recipe_crystal_exists(recipe_id, crystal_id):
raise ValueError(f"Recipe {recipe_id} and crystal {crystal_id} already exists in RecipeToCrystals")
Я также заменил session.add() на session.merge() , удалил сеанс, попытался полностью удалить базу данных и повторил попытку.
Вывод
Однако ошибка все еще сохраняется, и я остался в тупике. Самое странное, что add_recipe_materials(), который функционально идентичен и запускается до add_recipe_crystals(), похоже, не имеет этой проблемы. В противном случае я бы предположил, что ошибка будет выдана в первую очередь.
Что вызывает эту ошибку и/или есть ли какой-либо способ узнать, какие конкретно строки являются дубликатами, чтобы лучше диагностировать эту проблему? Любая помощь будет оценена по достоинству.
Подробнее здесь: https://stackoverflow.com/questions/798 ... -duplicate
Мобильная версия