Home > database >  Django - Foreign Key on non-unique field
Django - Foreign Key on non-unique field

Time:11-21

Consider two models:

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)

and some data:

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")

From the above:

  • f1 is associated to two comments: c1 and c2
  • f2 is also associated to two comments: c1 and c2
  • f3 is associated to one comment: c3

But according to this, ForeignKey.to_field requires File.name to be unique but that's not the case. How can I achieve this relationship without creating any intermediary tables?

So ideally:

  • f1.comments would return c1 and c2
  • c1.files would return f1 and f2 etc

CodePudding user response:

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")

The ID's or primary keys will be added automatically and will of course need to be unique. Also the default is that unique=False elsewhere. As you can see below I have provided some examples, and tried to make it clear that we are dealing with objects:

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) 

CodePudding user response:

Django does not allow referring to non-unique field in foreign key, which is a bad practice anyway. However, if you still want to refer to the foreign table with non-unique field then instead of creating a foreign key you can treat file_name as a normal attribute and then override delete and save functions of File and Comment models accordingly.

For example, if you want to achieve on_delete=CASCADE functionality of foreign key then you can override delete function of the File model and delete related comments in this function. Here is how you can declare your models

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)
  • Related