Why is model._meta.get_fields() returning unexpected relationship column names, and can this be prevented?

Question:

Imagine I have some models as below:

class User(AbstractUser):
    pass

class Medium(models.Model):
    researcher = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True, related_name="medium_researcher")
    old_medium_name = models.CharField(max_length=20, null=True, blank=True)

class Uptake(models.Model):
    material_quality = models.CharField(max_length=20, null=True, blank=True)
    medium = models.ForeignKey(Medium, on_delete=models.CASCADE, blank=True, null=True, related_name="uptake_medium")

Now I have a function to return all column names to generate some overview in my HTML as such:

from database.models import Medium
MODEL_HEADERS=[f.name for f in Medium._meta.get_fields()]
MODEL_HEADERS
['uptake_medium', 'id', 'researcher', 'old_medium_name']

Why does this return uptake_medium? As this is a ForeignKey relation set within the Uptake model, it should only be present within the Uptake model right? When I review the admin models this column does not show up, neither in the db.sqlite3 model when checking Uptake, so it seems to be sort of hidden, and only show up when requested with _meta. The relationship seems to be correct… This is causing a lot of problems with my code, and it would be great if only the ‘non-meta’ columns only could be returned. How should I approach?

Asked By: Rivered

||

Answers:

Why does this return uptake_medium? As this is a ForeignKey relation set within the Uptake model, it should only be present within the Uptake model right?

You can access the relation in reverse, for example:

my_medium.uptake_medium.all()

to obtain all Updates related to the Medium instance named medium.

You can also filter on that field, for example:

Medium.objects.filter(uptake_medium__material_quantity=42)

hence it is accessible just like any field.

You can filter with:

from django.db.models.fields.reverse_related import ManyToOneRel

[f.name for f in Medium._meta.get_fields() if not isinstance(f, ManyToOneRel)]
Answered By: Willem Van Onsem