示例#1
0
    def column_sql(self,
                   model: Model,
                   field: Field,
                   include_default=False) -> Tuple[str, list]:
        # Get the column's type and use that as the basis of the SQL
        db_params = field.db_parameters(connection=self.connection)
        sql = db_params['type']
        params = []

        # Check for fields that aren't actually columns (e.g. M2M)
        if sql is None:
            return None, None

        if field.null and not self.connection.features.implied_column_null:
            sql += ' NULL'

        # Check constraints
        if not field.null:
            check = '%s IS NOT NULL' % self.quote_name(field.column)
            self.deferred_column_sql['CHECK'].append(
                self._create_check_sql_for_field(model,
                                                 field,
                                                 check,
                                                 qualifier='_nn'))

        db_params = field.db_parameters(connection=self.connection)
        if db_params['check']:
            self.deferred_column_sql['CHECK'].append(
                self._create_check_sql_for_field(model, field,
                                                 db_params['check']))

        # Primary/unique keys
        if field.primary_key:
            self.deferred_column_sql['PRIMARY_KEY'].append(
                self._create_primary_key_sql(model, field))
        elif field.unique:
            self.deferred_column_sql['UNIQUE'].append(
                self._create_unique_sql(model, [field.column]))

        # FK
        if field.remote_field and field.db_constraint:
            self.deferred_column_sql['FOREIGN_KEY'].append(
                self._create_fk_sql(model,
                                    field,
                                    suffix='_fk_%(to_table)s_%(to_column)s'))

        # Autoincrement SQL (for backends with post table definition variant)
        if field.get_internal_type() in ('AutoField', 'BigAutoField'):
            autoinc_sql = self.connection.ops.autoinc_sql(
                model._meta.db_table, field.column)
            if autoinc_sql:
                self.deferred_column_sql['AUTOINCREMENT'].extend(autoinc_sql)

        # Comment columns for fields with help_text
        if field.help_text:
            self.deferred_column_sql['COMMENT'].append(
                self._create_comment_sql(model, field))

        return sql, params
示例#2
0
    def get_field_meta(self, field: models.Field) -> OrderedDict:
        # check if we need to override default get_field_meta behaviour
        get_field_meta = getattr(self, 'get_%s_field_meta' % field.name, None)
        if callable(get_field_meta):
            return get_field_meta(field, self.obj)

        sentinel = object()
        d = OrderedDict()

        for attr in self.attr_list:
            val = getattr(field, attr, sentinel)
            if val is sentinel:
                continue
            if attr in ['max_length']:
                if val is None:
                    continue

            if attr in ['verbose_name']:
                val = force_text(val)

            d[attr] = val

        d['type'] = field.get_internal_type()
        d['required'] = self.is_required(field)
        if field.default != models.NOT_PROVIDED:
            if callable(field.default):
                d['default'] = field.default()
            else:
                d['default'] = field.default
        if hasattr(field, 'choices') and field.choices:
            d['choices'] = list(self.format_choices(field))

        if field.related_model:
            if field.name not in self.no_data:
                user_url_getter = getattr(self, 'get_%s_dataset_url' % field.name.lower(), None)
                if callable(user_url_getter):
                    d['data'] = user_url_getter(field, self.obj)
                elif field.name in self.dataset_urls:
                    d['data'] = force_text(self.dataset_urls[field.name])
                else:
                    d['data'] = self.get_field_related_data(field)

        data_update = self.update_fields.get(field.name, {})
        d.update(data_update)

        # check if we need to perform runtime update
        update_field_meta_callback = getattr(self, 'update_%s_field_meta' % field.name, None)
        if callable(update_field_meta_callback):
            d.update(update_field_meta_callback(field, self.obj))

        return d
示例#3
0
def _column_sql(field: Field) -> str:
    """
    Generate the SQL required to create the given field in a CREATE TABLE
    statement.
    """

    field_type = field.get_internal_type()
    data_types = PostgreSQLDatabaseWrapper.data_types
    data_type_check_constraints = PostgreSQLDatabaseWrapper.data_type_check_constraints

    if isinstance(field, JSONField):
        column_type = "jsonb"
    elif isinstance(field, ForeignKey):
        column_type = "integer"
    else:
        column_type = data_types[field_type]

    try:
        check_constraint = data_type_check_constraints[field_type]
        check_constraint = check_constraint % {"column": field.column}
    except KeyError:
        check_constraint = None

    # Interpolate any dynamic values (like max_length)
    column_type = column_type % field.__dict__

    # Add NOT NULL if null values are not allowed
    if not field.null:
        column_type += " NOT NULL"

    if field.choices:
        # Assume that if choices is set, it's a char field and that the choices
        # are a list of strings that are valid values.
        column_type += f' CHECK ("{field.column}" IN ('
        column_type += ", ".join([f"'{choice}'" for _, choice in field.choices])
        column_type += "))"
    elif check_constraint:
        # If the column had a check constrain defined, add that
        column_type += f" CHECK ({check_constraint})"

    return f"{field.column} {column_type}"
示例#4
0
class QueryField:

    LENGTH_SUFFIX = '_length'

    # non-database fields
    DYNAMIC_FIELDS = {
        'ipv4': {
            'verbose_name': 'IPv4',
            'function': HelperFunctions.get_ipv4
        },
        'ipv6': {
            'verbose_name': 'IPv6',
            'function': HelperFunctions.get_ipv6
        },
        'status_ping': {
            'verbose_name': 'Ping',
            'function': HelperFunctions.get_status_ping
        }
    }

    MAPPING = {
        # Aliases
        'architecture': {
            'field': Architecture._meta.get_field('name'),
            'related_name': 'architecture',
            'verbose_name': 'Architecture'
        },
        'name': {
            'field': Machine._meta.get_field('fqdn')
        },
        'domain': {
            'field': Domain._meta.get_field('name'),
            'related_name': 'fqdn_domain',
            'verbose_name': 'Domain'
        },
        'enclosure': {
            'field':
            Machine._meta.get_field('enclosure'),
            'pre':
            lambda x: Enclosure.objects.get(name__iexact=x)
            if type(x) is str else x,
            'post':
            lambda x: Enclosure.objects.get(pk=x).name
        },
        'group': {
            'field':
            Machine._meta.get_field('group'),
            'pre':
            lambda x: MachineGroup.objects.get(name__iexact=x)
            if type(x) is str else x,
            'post':
            lambda x: MachineGroup.objects.get(pk=x).name
        },
        'system': {
            'field': System._meta.get_field('name'),
            'related_name': 'system',
            'verbose_name': 'System',
            'pre': lambda x: x if type(x) is str else x,
            'post': lambda x: x
        },
        'ram': {
            'field': Machine._meta.get_field('ram_amount')
        },
        'reserved_by': {
            'field':
            Machine._meta.get_field('reserved_by'),
            'pre':
            lambda x: HelperFunctions.username_to_id(x)
            if type(x) is str else x,
            'post':
            lambda x: User.objects.get(pk=x).username
        },
        'res_by': {
            'field':
            Machine._meta.get_field('reserved_by'),
            'pre':
            lambda x: HelperFunctions.username_to_id(x)
            if type(x) is str else x,
            'post':
            lambda x: User.objects.get(pk=x).username
        },
        'reserved_by_email': {
            'field': User._meta.get_field('email'),
            'related_name': 'reserved_by',
            'verbose_name': 'Email'
        },
        'status_ipv4': {
            'field':
            Machine._meta.get_field('status_ipv4'),
            'pre':
            lambda x: dict({
                value: key
                for key, value in dict(Machine.StatusIP.CHOICE).items()
            }).get(x) if type(x) is str else x,
            'post':
            lambda x: dict(Machine.StatusIP.CHOICE).get(x)
        },
        'status_ipv6': {
            'field':
            Machine._meta.get_field('status_ipv6'),
            'pre':
            lambda x: dict({
                value: key
                for key, value in dict(Machine.StatusIP.CHOICE).items()
            }).get(x) if type(x) is str else x,
            'post':
            lambda x: dict(Machine.StatusIP.CHOICE).get(x)
        },

        # SerialConsole
        'serial_console_server': {
            'field':
            SerialConsole._meta.get_field('console_server'),
            'related_name':
            'serialconsole',
            'verbose_name':
            'Console server',
            'pre':
            lambda x: Machine.objects.get(fqdn__iexact=x)
            if type(x) is str else x,
            'post':
            lambda x: Machine.objects.get(pk=x).fqdn
        },
        'serial_cscreen_server': {
            'field':
            SerialConsole._meta.get_field('cscreen_server'),
            'related_name':
            'serialconsole',
            'verbose_name':
            'CScreen server',
            'pre':
            lambda x: Machine.objects.get(fqdn__iexact=x)
            if type(x) is str else x,
            'post':
            lambda x: Machine.objects.get(pk=x).fqdn
        },
        'serial_management_bmc': {
            'field':
            SerialConsole._meta.get_field('management_bmc'),
            'related_name':
            'serialconsole',
            'verbose_name':
            'Management BMC',
            'pre':
            lambda x: Machine.objects.get(fqdn__iexact=x)
            if type(x) is str else x,
            'post':
            lambda x: Machine.objects.get(pk=x).fqdn
        },
        'serial_type': {
            'field':
            SerialConsole._meta.get_field('type'),
            'related_name':
            'serialconsole',
            'verbose_name':
            'Serial console',
            'pre':
            lambda x: SerialConsoleType.Type.to_int(x)
            if type(x) is str else x,
            'post':
            lambda x: SerialConsoleType.Type.to_str(x)
        },
        'sconsole': {
            'field':
            SerialConsole._meta.get_field('type'),
            'related_name':
            'serialconsole',
            'verbose_name':
            'Serial console',
            'pre':
            lambda x: SerialConsoleType.Type.to_int(x)
            if type(x) is str else x,
            'post':
            lambda x: SerialConsoleType.Type.to_str(x)
        },
        'serial_baud': {
            'field': SerialConsole._meta.get_field('baud_rate'),
            'related_name': 'serialconsole',
            'verbose_name': 'Baud rate',
        },
        'serial_command': {
            'field': SerialConsole._meta.get_field('command'),
            'related_name': 'serialconsole',
            'verbose_name': 'Command',
        },
        'serial_device': {
            'field': SerialConsole._meta.get_field('device'),
            'related_name': 'serialconsole',
            'verbose_name': 'Device',
        },
        'serial_kernel_device': {
            'field': SerialConsole._meta.get_field('kernel_device'),
            'related_name': 'serialconsole',
            'verbose_name': 'Device',
        },
        'serial_port': {
            'field': SerialConsole._meta.get_field('port'),
            'related_name': 'serialconsole',
            'verbose_name': 'Port',
        },
        'serial_comment': {
            'field': SerialConsole._meta.get_field('comment'),
            'related_name': 'serialconsole',
            'verbose_name': 'Comment',
        },

        # RemotePower
        'rpower_management_bmc': {
            'field':
            RemotePower._meta.get_field('management_bmc'),
            'related_name':
            'remotepower',
            'verbose_name':
            'Management BMC',
            'pre':
            lambda x: Machine.objects.get(fqdn__iexact=x)
            if type(x) is str else x,
            'post':
            lambda x: Machine.objects.get(pk=x).fqdn
        },
        'rpower_power_device': {
            'field':
            RemotePower._meta.get_field('remote_power_device'),
            'related_name':
            'remotepower',
            'verbose_name':
            'Remote power device',
            'pre':
            lambda x: Machine.objects.get(fqdn__iexact=x)
            if type(x) is str else x,
            'post':
            lambda x: Machine.objects.get(pk=x).fqdn
        },
        'rpower_device': {
            'field': RemotePower._meta.get_field('device'),
            'related_name': 'remotepower',
            'verbose_name': 'Device',
        },
        'rpower_port': {
            'field': RemotePower._meta.get_field('port'),
            'related_name': 'remotepower',
            'verbose_name': 'Port',
        },
        'rpower_type': {
            'field': RemotePower._meta.get_field('type'),
            'related_name': 'remotepower',
            'verbose_name': 'Remotepower',
            'pre': lambda x: RemotePower.Type.to_int(x)
            if type(x) is str else x,
            'post': lambda x: RemotePower.Type.to_str(x)
        },
        'rpower': {
            'field': RemotePower._meta.get_field('type'),
            'related_name': 'remotepower',
            'verbose_name': 'Remotepower',
            'pre': lambda x: RemotePower.Type.to_int(x)
            if type(x) is str else x,
            'post': lambda x: RemotePower.Type.to_str(x)
        },

        # Installation
        'inst_active': {
            'field': Installation._meta.get_field('active'),
            'related_name': 'installations',
            'verbose_name': 'Active inst.'
        },
        'inst_arch': {
            'field': Installation._meta.get_field('architecture'),
            'related_name': 'installations',
            'verbose_name': 'Inst. architecture'
        },
        'inst_dist': {
            'field': Installation._meta.get_field('distribution'),
            'related_name': 'installations',
            'verbose_name': 'Distribution'
        },
        'inst_kernel': {
            'field': Installation._meta.get_field('kernelversion'),
            'related_name': 'installations',
            'verbose_name': 'Kernel version'
        },
        'inst_partition': {
            'field': Installation._meta.get_field('partition'),
            'related_name': 'installations',
            'verbose_name': 'Partition'
        },

        # NetworkInterface
        'iface_driver_module': {
            'field': NetworkInterface._meta.get_field('driver_module'),
            'related_name': 'networkinterfaces',
            'verbose_name': 'IF driver module'
        },
        'iface_ethernet_type': {
            'field': NetworkInterface._meta.get_field('ethernet_type'),
            'related_name': 'networkinterfaces',
            'verbose_name': 'IF ethernet type'
        },
        'iface_mac_address': {
            'field': NetworkInterface._meta.get_field('mac_address'),
            'related_name': 'networkinterfaces',
            'verbose_name': 'IF MAC address'
        },
        'iface_name': {
            'field': NetworkInterface._meta.get_field('name'),
            'related_name': 'networkinterfaces',
            'verbose_name': 'IF name'
        },
        'iface_primary': {
            'field': NetworkInterface._meta.get_field('primary'),
            'related_name': 'networkinterfaces',
            'verbose_name': 'IF primary'
        },

        # PCIDevice
        'pci_slot': {
            'field': PCIDevice._meta.get_field('slot'),
            'related_name': 'pcidevice',
            'verbose_name': 'Slot'
        },
        'pci_vendorid': {
            'field': PCIDevice._meta.get_field('vendor_id'),
            'related_name': 'pcidevice',
            'verbose_name': 'Vendor ID'
        },
        'pci_vendor': {
            'field': PCIDevice._meta.get_field('vendor'),
            'related_name': 'pcidevice',
            'verbose_name': 'Vendor'
        },
        'pci_deviceid': {
            'field': PCIDevice._meta.get_field('device_id'),
            'related_name': 'pcidevice',
            'verbose_name': 'Device ID'
        },
        'pci_device': {
            'field': PCIDevice._meta.get_field('device'),
            'related_name': 'pcidevice',
            'verbose_name': 'Device'
        },
        'pci_classid': {
            'field': PCIDevice._meta.get_field('class_id'),
            'related_name': 'pcidevice',
            'verbose_name': 'Class ID'
        },
        'pci_classname': {
            'field': PCIDevice._meta.get_field('classname'),
            'related_name': 'pcidevice',
            'verbose_name': 'Class name'
        },
        'pci_svendorid': {
            'field': PCIDevice._meta.get_field('subvendor_id'),
            'related_name': 'pcidevice',
            'verbose_name': 'Subvendor ID'
        },
        'pci_svendorname': {
            'field': PCIDevice._meta.get_field('subvendor'),
            'related_name': 'pcidevice',
            'verbose_name': 'Subvendor'
        },
        'pci_sdeviceid': {
            'field': PCIDevice._meta.get_field('subdevice_id'),
            'related_name': 'pcidevice',
            'verbose_name': 'Subdevice ID'
        },
        'pci_sdevicename': {
            'field': PCIDevice._meta.get_field('subdevice'),
            'related_name': 'pcidevice',
            'verbose_name': 'Subdevice'
        },
        'pci_revision': {
            'field': PCIDevice._meta.get_field('revision'),
            'related_name': 'pcidevice',
            'verbose_name': 'Revision'
        },
        'pci_driver': {
            'field': PCIDevice._meta.get_field('drivermodule'),
            'related_name': 'pcidevice',
            'verbose_name': 'Drivermodule'
        },

        # Platform
        'enclosure_platform': {
            'field': Platform._meta.get_field('name'),
            'related_name': 'enclosure__platform',
            'verbose_name': 'Platform'
        },

        # Vendor
        'enclosure_vendor': {
            'field': Vendor._meta.get_field('name'),
            'related_name': 'enclosure__platform__vendor',
            'verbose_name': 'Vendor'
        },

        # Annotation
        'annotation_text': {
            'field': Annotation._meta.get_field('text'),
            'related_name': 'annotations',
            'verbose_name': 'Annotation'
        },
        'annotation_reporter': {
            'field':
            Annotation._meta.get_field('reporter'),
            'related_name':
            'annotations',
            'verbose_name':
            'Reporter',
            'pre':
            lambda x: HelperFunctions.username_to_id(x)
            if type(x) is str else x,
            'post':
            lambda x: User.objects.get(pk=x).username
        },
        'annotation_created': {
            'field': Annotation._meta.get_field('created'),
            'related_name': 'annotations',
            'verbose_name': 'Created',
        }
    }

    def __init__(self, token):
        """
        Constructor for `QueryField`. This constructor tries to define a valid query field with
        all corresponding values for further processing in the context of DB querying.

        Example:

            QueryField('comment')                -> Machine.comment
            QueryField('comment_length')         -> Machine.comment (for querying the char length)
            QueryField('ipv4')                   -> Machine.ipv4 (dynamic field)
            QueryField('installations__comment') -> Machine.installations.comment (related field)
        """
        self._field = None
        self._related_name = None
        self._verbose_name = None
        self._dynamic = False
        self._pre_function = None
        self._post_function = None

        if self.LENGTH_SUFFIX in token:
            token = token.replace(self.LENGTH_SUFFIX, '')
            self._annotation = self.LENGTH_SUFFIX
        else:
            self._annotation = None

        try:
            self._field = self.MAPPING[token]['field']
            self._verbose_name = self.MAPPING[token].get('verbose_name')
            if not self._verbose_name:
                self._verbose_name = Machine._meta.get_field(
                    self._field.name).verbose_name
            self._related_name = self.MAPPING[token].get('related_name')
            self._pre_function = self.MAPPING[token].get('pre')
            self._post_function = self.MAPPING[token].get('post')
        except KeyError:
            pass

        if not self._field and (token in self.DYNAMIC_FIELDS):
            self._field = Field(name=token)
            self._verbose_name = self.DYNAMIC_FIELDS[
                self._field.name]['verbose_name']
            self._dynamic_field_function = self.DYNAMIC_FIELDS[
                self._field.name]['function']
            self._dynamic = True

        if not self._field:
            try:
                self._field = Machine._meta.get_field(token)
                self._verbose_name = self._field.verbose_name
            except FieldDoesNotExist:
                pass

        if not self._field:
            related_name = '__'.join(token.split('__')[:-1])
            field_name = token.split('__')[-1]

            if related_name:
                for token, values in self.MAPPING.items():
                    if related_name == values.get('related_name'):
                        if field_name == values['field'].name:
                            self._field = self.MAPPING[token]['field']
                            self._related_name = related_name
                            self._verbose_name = self.MAPPING[token][
                                'verbose_name']
                            self._pre_function = self.MAPPING[token].get('pre')
                            self._post_function = self.MAPPING[token].get(
                                'post')

        if not self._field:
            raise ValueError("Unknown field '{}'!".format(token))

    def __str__(self):
        return self.db_field_name

    def __repr__(self):
        if not self.is_dynamic:
            return '<{}: {}>'.format(self.__class__.__name__,
                                     self.db_field_name)
        return '<{}: {} (dynamic)>'.format(self.__class__.__name__,
                                           self.db_field_name)

    @classmethod
    def get_valid_field_names(cls):
        """
        Returns a list of valid field names. These are all `Machine` field names, all `MAPPING`
        field names and all dynamic field names in `DYNAMIC_FIELDS`.
        """
        field_names = [field.name for field in Machine._meta.fields]
        field_names += list(cls.MAPPING.keys())
        field_names += list(cls.DYNAMIC_FIELDS.keys())
        return field_names

    @property
    def db_field_name(self):
        """
        Returns a valid field name for querying the DB.
        """
        if not self._related_name:
            field_name = self._field.name
        else:
            field_name = '{}__{}'.format(self._related_name, self._field.name)

        if self._annotation is not None:
            field_name += self._annotation

        return field_name

    @property
    def related_name(self):
        """
        Returns the related name of a `QueryField` object.
        """
        return self._related_name

    @property
    def verbose_name(self):
        """
        Returns the verbose name of a `QueryField` object.
        """
        if self._verbose_name.islower():
            return self._verbose_name.capitalize()
        return self._verbose_name

    @property
    def null(self):
        """
        Returns if a `QueryField` object can be `NULL` in the DB.
        """
        return self._field.null

    @property
    def is_dynamic(self):
        """
        Returns if a `QueryField` object is dynamic (non-database value) or not.
        """
        return self._dynamic

    def is_BooleanField(self):
        """
        Checks if a `QueryField` object is a boolean field.
        """
        return 'BooleanField' in self._field.get_internal_type()

    def is_CharField(self):
        """
        Checks if a `QueryField` object is a character field.
        """
        return 'CharField' in self._field.get_internal_type()

    def is_TextField(self):
        """
        Checks if a `QueryField` object is a character field.
        """
        return 'TextField' in self._field.get_internal_type()

    def is_ForeignKey(self):
        """
        Checks if a `QueryField` object is a foreign key.
        """
        return 'ForeignKey' in self._field.get_internal_type()

    def is_DateField(self):
        """
        Checks if a `QueryField` object is a date field.
        """
        return 'DateField' in self._field.get_internal_type()

    def is_DateTimeField(self):
        """
        Checks if a `QueryField` object is a datetime field.
        """
        return 'DateTimeField' in self._field.get_internal_type()

    def get_db_function_length(self):
        """
        Returns a tuple with a valid DB field name for querying string length and its corresponding
        DB function.

        Example:

            ('comment_lenght', Length('comment'))
        """
        field = QueryField(self.db_field_name + self.LENGTH_SUFFIX)
        return (field, {field.db_field_name: Length(self.db_field_name)})

    @property
    def type(self):
        """
        Returns fields type as string.
        """
        return self._field.get_internal_type()

    @property
    def dynamic_field_function(self):
        """
        Returns a optional function for processing dynamic fields (non-database fields). If no
        dynamic function is defined, a simple lambda function is returned which simply returns the
        input value.
        """
        if self._dynamic_field_function:
            return self._dynamic_field_function
        return lambda x: x

    @property
    def pre_function(self):
        """
        Returns a optional pre-function. If no pre-function is defined, a simple lambda function is
        returned which simply returns the input value.
        """
        if self._pre_function:
            return self._pre_function
        return lambda x: x

    @property
    def post_function(self):
        """
        Returns a optional post-function. If no post-function is defined, a simple lambda function
        is returned which simply returns the input value.
        """
        if self._post_function:
            return self._post_function
        return lambda x: x