I have a simple Django (4.1/Python3.9) table with textual and numerical fields.
In this table, four of these are ForeignKeys:
1 is referencing itself
1 is referencing an other table through a OneToMany relationship (aka simple ForeignKey)
2 are referencing 2 other tables through a ManyToMany relationship
Django is kind enough, when using the model._meta.get_fields() method, to retrieve all related fields along with the usual fields of the model. And that's what I want.
Within the admin page of that model, I can see all these related fields as "resolved", e.g. their name is displayed instead of their ids, this is because of the usual __str__(self) method we can use in the model classes:
from django.db import models
class Foo(models.Model):
foo_name = model.CharFields(
max_length=100, unique=True, verbose_name="Foo name"
)
# here are some other usual data fields
self_relation = models.ForeignKey(
"self", on_delete=models.CASCADE, null=True, blank=True
)
bar = models.ForeignKey(
"Bar", on_delete=models.CASCADE, null=True, blank=True
)
baz = models.ManyToManyField(Baz)
qux = models.ManyToManyField(Qux)
def __str__(self):
return self.name
class Bar(models.Model):
bar_name = model.CharFields(
max_length=100, unique=True, verbose_name="Bar name"
)
# ...
def __str__(self):
return self.name
class Baz(models.Model):
baz_name = model.CharFields(
max_length=100, unique=True, verbose_name="Baz name"
)
# ...
def __str__(self):
return self.name
class Qux(models.Model):
qux_name = model.CharFields(
max_length=100, unique=True, verbose_name="Qux name"
)
# ...
def __str__(self):
return self.name
But in an attempt to export this table data to a text file using a dedicated route, they are not auto-magically resolved; it's only the ids of the related fields which are exported. Which makes this export hard to read, unless one perfectly knows the mapping between the ids and the names on all related models (this is impossible from a human perspective once the models become a little complicated).
Therefore, in the views.py file, I do have a simple function to export to a comma separated text file as follow:
from django.http import HttpResponse
import csv
from .models import Foo, Bar, Baz, Qux
def to_comma_separated_file(model, filename):
response = HttpResponse()
response['Content-Disposition'] = f'attachment; filename={filename}.csv'
writer = csv.writer(response)
model_fields = [
f for f in model._meta.get_fields(
include_parents=True,
include_hidden=False
)
]
model_fields_names = [f.name for f in model_fields]
# write the headers:
writer.writerow(model_fields_names)
Then, what I noticed is that I need to loop over the columns of the table to check if a field is actually a relation, and then to loop over the rows to actually replace them with their names before writing them in the file.
So, this piece of code is following, in that same views.py file:
columns = dict()
# Loop over the columns to check if a field is a relation or not:
for f in model_fields:
columns[f.name] = f.is_relation # this tells me if a field is a foreignkey
# Loop over the rows to replace id by their names in the relation fields:
for row in objects.values(*model_fields_names): # cannot use model_fields here.
for k,v in row.items():
if columns[k]:
new_value = model.objects.get() # <- put intelligence here.
row[k] = new_value
# write each row of data to the file
writer.writerow(row)
My current problem lies where the # <- put intelligence here. is.
I cannot figure out a way to get the names corresponding to the name field of related tables so that I would be able to export theses names instead of their ids in my text file.
Do you see how I can achieve that?
Please note that standard users of the app should be able to make use of this export functionality, not only admins. That's why it was designed in such way for the moment and not as a dedicated action (e.g. using django-import-export) in the admin page.