Obtaining values from a foreign key Python model

Let's say I have these models/classes:

class User(models.Model):
    id = models.AutoField. . .
    group = models.ForeignKey(
        . . .
    . . .

class Group(models.Model):
    id = models.AutoField. . .
    name = models.CharField. . .
    . . .

In a custom logging function called when there is a change being made, I do this:

obj = # object/model being updated; in this case: User
old_values = {}
new_values = {}
for i in range(len(form.changed_data)):
    vname = obj._meta.get_field(form.changed_data[i]).verbose_name

        { vname: form[form.changed_data[i]].initial }
        { vname: form.cleaned_data[form.changed_data[i]] }

That leads to this output:

old_values = {'Group': 2}

new_values = {'Group': <Group: New Group Name>}

Looks like form.initial uses the id while form.cleaned_data uses some kind of unsightly object name format.

Neither are desired. I want the output to look like this:

old_values = {'Group': 'Old Group Name'}

new_values = {'Group': 'New Group Name'}

How do I do this? I cannot explicitly import the model name and use it. User and Group are merely two of dozens of models that must be treated non-explicitly in this generic logging function.

I've tried apps.get_model(), get_object_or_404(), and other methods, but nothing has been working for me so far.

The value of Foreign Key in the default form is the PK of the model.

# old_values = {'Group': 2}
group = Group.objects.get(pk=old_values['Group'])
new_values = {'Group': group.name}

I still do not know how to do what I tried above, but I solved my problem another way.

To start, each object has a meta variable that references its object class. Using that, we can obtain a list of fields for any given object.

for field in obj._meta.get_fields():
    value = getattr(obj, field.name, None)

You can check each field for whatever specifications you have, like if they should be ignored like ID fields, should be censored like password fields, or is a ManyToManyField etc.

if isinstance(field, ManyToManyField):
    values = []
    for obj in value.all():

Depending on what the value is, you may want repr(value), str(value), str(object), or just plain value.

What I did was create a function get_values(object) that utilized the above to return a standardized list of stuff. Sending that function the objects during the logging process worked just fine.

Back to Top