Связывать модели Django вместе с помощью GenericKey?
У меня есть следующие модели:
class Team(models.Model):
users = models.ManyToManyField("User")
class User(AbstractUser):
...
class Subscription(models.Model):
team = models.ForeignKey("Team", on_delete=models.CASCADE)
name = models.CharField(max_length=64)
class Package(models.Model):
name = models.CharField(max_length=64) # packageA, packageB
max_activation_number = models.PositiveIntegerField(default=1)
class Activation(models.Model):
subscription = models.ForeignKey("Subscription", on_delete=models.CASCADE)
package = models.ForeignKey("Package", on_delete=models.CASCADE)
created = models.DatetimeField()
class PackageA(models.Model):
...
class PackageB(models.Model):
...
У команды одна подписка, и она может активировать один или несколько пакетов, причем один и тот же пакет может быть активирован более одного раза. (количество раз задается параметром "max_ativation_number")
Пример:
У команды есть подписка под названием Suite и доступными пакетами являются: EmailAccount и Calendar. Команда выбирает активировать 3 EmailAccount и 2 Calendar (пакеты не связаны друг с другом)
По этой причине команда может активировать один и тот же пакет большее количество раз.
Для каждой активации мне нужно создать новый экземпляр на PackageA или PackageB (это зависит от выбора команды), а затем я должен как-то "привязать" его к этому экземпляру.
Должен ли я использовать поле GenericKey внутри модели Activation? Мне нужно не только имя выбранного пакета, но и выяснить, какой экземпляр.
Да, я также думаю, что GenericForeignKey - очень хорошая идея в этом случае, так что мы можем динамически связать модель активации с PackageA или PackageB.
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
class Activation(models.Model):
subscription = models.ForeignKey("Subscription", on_delete=models.CASCADE)
package = models.ForeignKey("Package", on_delete=models.CASCADE)
created = models.DatetimeField()
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
Вот пример того, как можно создать экземпляр Activation:
subscription = Subscription.objects.get(...)
package = Package.objects.get(...)
content_type = ContentType.objects.get_for_model(PackageA)
package_a = PackageA.objects.get(...)
activation = Activation.objects.create(
subscription=subscription,
package=package,
content_type=content_type,
object_id=package_a.id,
created=datetime.now()
)
Это можно решить несколькими способами, поскольку вы хотите создать новый экземпляр пакета. Одно из решений - использовать сигналы Django для создания нового экземпляра пакетов с помощью post_save сигнала from django.db.models.signals import post_save
.
from django.db.models.signals import post_save
@receiver(post_save, sender= Activation)
def post_activation_save(sender, instance, **kwargs):
if kwargs.get('created'):
# create new package instance
....
Насколько я понимаю, вы пытаетесь создать отношение "многие-ко-многим" https://docs.djangoproject.com/en/dev/topics/db/models/#extra-fields-on-many-to-many-relationships:
class Team(models.Model):
users = models.ManyToManyField("User")
class User(AbstractUser):
...
class Subscription(models.Model):
team = models.ForeignKey("Team", on_delete=models.CASCADE)
name = models.CharField(max_length=64)
packages = models.ManyToManyField(Package, through=Activation)
class Package(models.Model):
name = models.CharField(max_length=64) # packageA, packageB
class Activation(models.Model):
subscription = models.ForeignKey("Subscription", on_delete=models.CASCADE)
package = models.ForeignKey("Package", on_delete=models.CASCADE)
max_activation_number = models.PositiveIntegerField(default=1)
created = models.DatetimeField()
Итак, для того чтобы создать подписку Suite
с 3 EmailAccount и 2 Calendar
suite_subscription = Subscription.objects.create(name='Suite', team=team_1)
email_package = Package.objects.create(name='EmailAccount')
calendar_package = Package.objects.create(name='Calendar')
suite_subscription.activation_set.create(package=email_package, max_activation_number=3)
suite_subscription.activation_set.create(package=calendar_package, max_activation_number=2)
Теперь у вас есть suite_subscription
с 3 максимальными пакетами электронной почты и 2 максимальными пакетами календаря. Аналогично этому, вы можете создавать дополнительные пакеты и подписки...
И вы можете добавить столько пользовательских атрибутов, сколько захотите, к этой "поворотной" таблице Активации.