Django - Внешний ключ по неуникальному полю
Рассмотрите две модели:
from django.db import models
class File(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=256, unique=False)
class Comment(models.Model):
id = models.AutoField(primary_key=True)
file_name = models.ForeignKey(to=File, on_delete=models.CASCADE, to_field="name")
text = models.CharField(max_length=1000, unique=False)
и некоторые данные:
f1 = File.objects.create(id=1, name="a.xlsx")
f2 = File.objects.create(id=2, name="a.xlsx")
f3 = File.objects.create(id=3, name="b.xlsx")
c1 = Comment.objects.create(id=1, file_name="a.xlsx", comment="Comment 1")
c2 = Comment.objects.create(id=2, file_name="a.xlsx", comment="Comment 2")
c3 = Comment.objects.create(id=2, file_name="b.xlsx", comment="Comment 3")
Из вышесказанного:
f1
связан с двумя комментариями:c1
иc2
.
f2
также связан с двумя комментариями:c1
иc2
.
f3
связан с одним комментарием:c3
Но согласно этой связи, ForeignKey.to_field
требует, чтобы File.name
была уникальной, но это не так. Как я могу добиться такого отношения без создания промежуточных таблиц ?
В идеале:
f1.comments
вернетc1
иc2
c1.files
вернетf1
иf2
и т.д
class File(models.Model):
name = models.CharField(max_length=256)
class Comment(models.Model):
file_object = models.ForeignKey(File, on_delete=models.CASCADE)
text = models.CharField(max_length=1000)
c1 = Comment.objects.create(file_object=f1, comment="Comment 1")
c2 = Comment.objects.create(file_object=f2, comment="Comment 2")
ID или первичные ключи будут добавлены автоматически и, конечно, должны быть уникальными. Также по умолчанию в других местах используется значение unique=False. Как вы можете видеть ниже, я привел несколько примеров и постарался дать понять, что мы имеем дело с объектами:
my_file_objects = File.objects.filter(name="a.xlsx").all()
for file_object in my_file_objects:
print(file_object.comment.text)
my_comment_objects = Comment.objects.filter(file_object__name="a.xlsx").all()
for comment_object in my_comment_objects:
print(comment_object.text)
print(comment_object.file_object.pk)
Django не позволяет ссылаться на неуникальное поле во внешнем ключе, что в любом случае является плохой практикой. Однако, если вы все же хотите ссылаться на внешнюю таблицу с неуникальным полем, то вместо создания внешнего ключа вы можете рассматривать file_name как обычный атрибут и затем переопределить функции delete и save моделей File и Comment соответствующим образом.
Например, если вы хотите добиться функциональности on_delete=CASCADE для внешнего ключа, то вы можете переопределить функцию delete модели File и удалить связанные комментарии в этой функции. Вот как вы можете объявить свои модели
from django.db import models
class File(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=256, unique=False)
def delete(self, *args, **kwargs):
# you might want to add some conditions here based on your needs
# before deleting related comments
Comment.objects.filter(f_name=self.name).delete()
# deleting file object itself
super(File, self).delete(*args, **kwargs)
class Comment(models.Model):
id = models.AutoField(primary_key=True)
f_name = models.CharField(max_length=100)
text = models.CharField(max_length=1000, unique=False)