Looking up Django model fields from strings

2015-02-16 python django

I recently needed to take Django field lookup/query strings and find out what model field they were referencing. For example, imagine we have the following models:

class Business(models.Model):

    name = models.CharField(max_length=255)
    business_type = models.IntegerField(choices=zip(range(5), range(5)), default=1)

class Department(models.Model):

    name = models.CharField(max_length=255)
    business = models.ForeignKey(Business)

class Staff(models.Model):

    first_name = models.CharField(max_length=255)
    last_name = models.CharField(max_length=255)

    department = models.ForeignKey(Department)

I needed a function that would take a string like department__business__business_type and return the Business.type <django.db.models.fields.IntegerField: business_type> field (I needed to access the choices attribute of fields).

Here's a short function which will do that sort of lookup:

def find_field(cls, lookup):
    """Take a root class and a field lookup string
    and return the model field if it exists or raise
    a django.db.models.fields.FieldDoesNotExist if the
    field is not found."""

    lookups = list(reversed(lookup.split("__")))

    field = None

    while lookups:

        f = lookups.pop()

        # will raise FieldDoesNotExist exception if not found
        field = cls._meta.get_field(f)

        try:
            cls = field.rel.to
        except AttributeError:
            if lookups:
                # not all lookup fields were used
                # must be an incorrect lookup
                raise django.db.models.fields.FieldDoesNotExist(lookup)

    return field

# use like:
>> find_field(Staff, "department__business__business_type")
<django.db.models.fields.IntegerField: business_type>
>>
>> find_field(Staff, "department__business")
<django.db.models.fields.related.ForeignKey: business>
>>
>> find_field(Staff, "department__foo")
FieldDoesNotExist: Department has no field named 'foo'
>>
comments powered by Disqus