Skip to content

Abstract Models break typomatic #66

@daninus14

Description

@daninus14

Problem

Whenever there are abstract models in a project, typomatic breaks. This is a bug. There should either be an interface generated, or it should be skipped. It should not break the program...

/python3.13/site-packages/rest_framework/serializers.py", line 1065, in get_fields
    raise ValueError(
        'Cannot use ModelSerializer with Abstract Models.'
    )
ValueError: Cannot use ModelSerializer with Abstract Models.

Solution By Skipping

We can just check the model for which the interface is to be generated if modelName.__meta__.abstract == True and that can allow for skipping.

Solution To Make It Work With Abstract Classes

Seemingly, though I have not looked at the code, it seems the issue is that typomatic is instantiating the class to get its fields and it fails because the class is abstract. Here's an LLM output on how to get the fields of an abstract class including all its superclasses

from django.db import models

class BaseAbstract(models.Model):
    base_field = models.IntegerField()

    class Meta:
        abstract = True

class AnotherBaseAbstract(models.Model):
    another_base_field = models.CharField(max_length=100)

    class Meta:
        abstract = True

class MyAbstract(BaseAbstract, AnotherBaseAbstract):
    my_field = models.CharField(max_length=50)

    class Meta:
        abstract = True

def get_abstract_model_fields(model_class):
    """
    Gets all fields of an abstract Django model class, including inherited fields.
    """
    fields = []
    for base in model_class.__bases__:
        if issubclass(base, models.Model) and base._meta.abstract:
            fields.extend(get_abstract_model_fields(base)) #recursion to get fields of base classes
    fields.extend(model_class._meta.fields)
    return fields

def is_model_abstract(model_class):
    return model_class._meta.abstract

# Example usage:
if is_model_abstract(MyAbstract):
    fields = get_abstract_model_fields(MyAbstract)
    for field in fields:
        print(field.name, field.__class__.__name__)

# Example of a concrete class inheriting from MyAbstract
class ConcreteModel(MyAbstract):
    concrete_field = models.BooleanField()

if is_model_abstract(ConcreteModel):
    print("Concrete model should not be abstract")
else:
    fields = get_abstract_model_fields(ConcreteModel)
    print("\nFields of concrete class, before inheritance:")
    for field in fields:
        print(field.name, field.__class__.__name__)

print("\nFields of concrete class, after inheritance:")
for field in ConcreteModel._meta.fields:
    print(field.name, field.__class__.__name__)

So get_abstract_model_fields should be used instead of the current:

django_typomatic/__init__.py", line 547, in __get_ts_interface
    fields = instance.get_fields().items()
             ~~~~~~~~~~~~~~~~~~~^^

The problem is the instantiation of the abstract class in the line above.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions