mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2026-01-02 01:27:03 -05:00
feat: Implement initial schema and add various API, service, and management command enhancements across the application.
This commit is contained in:
@@ -25,21 +25,13 @@ class FilterOptionSerializer(serializers.Serializer):
|
||||
selected?: boolean;
|
||||
}
|
||||
"""
|
||||
value = serializers.CharField(
|
||||
help_text="The actual value used for filtering"
|
||||
)
|
||||
label = serializers.CharField(
|
||||
help_text="Human-readable display label"
|
||||
)
|
||||
|
||||
value = serializers.CharField(help_text="The actual value used for filtering")
|
||||
label = serializers.CharField(help_text="Human-readable display label")
|
||||
count = serializers.IntegerField(
|
||||
required=False,
|
||||
allow_null=True,
|
||||
help_text="Number of items matching this filter option"
|
||||
)
|
||||
selected = serializers.BooleanField(
|
||||
default=False,
|
||||
help_text="Whether this option is currently selected"
|
||||
required=False, allow_null=True, help_text="Number of items matching this filter option"
|
||||
)
|
||||
selected = serializers.BooleanField(default=False, help_text="Whether this option is currently selected")
|
||||
|
||||
|
||||
class FilterRangeSerializer(serializers.Serializer):
|
||||
@@ -54,22 +46,12 @@ class FilterRangeSerializer(serializers.Serializer):
|
||||
unit?: string;
|
||||
}
|
||||
"""
|
||||
min = serializers.FloatField(
|
||||
allow_null=True,
|
||||
help_text="Minimum value for the range"
|
||||
)
|
||||
max = serializers.FloatField(
|
||||
allow_null=True,
|
||||
help_text="Maximum value for the range"
|
||||
)
|
||||
step = serializers.FloatField(
|
||||
default=1.0,
|
||||
help_text="Step size for range inputs"
|
||||
)
|
||||
|
||||
min = serializers.FloatField(allow_null=True, help_text="Minimum value for the range")
|
||||
max = serializers.FloatField(allow_null=True, help_text="Maximum value for the range")
|
||||
step = serializers.FloatField(default=1.0, help_text="Step size for range inputs")
|
||||
unit = serializers.CharField(
|
||||
required=False,
|
||||
allow_null=True,
|
||||
help_text="Unit of measurement (e.g., 'feet', 'mph', 'stars')"
|
||||
required=False, allow_null=True, help_text="Unit of measurement (e.g., 'feet', 'mph', 'stars')"
|
||||
)
|
||||
|
||||
|
||||
@@ -84,15 +66,10 @@ class BooleanFilterSerializer(serializers.Serializer):
|
||||
description: string;
|
||||
}
|
||||
"""
|
||||
key = serializers.CharField(
|
||||
help_text="The filter parameter key"
|
||||
)
|
||||
label = serializers.CharField(
|
||||
help_text="Human-readable label for the filter"
|
||||
)
|
||||
description = serializers.CharField(
|
||||
help_text="Description of what this filter does"
|
||||
)
|
||||
|
||||
key = serializers.CharField(help_text="The filter parameter key")
|
||||
label = serializers.CharField(help_text="Human-readable label for the filter")
|
||||
description = serializers.CharField(help_text="Description of what this filter does")
|
||||
|
||||
|
||||
class OrderingOptionSerializer(serializers.Serializer):
|
||||
@@ -105,12 +82,9 @@ class OrderingOptionSerializer(serializers.Serializer):
|
||||
label: string;
|
||||
}
|
||||
"""
|
||||
value = serializers.CharField(
|
||||
help_text="The ordering parameter value"
|
||||
)
|
||||
label = serializers.CharField(
|
||||
help_text="Human-readable label for the ordering option"
|
||||
)
|
||||
|
||||
value = serializers.CharField(help_text="The ordering parameter value")
|
||||
label = serializers.CharField(help_text="Human-readable label for the ordering option")
|
||||
|
||||
|
||||
class StandardizedFilterMetadataSerializer(serializers.Serializer):
|
||||
@@ -120,27 +94,16 @@ class StandardizedFilterMetadataSerializer(serializers.Serializer):
|
||||
This serializer ensures all filter metadata responses follow the same structure
|
||||
that the frontend expects, preventing runtime type errors.
|
||||
"""
|
||||
|
||||
categorical = serializers.DictField(
|
||||
child=FilterOptionSerializer(many=True),
|
||||
help_text="Categorical filter options with value/label/count structure"
|
||||
child=FilterOptionSerializer(many=True), help_text="Categorical filter options with value/label/count structure"
|
||||
)
|
||||
ranges = serializers.DictField(
|
||||
child=FilterRangeSerializer(),
|
||||
help_text="Range filter metadata with min/max/step/unit"
|
||||
)
|
||||
total_count = serializers.IntegerField(
|
||||
help_text="Total number of items in the filtered dataset"
|
||||
)
|
||||
ordering_options = FilterOptionSerializer(
|
||||
many=True,
|
||||
required=False,
|
||||
help_text="Available ordering options"
|
||||
)
|
||||
boolean_filters = BooleanFilterSerializer(
|
||||
many=True,
|
||||
required=False,
|
||||
help_text="Available boolean filter options"
|
||||
child=FilterRangeSerializer(), help_text="Range filter metadata with min/max/step/unit"
|
||||
)
|
||||
total_count = serializers.IntegerField(help_text="Total number of items in the filtered dataset")
|
||||
ordering_options = FilterOptionSerializer(many=True, required=False, help_text="Available ordering options")
|
||||
boolean_filters = BooleanFilterSerializer(many=True, required=False, help_text="Available boolean filter options")
|
||||
|
||||
|
||||
class PaginationMetadataSerializer(serializers.Serializer):
|
||||
@@ -157,28 +120,13 @@ class PaginationMetadataSerializer(serializers.Serializer):
|
||||
total_pages: number;
|
||||
}
|
||||
"""
|
||||
count = serializers.IntegerField(
|
||||
help_text="Total number of items across all pages"
|
||||
)
|
||||
next = serializers.URLField(
|
||||
allow_null=True,
|
||||
required=False,
|
||||
help_text="URL for the next page of results"
|
||||
)
|
||||
previous = serializers.URLField(
|
||||
allow_null=True,
|
||||
required=False,
|
||||
help_text="URL for the previous page of results"
|
||||
)
|
||||
page_size = serializers.IntegerField(
|
||||
help_text="Number of items per page"
|
||||
)
|
||||
current_page = serializers.IntegerField(
|
||||
help_text="Current page number (1-indexed)"
|
||||
)
|
||||
total_pages = serializers.IntegerField(
|
||||
help_text="Total number of pages"
|
||||
)
|
||||
|
||||
count = serializers.IntegerField(help_text="Total number of items across all pages")
|
||||
next = serializers.URLField(allow_null=True, required=False, help_text="URL for the next page of results")
|
||||
previous = serializers.URLField(allow_null=True, required=False, help_text="URL for the previous page of results")
|
||||
page_size = serializers.IntegerField(help_text="Number of items per page")
|
||||
current_page = serializers.IntegerField(help_text="Current page number (1-indexed)")
|
||||
total_pages = serializers.IntegerField(help_text="Total number of pages")
|
||||
|
||||
|
||||
class ApiResponseSerializer(serializers.Serializer):
|
||||
@@ -193,22 +141,14 @@ class ApiResponseSerializer(serializers.Serializer):
|
||||
errors?: ValidationError;
|
||||
}
|
||||
"""
|
||||
success = serializers.BooleanField(
|
||||
help_text="Whether the request was successful"
|
||||
)
|
||||
|
||||
success = serializers.BooleanField(help_text="Whether the request was successful")
|
||||
response_data = serializers.JSONField(
|
||||
required=False,
|
||||
help_text="Response data (structure varies by endpoint)",
|
||||
source='data'
|
||||
)
|
||||
message = serializers.CharField(
|
||||
required=False,
|
||||
help_text="Human-readable message about the operation"
|
||||
required=False, help_text="Response data (structure varies by endpoint)", source="data"
|
||||
)
|
||||
message = serializers.CharField(required=False, help_text="Human-readable message about the operation")
|
||||
response_errors = serializers.DictField(
|
||||
required=False,
|
||||
help_text="Validation errors (field -> error messages)",
|
||||
source='errors'
|
||||
required=False, help_text="Validation errors (field -> error messages)", source="errors"
|
||||
)
|
||||
|
||||
|
||||
@@ -228,18 +168,11 @@ class ErrorResponseSerializer(serializers.Serializer):
|
||||
data: null;
|
||||
}
|
||||
"""
|
||||
status = serializers.CharField(
|
||||
default="error",
|
||||
help_text="Response status indicator"
|
||||
)
|
||||
error = serializers.DictField(
|
||||
help_text="Error details"
|
||||
)
|
||||
|
||||
status = serializers.CharField(default="error", help_text="Response status indicator")
|
||||
error = serializers.DictField(help_text="Error details")
|
||||
response_data = serializers.JSONField(
|
||||
default=None,
|
||||
allow_null=True,
|
||||
help_text="Always null for error responses",
|
||||
source='data'
|
||||
default=None, allow_null=True, help_text="Always null for error responses", source="data"
|
||||
)
|
||||
|
||||
|
||||
@@ -257,32 +190,13 @@ class LocationSerializer(serializers.Serializer):
|
||||
longitude?: number;
|
||||
}
|
||||
"""
|
||||
city = serializers.CharField(
|
||||
help_text="City name"
|
||||
)
|
||||
state = serializers.CharField(
|
||||
required=False,
|
||||
allow_null=True,
|
||||
help_text="State/province name"
|
||||
)
|
||||
country = serializers.CharField(
|
||||
help_text="Country name"
|
||||
)
|
||||
address = serializers.CharField(
|
||||
required=False,
|
||||
allow_null=True,
|
||||
help_text="Street address"
|
||||
)
|
||||
latitude = serializers.FloatField(
|
||||
required=False,
|
||||
allow_null=True,
|
||||
help_text="Latitude coordinate"
|
||||
)
|
||||
longitude = serializers.FloatField(
|
||||
required=False,
|
||||
allow_null=True,
|
||||
help_text="Longitude coordinate"
|
||||
)
|
||||
|
||||
city = serializers.CharField(help_text="City name")
|
||||
state = serializers.CharField(required=False, allow_null=True, help_text="State/province name")
|
||||
country = serializers.CharField(help_text="Country name")
|
||||
address = serializers.CharField(required=False, allow_null=True, help_text="Street address")
|
||||
latitude = serializers.FloatField(required=False, allow_null=True, help_text="Latitude coordinate")
|
||||
longitude = serializers.FloatField(required=False, allow_null=True, help_text="Longitude coordinate")
|
||||
|
||||
|
||||
# Alias for backward compatibility
|
||||
@@ -301,24 +215,15 @@ class CompanyOutputSerializer(serializers.Serializer):
|
||||
roles?: string[];
|
||||
}
|
||||
"""
|
||||
id = serializers.IntegerField(
|
||||
help_text="Company ID"
|
||||
)
|
||||
name = serializers.CharField(
|
||||
help_text="Company name"
|
||||
)
|
||||
slug = serializers.SlugField(
|
||||
help_text="URL-friendly identifier"
|
||||
)
|
||||
|
||||
id = serializers.IntegerField(help_text="Company ID")
|
||||
name = serializers.CharField(help_text="Company name")
|
||||
slug = serializers.SlugField(help_text="URL-friendly identifier")
|
||||
roles = serializers.ListField(
|
||||
child=serializers.CharField(),
|
||||
required=False,
|
||||
help_text="Company roles (manufacturer, operator, etc.)"
|
||||
child=serializers.CharField(), required=False, help_text="Company roles (manufacturer, operator, etc.)"
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
class ModelChoices:
|
||||
"""
|
||||
Utility class to provide model choices for serializers using Rich Choice Objects.
|
||||
@@ -331,6 +236,7 @@ class ModelChoices:
|
||||
def get_park_status_choices():
|
||||
"""Get park status choices from Rich Choice registry."""
|
||||
from apps.core.choices.registry import get_choices
|
||||
|
||||
choices = get_choices("statuses", "parks")
|
||||
return [(choice.value, choice.label) for choice in choices]
|
||||
|
||||
@@ -338,6 +244,7 @@ class ModelChoices:
|
||||
def get_ride_status_choices():
|
||||
"""Get ride status choices from Rich Choice registry."""
|
||||
from apps.core.choices.registry import get_choices
|
||||
|
||||
choices = get_choices("statuses", "rides")
|
||||
return [(choice.value, choice.label) for choice in choices]
|
||||
|
||||
@@ -345,6 +252,7 @@ class ModelChoices:
|
||||
def get_company_role_choices():
|
||||
"""Get company role choices from Rich Choice registry."""
|
||||
from apps.core.choices.registry import get_choices
|
||||
|
||||
# Get rides domain company roles (MANUFACTURER, DESIGNER)
|
||||
rides_choices = get_choices("company_roles", "rides")
|
||||
# Get parks domain company roles (OPERATOR, PROPERTY_OWNER)
|
||||
@@ -356,6 +264,7 @@ class ModelChoices:
|
||||
def get_ride_category_choices():
|
||||
"""Get ride category choices from Rich Choice registry."""
|
||||
from apps.core.choices.registry import get_choices
|
||||
|
||||
choices = get_choices("categories", "rides")
|
||||
return [(choice.value, choice.label) for choice in choices]
|
||||
|
||||
@@ -363,6 +272,7 @@ class ModelChoices:
|
||||
def get_ride_post_closing_choices():
|
||||
"""Get ride post-closing status choices from Rich Choice registry."""
|
||||
from apps.core.choices.registry import get_choices
|
||||
|
||||
choices = get_choices("post_closing_statuses", "rides")
|
||||
return [(choice.value, choice.label) for choice in choices]
|
||||
|
||||
@@ -370,6 +280,7 @@ class ModelChoices:
|
||||
def get_coaster_track_choices():
|
||||
"""Get coaster track material choices from Rich Choice registry."""
|
||||
from apps.core.choices.registry import get_choices
|
||||
|
||||
choices = get_choices("track_materials", "rides")
|
||||
return [(choice.value, choice.label) for choice in choices]
|
||||
|
||||
@@ -377,6 +288,7 @@ class ModelChoices:
|
||||
def get_coaster_type_choices():
|
||||
"""Get coaster type choices from Rich Choice registry."""
|
||||
from apps.core.choices.registry import get_choices
|
||||
|
||||
choices = get_choices("coaster_types", "rides")
|
||||
return [(choice.value, choice.label) for choice in choices]
|
||||
|
||||
@@ -384,6 +296,7 @@ class ModelChoices:
|
||||
def get_launch_choices():
|
||||
"""Get launch system choices from Rich Choice registry (legacy method)."""
|
||||
from apps.core.choices.registry import get_choices
|
||||
|
||||
choices = get_choices("propulsion_systems", "rides")
|
||||
return [(choice.value, choice.label) for choice in choices]
|
||||
|
||||
@@ -391,6 +304,7 @@ class ModelChoices:
|
||||
def get_propulsion_system_choices():
|
||||
"""Get propulsion system choices from Rich Choice registry."""
|
||||
from apps.core.choices.registry import get_choices
|
||||
|
||||
choices = get_choices("propulsion_systems", "rides")
|
||||
return [(choice.value, choice.label) for choice in choices]
|
||||
|
||||
@@ -398,6 +312,7 @@ class ModelChoices:
|
||||
def get_photo_type_choices():
|
||||
"""Get photo type choices from Rich Choice registry."""
|
||||
from apps.core.choices.registry import get_choices
|
||||
|
||||
choices = get_choices("photo_types", "rides")
|
||||
return [(choice.value, choice.label) for choice in choices]
|
||||
|
||||
@@ -405,6 +320,7 @@ class ModelChoices:
|
||||
def get_spec_category_choices():
|
||||
"""Get technical specification category choices from Rich Choice registry."""
|
||||
from apps.core.choices.registry import get_choices
|
||||
|
||||
choices = get_choices("spec_categories", "rides")
|
||||
return [(choice.value, choice.label) for choice in choices]
|
||||
|
||||
@@ -412,6 +328,7 @@ class ModelChoices:
|
||||
def get_technical_spec_category_choices():
|
||||
"""Get technical specification category choices from Rich Choice registry."""
|
||||
from apps.core.choices.registry import get_choices
|
||||
|
||||
choices = get_choices("spec_categories", "rides")
|
||||
return [(choice.value, choice.label) for choice in choices]
|
||||
|
||||
@@ -419,6 +336,7 @@ class ModelChoices:
|
||||
def get_target_market_choices():
|
||||
"""Get target market choices from Rich Choice registry."""
|
||||
from apps.core.choices.registry import get_choices
|
||||
|
||||
choices = get_choices("target_markets", "rides")
|
||||
return [(choice.value, choice.label) for choice in choices]
|
||||
|
||||
@@ -426,6 +344,7 @@ class ModelChoices:
|
||||
def get_entity_type_choices():
|
||||
"""Get entity type choices for search functionality."""
|
||||
from apps.core.choices.registry import get_choices
|
||||
|
||||
choices = get_choices("entity_types", "core")
|
||||
return [(choice.value, choice.label) for choice in choices]
|
||||
|
||||
@@ -433,6 +352,7 @@ class ModelChoices:
|
||||
def get_health_status_choices():
|
||||
"""Get health check status choices from Rich Choice registry."""
|
||||
from apps.core.choices.registry import get_choices
|
||||
|
||||
choices = get_choices("health_statuses", "core")
|
||||
return [(choice.value, choice.label) for choice in choices]
|
||||
|
||||
@@ -440,6 +360,7 @@ class ModelChoices:
|
||||
def get_simple_health_status_choices():
|
||||
"""Get simple health check status choices from Rich Choice registry."""
|
||||
from apps.core.choices.registry import get_choices
|
||||
|
||||
choices = get_choices("simple_health_statuses", "core")
|
||||
return [(choice.value, choice.label) for choice in choices]
|
||||
|
||||
@@ -455,15 +376,10 @@ class EntityReferenceSerializer(serializers.Serializer):
|
||||
slug: string;
|
||||
}
|
||||
"""
|
||||
id = serializers.IntegerField(
|
||||
help_text="Unique identifier"
|
||||
)
|
||||
name = serializers.CharField(
|
||||
help_text="Display name"
|
||||
)
|
||||
slug = serializers.SlugField(
|
||||
help_text="URL-friendly identifier"
|
||||
)
|
||||
|
||||
id = serializers.IntegerField(help_text="Unique identifier")
|
||||
name = serializers.CharField(help_text="Display name")
|
||||
slug = serializers.SlugField(help_text="URL-friendly identifier")
|
||||
|
||||
|
||||
class ImageVariantsSerializer(serializers.Serializer):
|
||||
@@ -478,19 +394,11 @@ class ImageVariantsSerializer(serializers.Serializer):
|
||||
avatar?: string;
|
||||
}
|
||||
"""
|
||||
thumbnail = serializers.URLField(
|
||||
help_text="Thumbnail size image URL"
|
||||
)
|
||||
medium = serializers.URLField(
|
||||
help_text="Medium size image URL"
|
||||
)
|
||||
large = serializers.URLField(
|
||||
help_text="Large size image URL"
|
||||
)
|
||||
avatar = serializers.URLField(
|
||||
required=False,
|
||||
help_text="Avatar size image URL (for user avatars)"
|
||||
)
|
||||
|
||||
thumbnail = serializers.URLField(help_text="Thumbnail size image URL")
|
||||
medium = serializers.URLField(help_text="Medium size image URL")
|
||||
large = serializers.URLField(help_text="Large size image URL")
|
||||
avatar = serializers.URLField(required=False, help_text="Avatar size image URL (for user avatars)")
|
||||
|
||||
|
||||
class PhotoSerializer(serializers.Serializer):
|
||||
@@ -509,39 +417,15 @@ class PhotoSerializer(serializers.Serializer):
|
||||
uploaded_at?: string;
|
||||
}
|
||||
"""
|
||||
id = serializers.IntegerField(
|
||||
help_text="Photo ID"
|
||||
)
|
||||
image_variants = ImageVariantsSerializer(
|
||||
help_text="Available image size variants"
|
||||
)
|
||||
alt_text = serializers.CharField(
|
||||
required=False,
|
||||
allow_null=True,
|
||||
help_text="Alternative text for accessibility"
|
||||
)
|
||||
image_url = serializers.URLField(
|
||||
required=False,
|
||||
help_text="Primary image URL (for compatibility)"
|
||||
)
|
||||
caption = serializers.CharField(
|
||||
required=False,
|
||||
allow_null=True,
|
||||
help_text="Photo caption"
|
||||
)
|
||||
photo_type = serializers.CharField(
|
||||
required=False,
|
||||
allow_null=True,
|
||||
help_text="Type/category of photo"
|
||||
)
|
||||
uploaded_by = EntityReferenceSerializer(
|
||||
required=False,
|
||||
help_text="User who uploaded the photo"
|
||||
)
|
||||
uploaded_at = serializers.DateTimeField(
|
||||
required=False,
|
||||
help_text="When the photo was uploaded"
|
||||
)
|
||||
|
||||
id = serializers.IntegerField(help_text="Photo ID")
|
||||
image_variants = ImageVariantsSerializer(help_text="Available image size variants")
|
||||
alt_text = serializers.CharField(required=False, allow_null=True, help_text="Alternative text for accessibility")
|
||||
image_url = serializers.URLField(required=False, help_text="Primary image URL (for compatibility)")
|
||||
caption = serializers.CharField(required=False, allow_null=True, help_text="Photo caption")
|
||||
photo_type = serializers.CharField(required=False, allow_null=True, help_text="Type/category of photo")
|
||||
uploaded_by = EntityReferenceSerializer(required=False, help_text="User who uploaded the photo")
|
||||
uploaded_at = serializers.DateTimeField(required=False, help_text="When the photo was uploaded")
|
||||
|
||||
|
||||
class UserInfoSerializer(serializers.Serializer):
|
||||
@@ -556,20 +440,11 @@ class UserInfoSerializer(serializers.Serializer):
|
||||
avatar_url?: string;
|
||||
}
|
||||
"""
|
||||
id = serializers.IntegerField(
|
||||
help_text="User ID"
|
||||
)
|
||||
username = serializers.CharField(
|
||||
help_text="Username"
|
||||
)
|
||||
display_name = serializers.CharField(
|
||||
help_text="Display name"
|
||||
)
|
||||
avatar_url = serializers.URLField(
|
||||
required=False,
|
||||
allow_null=True,
|
||||
help_text="User avatar URL"
|
||||
)
|
||||
|
||||
id = serializers.IntegerField(help_text="User ID")
|
||||
username = serializers.CharField(help_text="Username")
|
||||
display_name = serializers.CharField(help_text="Display name")
|
||||
avatar_url = serializers.URLField(required=False, allow_null=True, help_text="User avatar URL")
|
||||
|
||||
|
||||
def validate_filter_metadata_contract(data: dict[str, Any]) -> dict[str, Any]:
|
||||
@@ -613,27 +488,22 @@ def ensure_filter_option_format(options: list[Any]) -> list[dict[str, Any]]:
|
||||
if isinstance(option, dict):
|
||||
# Already in correct format or close to it
|
||||
standardized_option = {
|
||||
'value': str(option.get('value', option.get('id', ''))),
|
||||
'label': option.get('label', option.get('name', str(option.get('value', '')))),
|
||||
'count': option.get('count'),
|
||||
'selected': option.get('selected', False)
|
||||
"value": str(option.get("value", option.get("id", ""))),
|
||||
"label": option.get("label", option.get("name", str(option.get("value", "")))),
|
||||
"count": option.get("count"),
|
||||
"selected": option.get("selected", False),
|
||||
}
|
||||
elif hasattr(option, 'value') and hasattr(option, 'label'):
|
||||
elif hasattr(option, "value") and hasattr(option, "label"):
|
||||
# RichChoice object format
|
||||
standardized_option = {
|
||||
'value': str(option.value),
|
||||
'label': str(option.label),
|
||||
'count': None,
|
||||
'selected': False
|
||||
"value": str(option.value),
|
||||
"label": str(option.label),
|
||||
"count": None,
|
||||
"selected": False,
|
||||
}
|
||||
else:
|
||||
# Simple value - use as both value and label
|
||||
standardized_option = {
|
||||
'value': str(option),
|
||||
'label': str(option),
|
||||
'count': None,
|
||||
'selected': False
|
||||
}
|
||||
standardized_option = {"value": str(option), "label": str(option), "count": None, "selected": False}
|
||||
|
||||
standardized.append(standardized_option)
|
||||
|
||||
@@ -651,8 +521,8 @@ def ensure_range_format(range_data: dict[str, Any]) -> dict[str, Any]:
|
||||
Range data in standard format
|
||||
"""
|
||||
return {
|
||||
'min': range_data.get('min'),
|
||||
'max': range_data.get('max'),
|
||||
'step': range_data.get('step', 1.0),
|
||||
'unit': range_data.get('unit')
|
||||
"min": range_data.get("min"),
|
||||
"max": range_data.get("max"),
|
||||
"step": range_data.get("step", 1.0),
|
||||
"unit": range_data.get("unit"),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user