Mauricio
Mauricio

Reputation: 3089

Django: Get model fields recursively (ForeignKeys and it's fields) as strings into a 1 level list

I building a project in Django that needs to list all fields from a given model as options of a html select tag. I wrote a recursive function that reads the model and returns a list with all fields and subfields. Like this:

def get_fields_true_hierarchy(model, list_fields = []):
    fields_obj = model._meta.fields
    for field_obj in fields_obj:
        if field_obj.rel:
            dict_fields = {field_obj.attname: []}
            list_fields.append(dict_fields)
            get_fields_true_hierarchy(field_obj.rel.to, dict_fields[field_obj.attname])
        else:
            list_fields.append(field_obj.attname)
    return list_fields

It returns an object such as the following:

fiedls = [
    'id', 
    'title', 
    'number', 
    'start_date', 
    'finish_date', 
    {'status_id':
        ['id', 
        'name']
    }, 
    'postal_code', 
    {'requestor_id': 
        ['id', 
        {'user_id': 
            ['id', 
            'password', 
            'last_login', 
            'is_superuser', 
            'username', 
            'first_name', 
            'last_name', 
            'email', 
            'is_staff', 
            'is_active', '
            date_joined']
        }, 
        'name', 
        'phone', 
        'email',  
        'contact_name', 
        'contact_email']
    }, 
    {'reason_id': 
        ['id', 
        'description']
    }, 
    'details', 
    {'group_id': 
        ['id', 
        'description']
    }, 
    {'subgroup_id': 
        ['id', 
        'description']
    }, 
    {'manager_id': 
        ['id', 
        'password', 
        'last_login', 
        'is_superuser', 
        'username', 
        'first_name', 
        'last_name', 
        'email', 
        'is_staff', 
        'is_active', 
        'date_joined']
    }, 
    'datetime_subscription', 
    'allowed', 
    'data_def'
]

However, I need to get a single level list which would look like this:

fiedls = [
    'id', 
    'title', 
    'number', 
    'start_date', 
    'finish_date', 
    'status_id',
    'status_id.id', 
    'status_id.name', 
    'postal_code', 
    'requestor_id',
    'requestor_id.id', 
    'requestor_id.user_id',
    'requestor_id.user_id.id', 
    'requestor_id.user_id.password', 
    'requestor_id.user_id.last_login', 
    'requestor_id.user_id.is_superuser',
    'requestor_id.user_id.username', 
    'requestor_id.user_id.first_name', 
    'requestor_id.user_id.last_name', 
    'requestor_id.user_id.email', 
    'requestor_id.user_id.is_staff', 
    'requestor_id.user_id.is_active',
    'requestor_id.user_id.date_joined',
    'requestor_id.name', 
    'requestor_id.phone', 
    'requestor_id.email',  
    'requestor_id.contact_name', 
    'requestor_id.contact_email',
    'reason_id',
    'reason_id.id', 
    'reason_id.description',
    'details',
    'group_id', 
    'group_id.id', 
    'group_id.description',
    'subgroup_id',
    'subgroup_id.id', 
    'subgroup_id.description',
    'manager_id', 
    'manager_id.id',
    'manager_id.password', 
    'manager_id.last_login', 
    'manager_id.is_superuser', 
    'manager_id.username', 
    'manager_id.first_name', 
    'manager_id.last_name', 
    'manager_id.email', 
    'manager_id.is_staff', 
    'manager_id.is_active', 
    'manager_id.date_joined',
    'datetime_subscription', 
    'allowed',
    'data_def'
]

I couldn't write a recursive function to return such result. Do you have any ideas on how to perform this task? I'm stuck on this function below. But it does not behave as expected.

PS.: list_fields argument will come from the get_fields_true_hierarchy() function.

def get_fields_fake_hierarchy(list_fields, fake_hierarchy = [], parent_str = "", reset=True):
    for field in list_fields:
        if reset:
            parent_str = ""
        if isinstance(field, dict):
            for key, value in field.iteritems():
                fake_hierarchy.append(key)
                parent_str += "."+key
                get_fields_fake_hierarchy(value, fake_hierarchy, parent_str, reset=False)
        else:
            fake_hierarchy.append("%s.%s"%(parent_str, field))
    fake_hierarchy = [i[1:] if i[0]=='.' else i for i in fake_hierarchy]
    return fake_hierarchy

Upvotes: 1

Views: 823

Answers (1)

bruno desthuilliers
bruno desthuilliers

Reputation: 77912

Use a recursive generator instead, and prefix the names on the fly:

def do_prefix(name, prefix):
    if prefix:
        return "%s.%s" % (prefix, name)
    return name

def get_fields_flat(model):
    return [name for name in iter_fields(model)]

def iter_fields(model, prefix=None):
    fields = model._meta.fields
    for field in fields:
        name = do_prefix(field.attname, prefix)
        yield  name
        if field.rel:
            rel = field.rel.to
            for f in iter_fields(rel, name):
                yield f

Upvotes: 4

Related Questions