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)
Вернуться на верх