Найти - Пользователи
Полная версия: Пару нубских вопросов на тему "Как правильно связать таблицы?"
Начало » Базы данных » Пару нубских вопросов на тему "Как правильно связать таблицы?"
1 2
TitanFighter
Утро\день\вечер добрый.

Подскажите пожалуйста, как правильно в моем случае построить БД (в контексте Django)? Хочу учиться правильным вещам с первых шагов, чтоб потом не переделывать и не переучиваться.

Есть парсер, который тянет из разных источников <сеансы фильмов в кинотеатрах> и <описания фильмов> и соответственно формирует 2 файла (временно). Сейчас нахожусь на стадии формирования модели в Джанго.

### Инфо для данного блока берется из одного файла <сеансы фильмов в кинотеатрах> и соответственно данные легко разложить по переменным
class Cinemas(models.Model):
    cinema_city
    cinema_name
    cinema_street
    cinema_phone
class MoviesTimetable(models.Model):
    cinema = models.ForeignKey(Cinemas)
    movie_name
    movie_dates
    movie_times
### Инфо для данного блока берется из файла <описания фильмов>
class MoviesDetails(models.Model):
    movie_name
    movie_length
    movie_genre
    movie_description

Вопросы:
- Нормальное ли явление, когда в 2х таблицах (MoviesTimetable и MoviesDetails) будут 2 одинаковых поля (movie_name) с одинаковыми названиями фильмов? Вопрос интересен больше в плоскости «дублирования информации», что и там и там одни и те же названия фильмов будут.
- Вопрос, расширяющий предыдущий… Как правильно связать class MoviesTimetable и class MoviesDetails? По названию фильма? Или делать movie = models.ForeignKey(MoviesTimetable)?

- Есть ли разница, в каком классе указывать ForeignKey?
Т.е., есть ли разница, если я сделаю скажем
timetable = models.ForeignKey(MoviesTimetable), вместо
cinema = models.ForeignKey(Cinemas) как сейчас?

П.С. Гуглю уже пол дня (и часть ночи) эти вопросы — прочитал уже несколько туториалов аля «Как правильно создавать базы» — не могу найти ответы на свои простые вопросы (или они были, но поданы в другом виде и я их просто не осознал из-за отсутствия опыта).

Спасибо.
PooH
Как-то так:
class Cinema(models.Model):
    city
    name
    street
    phone
class Movie(models.Model):
    name
    length
    genre
    description
class MoviesTimetable(models.Model):
    cinema = models.ForeignKey(Cinemas)
    movie = models.ForeignKey(Movie)
    dates
    times
хотя сдается мне MoviesTimetable должен еще быть разложен на таблицы, но не зная постановки задачи не подскажу
TitanFighter
PooH
хотя сдается мне MoviesTimetable должен еще быть разложен на таблицы, но не зная постановки задачи не подскажу
1) Разве MoviesTimetable еще не является самым простым вариантом? Или раскладывать еще на Даты? 1ое августа, 2ое августа и тд?)

2) Из MoviesTimetable вы убрали Name.
class Movie и class MoviesTimetable наполняются из двух разных файлов, где к примеру Терминатор из MoviesTimetable совершенно ничего не знает о своем описании в Movie.

Не могли бы вы мне объяснить, пожалуйста, откуда Терминатор из класса MoviesTimetable знает, что ему нужно открыть в Movies свое описание, а не скажем какую то Матрицу? Если бы данные заливались из 1го файла, где уже имеется связь, эта связь (расписание-описание) бы переносилась в БД, а так между двумя файлами нету никакой связи. Единственное, что у них есть общее - это название фильма. Может нужно написать какой то обработчик, чтобы связывал по названию фильма расписание с описанием до размещения инфы в БД? Не буду же я руками в БД присваивать каждому фильму из MoviesTimetable соответствующий ID из Movie.

Т.е. прошу подсказать, как связать 2 “блока” информации между собой, которые ничего друг о друге изначально не знают. Вчера еще я предполагал, что по колонке Name в каждом из них, но из класса MoviesTimetable вы эту колонку убрали и теперь я совсем в потерях

Примеры файлов

"Ивано-Франковск": {
        "Линия кино Космос": {
            "address": "улица Независимости, 97",
            "phone_number": " 0342503055, 0342502625",
            "films": {
                "film_times": [
                    "10:00"
                ],
                "film_dates": "19 августа, среда",
                "film_name": "Пиксели",
            }
        }
--------------------------------
{
    "Пиксели": {
        "details": {
            "films_genre": [
                "фантастика, комедия"
            ],
            "films_length": [
                "106 мин"
            ],
            "films_description": [
                "Пришельцы из космоса по ошибке трактуют тематику космических компьютерных игр как объявление им войны и атакуют Землю, используя видеоигры 80-х в качестве моделей для разнообразных стратегий вторжения. Президент страны обращается за помощью к другу детства, чемпиону тех лет по игре в автоматы Сэму Бреннеру. Тот собирает команду игроков, специалистов по аркадам, чтобы сообща одолеть пришельцев и спасти планету."
            ],
        },
    },
PooH
TitanFighter
1) Разве MoviesTimetable еще не является самым простым вариантом? Или раскладывать еще на Даты? 1ое августа, 2ое августа и тд?)
Пока не надо. Для начала пойдет, надо смотреть по задаче, не существует единственно верного разложения.

TitanFighter
2) Из MoviesTimetable вы убрали Name.
class Movie и class MoviesTimetable наполняются из двух разных файлов, где к примеру Терминатор из MoviesTimetable совершенно ничего не знает о своем описании в Movie.

У каждой модели есть атрибут id(если даже вы его не опишите явно, django все равно его создаст). Атрибуты cinema = models.ForeignKey(Cinema) и movie = models.ForeignKey(Movie) создадут в таблице MoviesTimetable колонки cinema_id и movie_id в которые будут писаться id связанных с ней Cinema и Movie.

TitanFighter
Если бы данные заливались из 1го файла, где уже имеется связь, эта связь (расписание-описание) бы переносилась в БД, а так между двумя файлами нету никакой связи.

Для поля name у Movie и Cinema ставите db_index. Сначала проходите по файлу с описаниями и создаете Movie. Потом читаете файл с расписаниями. Для каждого фильма делает поиск по имени в Movie, типа такого
#пусть у вас movie_name имя фильма из файла расписаний
try:
    movie = Movie.objects.get(name=movie_name)
except Movie.DoesNotExists:
    movie = Movie(name=movie_name) #если не нашли с таким названием - создаем
#также ищете и Cinema
#потом создаете объект timetable и назначаете ему Movie и Cinema
timetable = MoviesTimetable(movie=movie, cinema=cinema, бла-бла-бла)
timetable.save()
У вас будет MoviesTimetable связан с Movie по MoviesTimetable.movie_id = Movie.id и с Cinema по MoviesTimetable.cinema_id = Cinema.id. У django-моделей есть методы чтобы их подгружать из базы.
TitanFighter
Узнал, чтоб не делать проверку через try except, можно воспользоваться objects.get_or_create
TitanFighter
Pooh, в продолжении темы, уделите пожалуйста пару минут.

Я создал модель, как вы и предложили, все работает. Но сейчас пришло время расширять функциональность и начал знакомиться со связью многие-ко-многим и ковырять гугл, хабру и документацию Джанго на эту тему.

Насколько я могу понять, связь кинотеатры-фильмы как раз относиться к типу связей в БД многие-ко-многим = в одном кинотеатре может быть показаны разные фильмы и эти фильмы могут быть показаны в других кинотеатрах. Связь многие-ко-многим подразумевает создание промежуточной (третьей) таблицы и как я понимаю, в примере выше вы это реализовали в MoviesTimetable, только используя ForeignKey вместо ManyToMany с элементом through

Собственно вопрос: Почему вы предложили построить связи через ForeignKey из, по сути, “промежуточной” (третьей) таблицы, а не через ManyToMany? Не исключаю, что я что-то не понимаю или чего-то не вижу и прошу мне объяснить, что не так

По примеру документации Джанго, накатал такой образец с применением ManyToMany и through
class Cinema(models.Model):
    movie = models.ManyToManyField(Movie, through='MoviesTimetable')    
    city
    name
    street
    phone
class Movie(models.Model):
    name
    length
    genre
    description
class MoviesTimetable(models.Model):
    movie = models.ForeignKey(Movie)
    cinema = models.ForeignKey(Cinemas)
    dates
    times

Мой вопрос не в коем случае не камень в ваш огород, а лишь вопрос ученика, желающего познать Дзен, потому как на своем нубском уровне я могу элементарно чего-то не знать. Планирую добавить кучу новых данных, в которых видится реализация связи многие-ко-многим и хочу узнать что правильней, как правильней, что лучше, почему? Сейчас функциональности мало, и переделывать легко, но потом такой роскоши уже не будет.

Спасибо.

Едит: Как я понимаю, ManyToMany в чистом виде используется в простой связке кинотеатры-фильмы. Параметр through добавляется, когда к фильмам нужно прилепить date и time (т.е. расписание сеансов). Исходя из того, что я читал, нужно уходить от схемы многие-ко-многим к один-ко-многим. Зачем тогда вообще этот вариант ManyToManyField и through, если можно делать так, как вы показали, с вариантом один-ко-многим? Может ManyToMany увеличивает производительность, так как фильтрация по схеме один-ко-многим в случаях “какие фильмы есть в данном кинотеатре” и “в каких кинотеатрах есть данный фильм” жрет больше ресурсов, чем вариант с ManyToMany?
PooH
TitanFighter
Как я понимаю, ManyToMany в чистом виде используется в простой связке кинотеатры-фильмы.

Вы все правильно понимаете, ManyToMany это просто для удобства когда у нас связь многие-ко-многим без каких-либо дополнительных атрибутов. Она создает промежуточную таблицу с двумя внешними ключами. Когда нужны атрибуты (в данном случае - сеансы), создаем промежуточную таблицу сами, ручками.

ЗЫ: Я бы на вашем месте почитал что-нибудь по теории реляционных баз данных, потому как в итоге все к ним сводится. Вот можно это посмотреть.

ЗЗЫ: Вы все правильно сделали, я просто джангу уже подзабыл.
TitanFighter
PooH
Прошу уделить мне еще немного времени.
1) До меня дошло, что вы имели в виду говоря
PooH
хотя сдается мне MoviesTimetable должен еще быть разложен на таблицы, но не зная постановки задачи не подскажу
Я добавил 2 класса, так как дата и время многократно повторяются, а нужно двигаться в сторону нормализации:
class Dates(models.Model):
    dates = models.DateField('Date')
class Times(models.Model):
    times = models.TimeField('Time')

и добавил ForeignKey в dates и times:
class Cinema(models.Model):
    movie = models.ManyToManyField(Movie, through='MoviesTimetable')    
    city
    name
    street
    phone
class Movie(models.Model):
    name
    length
    genre
    description
class MoviesTimetable(models.Model):
    movie = models.ForeignKey(Movie)
    cinema = models.ForeignKey(Cinemas)
# Добавил ForeignKey в dates и times
    dates = models.ForeignKey(Dates)
    times = models.ForeignKey(Times)
Изначально подумалось, что все верно. Позже, добавляя в модели много новых полей со связями m2m, у меня возник вопрос, правильно ли я сделал.

Из примера выше связь cinema-to-movie имеет тип m2m, так как в одном кинотеатре может быть кучу фильмов, и один фильм может быть в куче кинотеатров.
Переходим к MoviesTimetable. Как я понимаю, по аналогии можно так же сказать про dates и times:
dates - в один и тот же день могут быть разные сеансы, и один сеанс может быть в разные дни.
times - в одно и то же время могут быть разные сеансы, и один сеанс может быть в разное время.

Правильно ли будет сменить ForeignKey на ManyToMany для dates и times?

Продолжая, можно аналогично сказать и про movie и про cinema
- один и тот же фильм может присутствовать в разных сеансах, один сеанс может быть с разными фильмами (сеанс включает фильм, который может быть показан как в одном сеансе, так и десятках других и наоборот, сеанс может иметь как 1 фильм, так и 10 других)
- один и тот же кинотеатр имеет разные сеансы, один и тот же сеанс может быть в разных кинотеатрах

Но тут ничего менять не нужно, потому как эта таблица является промежуточной для внешних ключей. Вот если бы она была самостоятельной таблицей, тогда нужно было бы менять связи с 1m на m2m. Верно?

П.С. Прочитал уже много чего по связям, многократно перечитывая. Вроде теорию уловил, а как дело доходит до практики, в некоторых примерах я не могу однозначно ответить, какой тип связи нужно применять - 1m или m2m. Не хватает какого-то последнего шага, который бы сказал “это должно быть однозначно такой-то связью, по причине такой-то”. Я видимо путаю мягкое с пушистым и не могу уловить грани, когда однозначно использовать одно, а когда другое.

Благодарю.
ayb
Зачем дату и время выносить в отдельную сущность ?
PooH
TitanFighter
1) До меня дошло, что вы имели в виду говоря
PooH
хотя сдается мне MoviesTimetable должен еще быть разложен на таблицы, но не зная постановки задачи не подскажу
Я добавил 2 класса, так как дата и время многократно повторяются, а нужно двигаться в сторону нормализации:

Зря вы их добавили дата и время это уже базовые типы данных, не надо их отдельно выносить в таблицы. Я имел ввиду, что надо смотреть что вы дальше будете делать с расписанием, какие действия, если просто хранить список сеансов, и искать где, что и когда идет, то вполне достаточно:
class MoviesTimetable(models.Model):
    movie = models.ForeignKey(Movie)
    cinema = models.ForeignKey(Cinemas)
    datetime = models.DateTimeField()
причем сочетание (movie_id, cinema_id, datetime) - естественный ключ таблицы, но в джанге, помнится, они плохо поддерживаются, так что будет введен искусственный id, но можно сделать по ним уникальный индекс.

Если у вас для сеансов какие-то сценарии использования, излагайте, рассмотрим схему для них. Нету одной единственно правильной схемы данных, все зависит от задачи.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB