Search code examples
djangodjango-modelstypesfield

How to find out the Python representation of Django fields?


For example I have this code:

class SomeModel(models.Model):
    instance_name = models.UnknownField(max_length=255)
    instance_id = models.IntegerField()

I want to have the model name (SomeModel) and field name (for ex. instance_name) as an input, and the, and the output will be the python representation of that field (for ex.: srt, int, float...)

So it will be something like this:

def find_out_the_field_python_type(model: Model, field_name: str) -> type:
    # For example model = SomeModel, field_name = instance_id
    # return int
    return

I have tried this:

instance_id_field = SomeModel._meta.get_field('instance_id')
# Determine the Python representation of the field
python_representation = instance_id_field.get_internal_type()
print(python_representation)
# returns 'IntegerField'

But the problem that it returns not the python representation, but the type of Django field.


Solution

  • I don't think what you want is really do-able. The Source code is available. It appears that the Python type is not a meta variable, but is hard-coded into the to_python method of the field.

    The model is an interface between Python and a database. Not all databases directly support all Django field types. Where they don't, Django performs an encoding to store and a decoding to initialize a model instance.

    This, for example, is the to_python method for a DecimalField from the above source:

    def to_python(self, value):
        if value is None:
            return value
        if isinstance(value, float):
            return self.context.create_decimal_from_float(value)
        try:
            return decimal.Decimal(value)
        except (decimal.InvalidOperation, TypeError, ValueError):
            raise exceptions.ValidationError(
                self.error_messages['invalid'],
                code='invalid',
                params={'value': value},
            )
    

    If you were determined you could run an iteration feeding plausible values into the to_python method until you get a successful return. Before doing this you'd want to check the field's concrete (True) and is_relation (False) for appropriate other handling.

    instance_id_field = SomeModel._meta.get_field('instance_id')
    if instance_id_field.is_relation:
        # ??
    if not instance.id_field.is_concrete:
        # ??
    for thing in (
        '0',         # should work for Charfield and numeric fields and Boolean
        datetime.datetime( 2023,1,1),  # for all date or datetime,
        0.0,                           # for above DecimalField,
        ...                            # work through the above-linked source for other probe values!
    ):
        try:
            x = instance_id_field.to_python( thing)
            return type(x)
        except Exception:
            pass
    
    raise TypeError( 
        f'Unable to determine Python type of {instance_id_field}')
    

    But this is really horribly hacky.