def test_options_are_passed_to_field(self):
     schema = FieldSchema(name='field', data_type='integer')
     field_schema = ModelFieldSchema(model_schema=ModelSchema(name='mock'),
                                     field_schema=schema,
                                     null=True)
     field = FieldFactory(field_schema).make()
     assert field.null is True
 def test_make_field(self, data_type, expected_class, options):
     schema = FieldSchema(name='field', data_type=data_type)
     field_schema = ModelFieldSchema(model_schema=ModelSchema(name='mock'),
                                     field_schema=schema,
                                     **options)
     field = FieldFactory(field_schema).make()
     assert isinstance(field, expected_class)
Beispiel #3
0
 def test_make_field(self, data_type, expected_class, options,
                     model_schema):
     field_schema = FieldSchema(name='field',
                                data_type=data_type,
                                model_schema=model_schema)
     field = FieldFactory(field_schema).make_field()
     assert isinstance(field, expected_class)
Beispiel #4
0
 def test_make_field(self, class_name, expected_class, options,
                     model_schema):
     field_schema = FieldSchema(name="field",
                                class_name=class_name,
                                model_schema=model_schema,
                                kwargs=options)
     field = FieldFactory(field_schema).make_field()
     assert isinstance(field, expected_class)
Beispiel #5
0
 def test_table_relationship(self, class_name, expected_class, options,
                             model_schema, another_model_schema):
     field_schema = FieldSchema(
         name="field",
         class_name=class_name,
         model_schema=model_schema,
         kwargs={
             **options, "to": another_model_schema
         },
     )
     field_schema.null = True
     field = FieldFactory(field_schema).make_field()
     assert field.null is True
Beispiel #6
0
class FieldSchema(models.Model):
    _PROHIBITED_NAMES = ('__module__', '_schema', '_declared')
    _MAX_LENGTH_DATA_TYPES = ('character', )

    name = models.CharField(max_length=63)
    model_schema = models.ForeignKey(ModelSchema,
                                     on_delete=models.CASCADE,
                                     related_name='fields')
    data_type = models.CharField(max_length=16,
                                 choices=FieldFactory.data_type_choices(),
                                 editable=False)
    null = models.BooleanField(default=False)
    unique = models.BooleanField(default=False)
    max_length = models.PositiveIntegerField(null=True)

    class Meta:
        unique_together = (('name', 'model_schema'), )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._initial_name = self.name
        self._initial_null = self.null
        self._initial_field = self.get_registered_model_field()
        self._schema_editor = FieldSchemaEditor(self._initial_field)

    def save(self, **kwargs):
        self.validate()
        super().save(**kwargs)
        self.update_last_modified()
        model, field = self._get_model_with_field()
        self._schema_editor.update_column(model, field)

    def delete(self, **kwargs):
        model, field = self._get_model_with_field()
        self._schema_editor.drop_column(model, field)
        self.update_last_modified()
        super().delete(**kwargs)

    def validate(self):
        if self._initial_null and not self.null:
            raise NullFieldChangedError(
                f"Cannot change NULL field '{self.name}' to NOT NULL")

        if self.name in self.get_prohibited_names():
            raise InvalidFieldNameError(
                f'{self.name} is not a valid field name')

    def get_registered_model_field(self):
        latest_model = self.model_schema.get_registered_model()
        if latest_model and self.name:
            try:
                return latest_model._meta.get_field(self.name)
            except FieldDoesNotExist:
                pass

    @classmethod
    def get_prohibited_names(cls):
        # TODO: return prohbited names based on backend
        return cls._PROHIBITED_NAMES

    @classmethod
    def get_data_types(cls):
        return FieldFactory.get_data_types()

    @property
    def db_column(self):
        return slugify(self.name).replace('-', '_')

    def requires_max_length(self):
        return self.data_type in self.__class__._MAX_LENGTH_DATA_TYPES

    def update_last_modified(self):
        self.model_schema.last_modified = timezone.now()

    def get_options(self):
        """
        Get a dictionary of kwargs to be passed to the Django field constructor
        """
        options = {'null': self.null, 'unique': self.unique}
        if self.requires_max_length():
            options[
                'max_length'] = self.max_length or config.default_charfield_max_length(
                )
        return options

    def _get_model_with_field(self):
        model = self.model_schema.as_model()
        try:
            field = model._meta.get_field(self.db_column)
        except FieldDoesNotExist:
            field = None
        return model, field
Beispiel #7
0
 def get_data_types(cls):
     return FieldFactory.get_data_types()
Beispiel #8
0
 def test_options_are_passed_to_field(self, model_schema, field_schema):
     field_schema.null = True
     field = FieldFactory(field_schema).make_field()
     assert field.null is True