def __init__(self, model: Type[models.Model], model_pool: List[Type[models.Model]], type_url): self.model = model self.model_pool = model_pool self.type_url = type_url self.model_inspector = ModelInspector(model=model) self.fields = [ Field(field, model_pool=model_pool) for field in self.model_inspector.model_fields() ]
def __init__(self, model: Type[models.Model], model_pool: List[Type[models.Model]], parent: 'QuerysetLookups' = None, prefixes: List[str] = None, visited_models=None): self.model = model self.model_pool = model_pool self.parent = parent self.prefixes = prefixes if prefixes is not None else [] self.visited_models = visited_models if visited_models is not None else [] self.model_inspector = ModelInspector(model)
def __init__(self, model: Type[models.Model], model_pool: List[Type[models.Model]], type_url): self.model = model self.model_pool = model_pool self.type_url = type_url self.model_inspector = ModelInspector(model=model) self.queryset_lookups = QuerysetLookups(model=model, model_pool=model_pool)
def reverse_lookup_key(self): inspector = ModelInspector(model=self.field.related_model) return inspector.fk_field_to_model( model=self.field.model).name + '__id'
class QuerysetLookups(object): """ Helper class for rendering "type declarations" for Queryset lookups. """ def __init__(self, model: Type[models.Model], model_pool: List[Type[models.Model]], parent: 'QuerysetLookups' = None, prefixes: List[str] = None, visited_models=None): self.model = model self.model_pool = model_pool self.parent = parent self.prefixes = prefixes if prefixes is not None else [] self.visited_models = visited_models if visited_models is not None else [] self.model_inspector = ModelInspector(model) def _lookup_key(self, suffixes: List[str] = None): if suffixes is None: suffixes = [] return "__".join(self.prefixes + suffixes) def type_declarations(self): type_declarations = list() for field in self.model_inspector.model_fields(): type_declarations += self._field_type_declarations(field=field) return type_declarations def _field_type_declarations(self, field: models.Field): type_declarations = list() # If the field is relational, check that it is in the `model_pool`, and that # it has not already been "visited" (to prevent an infinite cycle). if isinstance( field, (models.ForeignKey, models.OneToOneField, models.ManyToOneRel)): if (field.related_model in self.model_pool) and (field.related_model not in self.visited_models): child_queryset_lookups = QuerysetLookups( model=field.related_model, model_pool=self.model_pool, prefixes=self.prefixes + [field.name], visited_models=self.visited_models + [self.model]) type_declarations += child_queryset_lookups.type_declarations() if not isinstance(field, models.ManyToOneRel): # This is the base lookup for the field. type_declarations.append( self._lookup_key([field.name]) + "? : " + self._lookup_type(field, None)) for lookup_str, lookup_cls in field.get_lookups().items(): type_declarations.append( self._lookup_key([field.name, lookup_str]) + "? : " + self._lookup_type(field, lookup_cls)) return type_declarations @staticmethod def _lookup_type(field, lookup_cls) -> str: if lookup_cls in TypeTranspiler.CONTAINER_TYPES: return TypeTranspiler.CONTAINER_TYPES[lookup_cls]( TypeTranspiler.transpile(field)) return TypeTranspiler.transpile(field)
def _test_get_fk_fields(self): inspector = ModelInspector(ThingChild) self.assertEqual(inspector.foreign_key_fields(), [ThingChild.parent])
def test_get_fk_field_to_model(self): inspector = ModelInspector(ThingChild) self.assertEqual(inspector.fk_field_to_model(Thing).name, 'parent')
def test_get_pk_field(self): inspector = ModelInspector(Thing) self.assertEqual(inspector.pk_field_name, 'id')
class Model(object): """ Class for rendering a TypeScript `rests` model. """ TEMPLATE = """ // ------------------------- // {{ model.name }} // // ------------------------- interface {{ model.interface_type_name }} { {% for type_dec in model.field_interface_type_declarations %}{{ type_dec }}, \n{% endfor %} } export class {{ model.name }} extends Model { static BASE_URL = '/{{ model.type_url }}'; static PK_FIELD_NAME = '{{ model.pk_field_name }}'; static FIELDS = [{{ model.literal_field_names }}]; static objects = {{ model.queryset_cls_name }}; static serverClient = serverClient; {% for type_dec in model.field_cls_type_declarations %}{{ type_dec }};\n{% endfor %} constructor({ {{ model.field_names|join(',') }} }: {{ model.interface_type_name }}){ super({ {{ model.field_names|join(',') }} }) } public async update(data: Partial<{{ model.interface_type_name }}>, responseHandlers: ResponseHandlers={}): Promise<{{ model.name }}>{ Object.keys(data).map((fieldName) => { this[fieldName] = data[fieldName]; }); await this.save(); return this; } {% for field in model.reverse_relation_fields %} public {{ field.name }}(lookups: {{ field.related_model_name }}Lookups = {}){ return new {{ field.related_model_name }}Queryset({...lookups, ...{ {{ field.reverse_lookup_key }}: this.pk()}}) } {% endfor %} } {{ model.queryset_cls_name }}.Model = {{ model.name }}; """ def __init__(self, model: Type[models.Model], model_pool: List[Type[models.Model]], type_url): self.model = model self.model_pool = model_pool self.type_url = type_url self.model_inspector = ModelInspector(model=model) self.fields = [Field(field) for field in self.model_inspector.model_fields()] @property def name(self): return self.model_inspector.model_name @property def pk_field_name(self): return self.model_inspector.pk_field_name @property def interface_type_name(self): """ Return the name given to the type interface for this model. """ return self.name + "Data" @property def field_names(self): field_names = list() for field in self.fields: field_names += field.names() return field_names @property def literal_field_names(self): return ", ".join("'{}'".format(f) for f in self.field_names) @property def field_interface_type_declarations(self): type_declarations = list() for field in self.fields: type_declarations += field.interface_type_declarations() return type_declarations @property def field_cls_type_declarations(self): type_declarations = list() for field in self.fields: type_declarations += field.cls_type_declarations() return type_declarations @property def queryset_cls_name(self): return self.name + "Queryset" @property def reverse_relation_fields(self): return [f for f in self.fields if f.is_reverse_relation] def render(self): return Template(self.TEMPLATE).render(model=self)