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
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
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}"
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