How can I introspect properties and model fields in Django?

10,799

Solution 1

If you strictly want just the model fields and properties (those declared using property) then:

def get_fields_and_properties(model, instance):
    field_names = [f.name for f in model._meta.fields]
    property_names = [name for name in dir(model) if isinstance(getattr(model, name), property)]
    return dict((name, getattr(instance, name)) for name in field_names + property_names)

instance = MyModel()
print get_fields_and_properties(MyModel, instance)

The only bit that's extra here is running through the class to find the fields that correspond to property descriptors. Accessing them via the class gives the descriptor, whereas via the instance it gives you the values.

Solution 2

The trouble is you say you only want fields, but then complicate things by throwing properties into the mix. There isn't really any easy way in Python of distinguishing between a property and any other random method. This isn't anything to do with Django: it's simply that a property is just a method that is accessed via a descriptor. Because any number of things in the class will also be descriptors, you'll have trouble distinguishing between them.

Is there any reason why you can't define a list at the class level that contains all the properties you want, then just call getattr on each of the elements?

Share:
10,799
shreddd
Author by

shreddd

Updated on June 18, 2022

Comments

  • shreddd
    shreddd almost 2 years

    I am trying to get a list of all existing model fields and properties for a given object. Is there a clean way to instrospect an object so that I can get a dict of fields and properties.

    class MyModel(Model)
        url = models.TextField()
    
        def _get_location(self):
            return "%s/jobs/%d"%(url, self.id)
    
        location = property(_get_location)
    

    What I want is something that returns a dict that looks like this:

    {
      'id' : 1,
      'url':'http://foo',
      'location' : 'http://foo/jobs/1'
    }   
    

    I can use model._meta.fields to get the model fields, but this doesn't give me things that are properties but not real DB fields.

  • shreddd
    shreddd over 13 years
    Doesn't quite work for Django models. There are plenty of other methods and variables that are available to the object (inherited from the base Django Model class) but are not strictly Django fields or properties. I need to be able to distinguish between properties/fields and other stuff.
  • shreddd
    shreddd over 13 years
    To clarify - I am referring to the "property" builtin (adam.gomaa.us/blog/2008/aug/11/the-python-property-builtin) - not just a generic attribute of a python object.
  • the_drow
    the_drow over 13 years
    @shreddd: Compare what you've got with model._meta.fields to distinguish between db fields and properties.
  • shreddd
    shreddd over 13 years
    Thanks - this is exactly what I needed. I was trying to look for the things of type "property" inside the instance, but I should have been looking for them in the Class.
  • shreddd
    shreddd over 13 years
    This gets me everything inside the dict, but not things that are declared using property(). Looks like I need to go and pull this from the Model class directly. See John Montgomery's solution above.
  • shreddd
    shreddd over 13 years
    Sorry - I think you misunderstood the question. See the accepted solution for the answer I was looking for. Turns out one does not know if something is of type "property" in the instance - you need to check the class.