__str__ returned non-string (type Node)

I have the following classes in my models.py files

class Node(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
class Edge(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    source_node = models.ForeignKey(Node, on_delete=models.CASCADE, related_name='edge_sources', related_query_name='edge_source')
    target_node = models.ForeignKey(Node, on_delete=models.CASCADE, related_name='edge_targets', related_query_name='edge_target')

When I add an edge using Django’s admin interface I get the following error:

TypeError at /admin/edges/edge/add/
__str__ returned non-string (type Node)
Request Method:	POST
Request URL:	http://localhost:8000/admin/edges/edge/add/
Django Version:	4.0.4
Exception Type:	TypeError
Exception Value:	__str__ returned non-string (type Node)
Exception Location: /Users/me/Documents/project/venv/lib/python3.10/site-packages/django/contrib/admin/options.py, line 921, in log_addition
Python Executable:	/Users/me/Documents/project/venv/bin/python3
Python Version:	3.10.4

It’s only the Edge class that’s causing trouble. For instance, I have a class Label:

class Label(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    node = models.ForeignKey(Node, on_delete=models.CASCADE, related_name='labels', related_query_name='label')
    name = models.CharField(max_length=64)

I can add labels using Django’s admin, without getting errors. I wonder whether referencing class Node twice from the class Edge is causing the problem. If so, is there a solution.

Kind regards,

Johanna

1 Like

The dual references to Node are not the issue.

Do you have a custom ModelAdmin class for Edge? (What does your ModelAdmin class for Edge look like?)

Do you have anything else in the Edge model? (Any model methods?) Do you have a custom __str__ method defined?

1 Like

Note: That last question asking about whether you have a custom __str__ method defined applies to both Edge and Node. If you have a custom __str__ for either of those models, please post it.

Hi Ken,

Thanks, for your reply.

Yes, I do have a custom __str__ method defined. When I remove the __str__ method the problem is solved.

This is the entire class definition.

class Edge(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    source_node = models.ForeignKey(Node, on_delete=models.CASCADE, related_name='edge_sources', related_query_name='edge_source')
    target_node = models.ForeignKey(Node, on_delete=models.CASCADE, related_name='edge_targets', related_query_name='edge_target')
    label = models.ForeignKey(Label, on_delete=models.CASCADE, related_name='edge_labels', related_query_name='edge_label')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class meta:
        constraints = [
            models.UniqueConstraint(fields=['source_node', 'target_node', 'label'], name='unique_edge')
        ]

    def __str__(self):
        return self.source_node

    def get_absolute_url(self):
        return reverse('edge-list')

Could you please explain why the __str__ method causes the error, is there a way to define it correctly in this case.

Kind regards,

Johanna

That is the problem.

Look at what the error message is telling you:

Your __str__ method is returning an object of type Node, not a string. That’s because self.source_node is a reference to an object of type Node.

What do you want __str__ to return?

The __str__ method must return a string. The contents of that string are entirely up to you.

Now the error message makes sense, actually it’s telling me all I need to know, I just didn’t link it to the __str__ method.

I’m not even sure I need the __str__ method to return anything. The first Django tutorials I did used Django’s interactive shell. Because the output to the shell is not very descriptive I was advised to add the __str__ method to the model classes.

Since I’m not using Django’s interactive shell anymore, I might as well remove them from the model classes. At least the one’s that don’t have a sensible string representation.

Kind regards,

Johanna

Side note: The Django interactive shell is an exceedingly useful tool. I generally have an instance open anytime I’m working on a project.

I started working with Django somewhere in April, I can imagine that in the near future I’ll be using the Django interactive shell more an more.

At the moment I spend most of my time exploring Django by going through the documentation, and figuring out the things I need for the project I’m working on.