Комбинация неуникальных внешних ключей для создания первичного ключа?

#python #database #sqlalchemy

#python #База данных #sqlalchemy

Вопрос:

Я пытаюсь создать таблицу в SQLAlchemy, которая соединяла бы фильм и категорию и присваивала бы ему оценку (у Shining в категории Thriller может быть оценка 700, В то время как у Shining в категории Drama может быть оценка 540 и т. Д.).

Это означает, что таблица будет содержать много ссылок на The Shining и много ссылок на Thriller, но только в одном случае, когда это комбинация The Shining и Thriller, которая также будет включать оценку 700. Эта картинка хорошо объясняет это.введите описание изображения здесь

Категория и фильм являются собственными классами.

Итак, в поисках ответа я обнаружил, что у меня может быть несколько первичных ключей, но в данном случае это не сработает, поскольку первичные ключи должны быть уникальными, а идентификатор фильма и идентификатор категории не являются уникальными.

Я нашел слово «Составной ключ», которое звучит так, как я ищу, но продемонстрировано в SQLAlchemy, похоже, оно не делает то же самое, что я думал.

Как мне создать таблицу, которая выполняет то, что я ищу? ОН ЖЕ «Оценки фильмов категории» с картинки? И причина, по которой я не просто создаю обычный идентификатор, который будет служить первичным ключом, заключается в том, что он никогда не будет использоваться, я только каждый раз запрашиваю эту таблицу с идентификаторами фильмов и категорий.

Заранее спасибо!

Ответ №1:

Это можно решить с помощью объекта ассоциации. Какое отношение «Многие ко многим» основано на объекте, чтобы иметь возможность хранить дополнительные данные.

 class Association(Base):
    __tablename__ = 'association'
    movie_id = Column(Integer, ForeignKey('movie.id'), primary_key=True)
    category_id = Column(Integer, ForeignKey('category.id'), primary_key=True)
    score = Column(Integer)
    votes = Column(Integer)
    category = relationship("Category", back_populates="movies")
    movie = relationship("Movie", back_populates="categories")

class Movie(Base):
    __tablename__ = 'movie'
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(50))
    categories = relationship("Association", back_populates="movie")

class Category(Base):
    __tablename__ = 'category'
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(50))
    movies = relationship("Association", back_populates="category")
 

Используйте sqlite_autoincrement=True с sqlite для создания автоинкремента на уровне базы данных.

Прежде чем вы сможете добавить категорию к фильму, она должна быть связана с экземпляром ассоциации:

 # create movie, append a category via association
m = Movie()
a = Association(score=700, votes=0)
a.category = Category()
m.categories.append(a)

# iterate through category objects via association, including association
# attributes
for assoc in m.categories:
    print(assoc.score)
    print(assoc.category)
 

Комментарии:

1. Спасибо, это очень помогает! Придется углубиться в ассоциативные объекты, чтобы понять, что происходит.

2. Хорошо, это, похоже, сработало, но теперь мне говорят: Column 'association.category_id' is marked as a member of the primary key for table 'association', but has no Python-side or server-side default generator indicated, nor does it indicate 'autoincrement=True' or 'nullable=True', and no explicit value is passed. Primary key columns typically may not store NULL. Note that as of SQLAlchemy 1.1, 'autoincrement=True' must be indicated explicitly for composite (e.g. multicolumn) primary keys if AUTO_INCREMENT/SERIAL/IDENTITY behavior is expected for one of the columns in the primary key.

3. Хорошо, исправление заключалось в добавлении a.movie = m , поскольку, похоже, оно не получало фильм автоматически, когда к нему добавлялась ассоциация. Не уверен, почему: (

4. @cobble, я изменил ответ, чтобы избавиться от предупреждения. Очень странно, что вам нужно установить a.movie = m , поскольку это должно обрабатываться append в модели. Я протестировал его локально, и результаты соответствуют ожидаемым без его установки.

5. Ах, хорошо, тогда я могу знать, почему это происходит. Я импортирую все данные из другой базы данных, что означает, что я устанавливаю идентификатор напрямую (поэтому автоинкремент ничего не делает). И мне тоже показалось странным, что мне нужно было его установить, поскольку мое понимание таблиц было таким же, как у вас, я продолжу играть с ним и посмотрю, получу ли я какие-либо другие результаты. Все равно спасибо!