Промежуточная таблица Django с полем «многие ко многим» вместо внешнего ключа?

У меня есть 2 модели в моем приложении:

  • Пользователь
  • Башня

Моя цель — связать множество башен с пользователем с помощью промежуточной таблицы, потому что мне нужно добавить период времени.

Итак, у меня есть что-то вроде этого в моих моделях:

class Tower(models.Model):
    name = models.CharField(max_length=128)

class User(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField('Tower', through='Dates')

class Dates(models.Model):
    user = models.ForeignKey('User', on_delete=models.CASCADE)
    tower = models.ForeignKey('Tower', on_delete=models.CASCADE)
    begin_date = models.DateTimeField()
    end_date = models.DateTimeField()

Но моя цель состояла в том, чтобы сделать полевую башню в классе Dates для многих-ко-многим следующим образом:

tower = models.ManyToManyField('Tower', blank=True)

Так что я могу связать много башен с пользователем в определенный период. Но, к сожалению, django говорит, что мне нужно использовать forgeignkey для классов Tower и User.

Есть ли какое-нибудь решение для этого? Применить напрямую поле «многие ко многим» в промежуточной таблице? Или я должен создать новый класс, своего рода GroupTower, у которого есть поле «многие ко многим» для класса Tower? Что-то вроде этого:

class Tower(models.Model):
    name = models.CharField(max_length=128)

class GroupTower(models.Model):
    tower = models.ManyToManyField('Tower', blank=True)

class User(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField('Tower', through='Dates')

class Dates(models.Model):
    user = models.ForeignKey('User', on_delete=models.CASCADE)
    tower = models.ForeignKey('GroupTower', on_delete=models.CASCADE)
    begin_date = models.DateTimeField()
    end_date = models.DateTimeField()

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


person Pedro Faria    schedule 28.03.2019    source источник


Ответы (1)


Существует так много способов, которыми вы можете спроектировать базу данных.

Пример:

class Tower(models.Model):
    name = models.CharField(max_length=128)

class User(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField('UserTower')

class UserTower(models.Model):
    user = models.ForeignKey('User', on_delete=models.CASCADE)
    tower = models.ManyToManyField('Tower')
    begin_date = models.DateTimeField()
    end_date = models.DateTimeField()

В этом дизайне вы можете добавить пользователю много экземпляров UserTower, и у каждого экземпляра UserTower может быть много башен.

Теперь, если вы запросите участников для пользователя:

User.objects.get(pk=1).members.all()

У вас будут участники, сгруппированные по периодам времени, если вы сохранили их таким образом, но вам потребуется написать некоторые коды, чтобы избежать дублирования для begin_date, begin_date и user.

А теперь, если вам нужны башни:

user_members = User.objects.get(pk=1).members.all().values_list('tower', flat=True)
towers = Tower.objects.filter(pk__in=user_members).distinct()

Этот дизайн подходит только в том случае, если вы действительно не хотите дубликатов с одинаковыми begin_date и begin_date, для которых я не могу найти причину.

Вы по-прежнему можете использовать все функции, если добавите несколько экземпляров с одинаковыми begin_date, begin_date и user, но разными tower.

person Navid Zarepak    schedule 28.03.2019
comment
Это именно то, чего я ожидал. Большое вам спасибо за вашу помощь. - person Pedro Faria; 28.03.2019