mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-01 05:16:26 -06:00
Maintain current query_params
API for form fields, transform data structure in widget
This commit is contained in:
parent
809de8683b
commit
658ac89af0
BIN
netbox/project-static/dist/netbox.js
vendored
BIN
netbox/project-static/dist/netbox.js
vendored
Binary file not shown.
BIN
netbox/project-static/dist/netbox.js.map
vendored
BIN
netbox/project-static/dist/netbox.js.map
vendored
Binary file not shown.
@ -1,13 +1,14 @@
|
||||
import queryString from 'query-string';
|
||||
import debounce from 'just-debounce-it';
|
||||
import { readableColor } from 'color2k';
|
||||
import debounce from 'just-debounce-it';
|
||||
import queryString from 'query-string';
|
||||
import SlimSelect from 'slim-select';
|
||||
import { createToast } from '../../bs';
|
||||
import { hasUrl, hasExclusions, isTrigger } from '../util';
|
||||
import { FilterFieldMap } from './filterFields';
|
||||
import { DynamicParamsMap } from './dynamicParams';
|
||||
import { isStaticParams } from './types';
|
||||
import {
|
||||
isTruthy,
|
||||
hasMore,
|
||||
isTruthy,
|
||||
hasError,
|
||||
getElement,
|
||||
getApiData,
|
||||
@ -90,8 +91,6 @@ export class APISelect {
|
||||
* Form Field Names → Object containing:
|
||||
* - Query parameter key name
|
||||
* - Query value
|
||||
* - Other options such as a default value, and the option to include
|
||||
* null values.
|
||||
*
|
||||
* This is different from `queryParams` in that it tracks all _possible_ related fields and their
|
||||
* values, even if they are empty. Further, the keys in `queryParams` correspond to the actual
|
||||
@ -99,7 +98,12 @@ export class APISelect {
|
||||
* the model. For example, `tenant_group` would be the field name, but `group_id` would be the
|
||||
* query parameter.
|
||||
*/
|
||||
private readonly filterFields: FilterFieldMap = new FilterFieldMap();
|
||||
private readonly dynamicParams: DynamicParamsMap = new DynamicParamsMap();
|
||||
|
||||
/**
|
||||
* API query parameters that are already known by the server and should not change.
|
||||
*/
|
||||
private readonly staticParams: QueryFilter = new Map();
|
||||
|
||||
/**
|
||||
* Mapping of URL template key/value pairs. If this element's URL contains Django template tags
|
||||
@ -187,30 +191,21 @@ export class APISelect {
|
||||
});
|
||||
|
||||
// Initialize API query properties.
|
||||
// this.getFilteredBy();
|
||||
this.getFilterFields();
|
||||
this.getStaticParams();
|
||||
this.getDynamicParams();
|
||||
this.getPathKeys();
|
||||
|
||||
// for (const filter of this.filterParams.keys()) {
|
||||
// this.updateQueryParams(filter);
|
||||
// }
|
||||
for (const filter of this.filterFields.keys()) {
|
||||
// Populate static query parameters.
|
||||
for (const [key, value] of this.staticParams.entries()) {
|
||||
this.queryParams.set(key, value);
|
||||
}
|
||||
|
||||
// Populate dynamic query parameters with any form values that are already known.
|
||||
for (const filter of this.dynamicParams.keys()) {
|
||||
this.updateQueryParams(filter);
|
||||
}
|
||||
|
||||
// Add any already-resolved key/value pairs to the API query parameters.
|
||||
// for (const [key, value] of this.filterParams.entries()) {
|
||||
// if (isTruthy(value)) {
|
||||
// this.queryParams.set(key, value);
|
||||
// }
|
||||
// }
|
||||
for (const value of this.filterFields.values()) {
|
||||
const { queryParam, queryValue } = value;
|
||||
if (isTruthy(queryValue)) {
|
||||
this.queryParams.set(queryParam, queryValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Populate dynamic path values with any form values that are already known.
|
||||
for (const filter of this.pathValues.keys()) {
|
||||
this.updatePathValues(filter);
|
||||
}
|
||||
@ -365,7 +360,7 @@ export class APISelect {
|
||||
// Create a unique iterator of all possible form fields which, when changed, should cause this
|
||||
// element to update its API query.
|
||||
// const dependencies = new Set([...this.filterParams.keys(), ...this.pathValues.keys()]);
|
||||
const dependencies = new Set([...this.filterFields.keys(), ...this.pathValues.keys()]);
|
||||
const dependencies = new Set([...this.dynamicParams.keys(), ...this.pathValues.keys()]);
|
||||
|
||||
for (const dep of dependencies) {
|
||||
const filterElement = document.querySelector(`[name="${dep}"]`);
|
||||
@ -559,11 +554,6 @@ export class APISelect {
|
||||
this.updatePathValues(target.name);
|
||||
this.updateQueryUrl();
|
||||
|
||||
console.group(this.name, this.queryUrl);
|
||||
console.log(this.filterFields);
|
||||
console.log(this.queryParams);
|
||||
console.groupEnd();
|
||||
|
||||
// Load new data.
|
||||
Promise.all([this.loadData()]);
|
||||
}
|
||||
@ -655,18 +645,27 @@ export class APISelect {
|
||||
|
||||
if (elementValue.length > 0) {
|
||||
// If the field has a value, add it to the map.
|
||||
this.filterFields.updateValue(fieldName, elementValue);
|
||||
|
||||
const current = this.filterFields.get(fieldName);
|
||||
this.dynamicParams.updateValue(fieldName, elementValue);
|
||||
// Get the updated value.
|
||||
const current = this.dynamicParams.get(fieldName);
|
||||
|
||||
if (typeof current !== 'undefined') {
|
||||
const { queryParam, queryValue, includeNull } = current;
|
||||
const { queryParam, queryValue } = current;
|
||||
let value = [] as Stringifiable[];
|
||||
if (includeNull) {
|
||||
value = [...value, null];
|
||||
|
||||
if (this.staticParams.has(queryParam)) {
|
||||
// If the field is defined in `staticParams`, we should merge the dynamic value with
|
||||
// the static value.
|
||||
const staticValue = this.staticParams.get(queryParam);
|
||||
if (typeof staticValue !== 'undefined') {
|
||||
value = [...staticValue, ...queryValue];
|
||||
}
|
||||
} else {
|
||||
// If the field is _not_ defined in `staticParams`, we should replace the current value
|
||||
// with the new dynamic value.
|
||||
value = queryValue;
|
||||
}
|
||||
if (queryValue.length > 0) {
|
||||
value = [...value, ...queryValue];
|
||||
if (value.length > 0) {
|
||||
this.queryParams.set(queryParam, value);
|
||||
} else {
|
||||
this.queryParams.delete(queryParam);
|
||||
@ -674,7 +673,7 @@ export class APISelect {
|
||||
}
|
||||
} else {
|
||||
// Otherwise, delete it (we don't want to send an empty query like `?site_id=`)
|
||||
const queryParam = this.filterFields.queryParam(fieldName);
|
||||
const queryParam = this.dynamicParams.queryParam(fieldName);
|
||||
if (queryParam !== null) {
|
||||
this.queryParams.delete(queryParam);
|
||||
}
|
||||
@ -773,17 +772,48 @@ export class APISelect {
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a select element should be filtered by the value of another select element.
|
||||
* Determine if a this instances' options should be filtered by the value of another select
|
||||
* element.
|
||||
*
|
||||
* Looks for the DOM attribute `data-filter-fields`, the value of which is a JSON array of
|
||||
* Looks for the DOM attribute `data-dynamic-params`, the value of which is a JSON array of
|
||||
* objects containing information about how to handle the related field.
|
||||
*/
|
||||
private getFilterFields(): void {
|
||||
const serialized = this.base.getAttribute('data-filter-fields');
|
||||
private getDynamicParams(): void {
|
||||
const serialized = this.base.getAttribute('data-dynamic-params');
|
||||
try {
|
||||
this.filterFields.addFromJson(serialized);
|
||||
this.dynamicParams.addFromJson(serialized);
|
||||
} catch (err) {
|
||||
console.group(`Unable to determine filter fields for select field '${this.name}'`);
|
||||
console.group(`Unable to determine dynamic query parameters for select field '${this.name}'`);
|
||||
console.warn(err);
|
||||
console.groupEnd();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this instance's options should be filtered by static values passed from the
|
||||
* server.
|
||||
*
|
||||
* Looks for the DOM attribute `data-static-params`, the value of which is a JSON array of
|
||||
* objects containing key/value pairs to add to `this.staticParams`.
|
||||
*/
|
||||
private getStaticParams(): void {
|
||||
const serialized = this.base.getAttribute('data-static-params');
|
||||
|
||||
try {
|
||||
if (isTruthy(serialized)) {
|
||||
const deserialized = JSON.parse(serialized);
|
||||
if (isStaticParams(deserialized)) {
|
||||
for (const { queryParam, queryValue } of deserialized) {
|
||||
if (Array.isArray(queryValue)) {
|
||||
this.staticParams.set(queryParam, queryValue);
|
||||
} else {
|
||||
this.staticParams.set(queryParam, [queryValue]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.group(`Unable to determine static query parameters for select field '${this.name}'`);
|
||||
console.warn(err);
|
||||
console.groupEnd();
|
||||
}
|
||||
|
@ -1,20 +1,19 @@
|
||||
import { isTruthy } from '../../util';
|
||||
import { isDataFilterFields } from './types';
|
||||
import { isDataDynamicParams } from './types';
|
||||
|
||||
import type { Stringifiable } from 'query-string';
|
||||
import type { FilterFieldValue } from './types';
|
||||
import type { QueryParam } from './types';
|
||||
|
||||
/**
|
||||
* Extension of built-in `Map` to add convenience functions.
|
||||
*/
|
||||
export class FilterFieldMap extends Map<string, FilterFieldValue> {
|
||||
export class DynamicParamsMap extends Map<string, QueryParam> {
|
||||
/**
|
||||
* Get the query parameter key based on field name.
|
||||
*
|
||||
* @param fieldName Related field name.
|
||||
* @returns `queryParam` key.
|
||||
*/
|
||||
public queryParam(fieldName: string): Nullable<FilterFieldValue['queryParam']> {
|
||||
public queryParam(fieldName: string): Nullable<QueryParam['queryParam']> {
|
||||
const value = this.get(fieldName);
|
||||
if (typeof value !== 'undefined') {
|
||||
return value.queryParam;
|
||||
@ -28,7 +27,7 @@ export class FilterFieldMap extends Map<string, FilterFieldValue> {
|
||||
* @param fieldName Related field name.
|
||||
* @returns `queryValue` value, or an empty array if there is no corresponding Map entry.
|
||||
*/
|
||||
public queryValue(fieldName: string): FilterFieldValue['queryValue'] {
|
||||
public queryValue(fieldName: string): QueryParam['queryValue'] {
|
||||
const value = this.get(fieldName);
|
||||
if (typeof value !== 'undefined') {
|
||||
return value.queryValue;
|
||||
@ -43,43 +42,33 @@ export class FilterFieldMap extends Map<string, FilterFieldValue> {
|
||||
* @param queryValue New value.
|
||||
* @returns `true` if the update was successful, `false` if there was no corresponding Map entry.
|
||||
*/
|
||||
public updateValue(fieldName: string, queryValue: FilterFieldValue['queryValue']): boolean {
|
||||
public updateValue(fieldName: string, queryValue: QueryParam['queryValue']): boolean {
|
||||
const current = this.get(fieldName);
|
||||
if (isTruthy(current)) {
|
||||
const { queryParam, includeNull } = current;
|
||||
this.set(fieldName, { queryParam, queryValue, includeNull });
|
||||
const { queryParam } = current;
|
||||
this.set(fieldName, { queryParam, queryValue });
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the underlying map based on the JSON passed in the `data-filter-fields` attribute.
|
||||
* Populate the underlying map based on the JSON passed in the `data-dynamic-params` attribute.
|
||||
*
|
||||
* @param json Raw JSON string from `data-filter-fields` attribute.
|
||||
* @param json Raw JSON string from `data-dynamic-params` attribute.
|
||||
*/
|
||||
public addFromJson(json: string | null | undefined): void {
|
||||
if (isTruthy(json)) {
|
||||
const deserialized = JSON.parse(json);
|
||||
// Ensure the value is the data structure we expect.
|
||||
if (isDataFilterFields(deserialized)) {
|
||||
for (const { queryParam, fieldName, defaultValue, includeNull } of deserialized) {
|
||||
let queryValue = [] as Stringifiable[];
|
||||
if (isTruthy(defaultValue)) {
|
||||
// Add the default value, if it exists.
|
||||
if (Array.isArray(defaultValue)) {
|
||||
// If the default value is an array, add all elements to the value.
|
||||
queryValue = [...queryValue, ...defaultValue];
|
||||
} else {
|
||||
queryValue = [defaultValue];
|
||||
}
|
||||
}
|
||||
if (isDataDynamicParams(deserialized)) {
|
||||
for (const { queryParam, fieldName } of deserialized) {
|
||||
// Populate the underlying map with the initial data.
|
||||
this.set(fieldName, { queryParam, queryValue, includeNull });
|
||||
this.set(fieldName, { queryParam, queryValue: [] });
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
`Data from 'data-filter-fields' attribute is improperly formatted: '${json}'`,
|
||||
`Data from 'data-dynamic-params' attribute is improperly formatted: '${json}'`,
|
||||
);
|
||||
}
|
||||
}
|
@ -27,6 +27,40 @@ export type FilterFieldValue = {
|
||||
includeNull: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* JSON data structure from `data-dynamic-params` attribute.
|
||||
*/
|
||||
export type DataDynamicParam = {
|
||||
/**
|
||||
* Name of form field to track.
|
||||
*
|
||||
* @example [name="tenant_group"]
|
||||
*/
|
||||
fieldName: string;
|
||||
/**
|
||||
* Query param key.
|
||||
*
|
||||
* @example group_id
|
||||
*/
|
||||
queryParam: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* `queryParams` Map value.
|
||||
*/
|
||||
export type QueryParam = {
|
||||
queryParam: string;
|
||||
queryValue: Stringifiable[];
|
||||
};
|
||||
|
||||
/**
|
||||
* JSON data structure from `data-static-params` attribute.
|
||||
*/
|
||||
export type DataStaticParam = {
|
||||
queryParam: string;
|
||||
queryValue: Stringifiable | Stringifiable[];
|
||||
};
|
||||
|
||||
/**
|
||||
* JSON data passed from Django on the `data-filter-fields` attribute.
|
||||
*/
|
||||
@ -109,3 +143,47 @@ export function isDataFilterFields(value: unknown): value is DataFilterFields[]
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strict Type Guard to determine if a deserialized value from the `data-dynamic-params` attribute
|
||||
* is of type `DataDynamicParam[]`.
|
||||
*
|
||||
* @param value Deserialized value from `data-dynamic-params` attribute.
|
||||
*/
|
||||
export function isDataDynamicParams(value: unknown): value is DataDynamicParam[] {
|
||||
if (Array.isArray(value)) {
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
if ('fieldName' in item && 'queryParam' in item) {
|
||||
return (
|
||||
typeof (item as DataDynamicParam).fieldName === 'string' &&
|
||||
typeof (item as DataDynamicParam).queryParam === 'string'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strict Type Guard to determine if a deserialized value from the `data-static-params` attribute
|
||||
* is of type `DataStaticParam[]`.
|
||||
*
|
||||
* @param value Deserialized value from `data-static-params` attribute.
|
||||
*/
|
||||
export function isStaticParams(value: unknown): value is DataStaticParam[] {
|
||||
if (Array.isArray(value)) {
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
if ('queryParam' in item && 'queryValue' in item) {
|
||||
return (
|
||||
typeof (item as DataStaticParam).queryParam === 'string' &&
|
||||
typeof (item as DataStaticParam).queryValue !== 'undefined'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import json
|
||||
from typing import Dict, Sequence, Union
|
||||
from typing import Dict, Sequence, List, Tuple, Union
|
||||
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
@ -28,6 +28,9 @@ __all__ = (
|
||||
)
|
||||
|
||||
JSONPrimitive = Union[str, bool, int, float, None]
|
||||
QueryParamValue = Union[JSONPrimitive, Sequence[JSONPrimitive]]
|
||||
QueryParam = Dict[str, QueryParamValue]
|
||||
ProcessedParams = Sequence[Dict[str, Sequence[JSONPrimitive]]]
|
||||
|
||||
|
||||
class SmallTextarea(forms.Textarea):
|
||||
@ -138,88 +141,132 @@ class APISelect(SelectWithDisabled):
|
||||
|
||||
:param api_url: API endpoint URL. Required if not set automatically by the parent field.
|
||||
"""
|
||||
|
||||
dynamic_params: Dict[str, str]
|
||||
static_params: Dict[str, List[str]]
|
||||
|
||||
def __init__(self, api_url=None, full=False, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.attrs['class'] = 'netbox-api-select'
|
||||
self.dynamic_params: Dict[str, List[str]] = {}
|
||||
self.static_params: Dict[str, List[str]] = {}
|
||||
|
||||
if api_url:
|
||||
self.attrs['data-url'] = '/{}{}'.format(settings.BASE_PATH, api_url.lstrip('/')) # Inject BASE_PATH
|
||||
|
||||
def add_query_param(self, key: str, value: JSONPrimitive) -> None:
|
||||
def _process_query_param(self, key: str, value: JSONPrimitive) -> None:
|
||||
"""
|
||||
Add a query parameter with a static value to the API request.
|
||||
Based on query param value's type and value, update instance's dynamic/static params.
|
||||
"""
|
||||
self.add_filter_fields({'accessor': key, 'field_name': key, 'default_value': value})
|
||||
if isinstance(value, str):
|
||||
# Coerce `True` boolean.
|
||||
if value.lower() == 'true':
|
||||
value = True
|
||||
# Coerce `False` boolean.
|
||||
elif value.lower() == 'false':
|
||||
value = False
|
||||
# Query parameters cannot have a `None` (or `null` in JSON) type, convert
|
||||
# `None` types to `'null'` so that ?key=null is used in the query URL.
|
||||
elif value is None:
|
||||
value = 'null'
|
||||
|
||||
def add_filter_fields(self, filter_fields: Union[Dict[str, JSONPrimitive], Sequence[Dict[str, JSONPrimitive]]]) -> None:
|
||||
# Check type of `value` again, since it may have changed.
|
||||
if isinstance(value, str):
|
||||
if value.startswith('$'):
|
||||
# A value starting with `$` indicates a dynamic query param, where the
|
||||
# initial value is unknown and will be updated at the JavaScript layer
|
||||
# as the related form field's value changes.
|
||||
field_name = value.strip('$')
|
||||
self.dynamic_params[field_name] = key
|
||||
else:
|
||||
# A value _not_ starting with `$` indicates a static query param, where
|
||||
# the value is already known and should not be changed at the JavaScript
|
||||
# layer.
|
||||
if key in self.static_params:
|
||||
current = self.static_params[key]
|
||||
self.static_params[key] = [*current, value]
|
||||
else:
|
||||
self.static_params[key] = [value]
|
||||
else:
|
||||
# Any non-string values are passed through as static query params, since
|
||||
# dynamic query param values have to be a string (in order to start with
|
||||
# `$`).
|
||||
if key in self.static_params:
|
||||
current = self.static_params[key]
|
||||
self.static_params[key] = [*current, value]
|
||||
else:
|
||||
self.static_params[key] = [value]
|
||||
|
||||
def _process_query_params(self, query_params: QueryParam) -> None:
|
||||
"""
|
||||
Add details about another form field, the value for which should
|
||||
be added to this APISelect's URL query parameters.
|
||||
|
||||
:Example:
|
||||
|
||||
```python
|
||||
{
|
||||
'field_name': 'tenant_group',
|
||||
'accessor': 'tenant',
|
||||
'default_value': 1,
|
||||
'include_null': False,
|
||||
}
|
||||
```
|
||||
|
||||
:param filter_fields: Dict or list of dicts with the following properties:
|
||||
|
||||
- accessor: The related field's property name. For example, on the
|
||||
`Tenant`model, a related model might be `TenantGroup`. In
|
||||
this case, `accessor` would be `group_id`.
|
||||
|
||||
- field_name: The related field's form name. In the above `Tenant`
|
||||
example, `field_name` would be `tenant_group`.
|
||||
|
||||
- default_value: (Optional) Set a default initial value, which can be
|
||||
overridden if the field changes.
|
||||
|
||||
- include_null: (Optional) Include `null` on queries for the related
|
||||
field. For example, if `True`, `?<fieldName>=null` will
|
||||
be added to all API queries for this field.
|
||||
|
||||
Process an entire query_params dictionary, and handle primitive or list values.
|
||||
"""
|
||||
for key, value in query_params.items():
|
||||
if isinstance(value, (List, Tuple)):
|
||||
# If value is a list/tuple, iterate through each item.
|
||||
for item in value:
|
||||
self._process_query_param(key, item)
|
||||
else:
|
||||
self._process_query_param(key, value)
|
||||
|
||||
def _serialize_params(self, key: str, params: ProcessedParams) -> None:
|
||||
"""
|
||||
Serialize dynamic or static query params to JSON and add the serialized value to
|
||||
the widget attributes by `key`.
|
||||
"""
|
||||
key = 'data-filter-fields'
|
||||
# Deserialize the current serialized value from the widget, using an empty JSON
|
||||
# array as a fallback in the event one is not defined.
|
||||
current = json.loads(self.attrs.get(key, '[]'))
|
||||
|
||||
# Create a new list of filter fields using camelCse to align with front-end code standards
|
||||
# (this value will be read and used heavily at the JavaScript layer).
|
||||
update: Sequence[Dict[str, str]] = []
|
||||
try:
|
||||
if isinstance(filter_fields, Sequence):
|
||||
update = [
|
||||
{
|
||||
'fieldName': field['field_name'],
|
||||
'queryParam': field['accessor'],
|
||||
'defaultValue': field.get('default_value'),
|
||||
'includeNull': field.get('include_null', False),
|
||||
} for field in filter_fields
|
||||
]
|
||||
elif isinstance(filter_fields, Dict):
|
||||
update = [
|
||||
{
|
||||
'fieldName': filter_fields['field_name'],
|
||||
'queryParam': filter_fields['accessor'],
|
||||
'defaultValue': filter_fields.get('default_value'),
|
||||
'includeNull': filter_fields.get('include_null', False),
|
||||
}
|
||||
]
|
||||
|
||||
except KeyError as error:
|
||||
raise KeyError(f"Missing required property '{error.args[0]}' on APISelect.filter_fields") from error
|
||||
|
||||
# Combine the current values with the updated values and serialize the result as
|
||||
# JSON. Note: the `separators` kwarg effectively removes extra whitespace from
|
||||
# the serialized JSON string, which is ideal since these will be passed as
|
||||
# attributes to HTML elements and parsed on the client.
|
||||
self.attrs[key] = json.dumps([*current, *update], separators=(',', ':'))
|
||||
self.attrs[key] = json.dumps([*current, *params], separators=(',', ':'))
|
||||
|
||||
def _add_dynamic_params(self) -> None:
|
||||
"""
|
||||
Convert post-processed dynamic query params to data structure expected by front-
|
||||
end, serialize the value to JSON, and add it to the widget attributes.
|
||||
"""
|
||||
key = 'data-dynamic-params'
|
||||
if len(self.dynamic_params) > 0:
|
||||
try:
|
||||
update = [{'fieldName': f, 'queryParam': q} for (f, q) in self.dynamic_params.items()]
|
||||
self._serialize_params(key, update)
|
||||
except IndexError as error:
|
||||
raise RuntimeError(f"Missing required value for dynamic query param: '{self.dynamic_params}'") from error
|
||||
|
||||
def _add_static_params(self) -> None:
|
||||
"""
|
||||
Convert post-processed static query params to data structure expected by front-
|
||||
end, serialize the value to JSON, and add it to the widget attributes.
|
||||
"""
|
||||
key = 'data-static-params'
|
||||
if len(self.static_params) > 0:
|
||||
try:
|
||||
update = [{'queryParam': k, 'queryValue': v} for (k, v) in self.static_params.items()]
|
||||
self._serialize_params(key, update)
|
||||
except IndexError as error:
|
||||
raise RuntimeError(f"Missing required value for static query param: '{self.static_params}'") from error
|
||||
|
||||
def add_query_params(self, query_params: QueryParam) -> None:
|
||||
"""
|
||||
Proccess & add a dictionary of URL query parameters to the widget attributes.
|
||||
"""
|
||||
# Process query parameters. This populates `self.dynamic_params` and `self.static_params`.
|
||||
self._process_query_params(query_params)
|
||||
# Add processed dynamic parameters to widget attributes.
|
||||
self._add_dynamic_params()
|
||||
# Add processed static parameters to widget attributes.
|
||||
self._add_static_params()
|
||||
|
||||
def add_query_param(self, key: str, value: QueryParamValue) -> None:
|
||||
"""
|
||||
Process & add a key/value pair of URL query parameters to the widget attributes.
|
||||
"""
|
||||
self.add_query_params({key: value})
|
||||
|
||||
|
||||
class APISelectMultiple(APISelect, forms.SelectMultiple):
|
||||
|
Loading…
Reference in New Issue
Block a user