Home > other >  Resolving a ForeignKey field pointing to the model it belongs to
Resolving a ForeignKey field pointing to the model it belongs to

Time:09-07

I have a model class in Django which has a ForeignKey referencing the model it actually belongs to:

class Foo(models.Model):
    name = models.CharField(max_length=256, verbose_name="Name")
    #... some other fields
    bar = models.ForeignKey(
        "self", on_delete=models.CASCADE, null=True, blank=True
    )
    def __str__(self):
        return self.name

I want to add a custom method in that class which resolves, on the fly, the name in a new field, e.g. bar_resolved when instantiating it in a QuerySet in a view:

from .models import Foo

foo = Foo.objects.all()
# do stuff

I've tried this:

class Foo(models.Model):
    name = models.CharField(max_length=256, verbose_name="Name")
    #... some other fields
    bar = models.ForeignKey(
        "self", on_delete=models.CASCADE, null=True, blank=True
    )
    # preparing the resolved bar field which should contain the 'name' value corresponding to the id:
    bar_resolved = models.CharField(
        max_length=256,
        verbose_name="Bar name resolved",
        null=True
    )

    def __str__(self):
        return self.name

    def resolve(self):
        if self.bar:
            self.bar_resolved = self.bar.name
        return super(Foo, self).resolve()

Then in my view:

from .models import Foo

foo = Foo.objects.all()
foo.resolve()

but it raises: 'QuerySet' object has no attribute 'resolve'

How could I achieve that? and do I need to hard code a 'resolved' field in my model for that (I think it's overkill to do so)?

CodePudding user response:

I do not understand why would you have a Foreing key referencing self in the database.

Instead of using resolve, you could probably do it on the save long before - i.e. when setting value of "bar"

Another idea that comes to mind is setting it in the __ init__ method of the model link to Stack

hope this helps.

def save(self, force_insert: bool = False, force_update: bool = False) -> None:
    if self.field is None:
        self.field = "value"
    # and so on...
    return super().save(force_insert, force_update)

CodePudding user response:

One way is to annotate..[Django-doc] your queryset using F expressions..[Django-doc] with bar's name field:

from django.db.models import F

foos = Foo.objects.annotate(bar_resolved=F("bar__name")).all()
for foo in foos:
    print(foo.bar_resolved)
  • Related