mirror of
https://github.com/eitchtee/WYGIWYH.git
synced 2026-03-28 20:22:01 +01:00
changes
This commit is contained in:
205
app/apps/common/fields/forms/dynamic_select.py
Normal file
205
app/apps/common/fields/forms/dynamic_select.py
Normal file
@@ -0,0 +1,205 @@
|
||||
from django import forms
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import transaction
|
||||
|
||||
from apps.common.widgets.tom_select import TomSelect, TomSelectMultiple
|
||||
|
||||
|
||||
# class DynamicModelChoiceField(forms.ModelChoiceField):
|
||||
# def __init__(self, model, *args, **kwargs):
|
||||
# self.model = model
|
||||
# self.queryset = kwargs.pop("queryset", model.objects.all())
|
||||
# super().__init__(queryset=self.queryset, *args, **kwargs)
|
||||
# self._created_instance = None
|
||||
#
|
||||
# self.widget = TomSelect(clear_button=True, create=True)
|
||||
#
|
||||
# def to_python(self, value):
|
||||
# if value in self.empty_values:
|
||||
# return None
|
||||
# try:
|
||||
# key = self.to_field_name or "pk"
|
||||
# return self.model.objects.get(**{key: value})
|
||||
# except (ValueError, TypeError, self.model.DoesNotExist):
|
||||
# return value # Return the raw value; we'll handle creation in clean()
|
||||
#
|
||||
# def clean(self, value):
|
||||
# if isinstance(value, self.model):
|
||||
# return value
|
||||
# if isinstance(value, str):
|
||||
# try:
|
||||
# if value.isdigit():
|
||||
# return self.model.objects.get(id=value)
|
||||
# else:
|
||||
# raise self.model.DoesNotExist
|
||||
# except self.model.DoesNotExist:
|
||||
# try:
|
||||
# with transaction.atomic():
|
||||
# instance = self.model.objects.create(name=value)
|
||||
# self._created_instance = instance
|
||||
# return instance
|
||||
# except Exception as e:
|
||||
# raise ValidationError(
|
||||
# self.error_messages["invalid_choice"], code="invalid_choice"
|
||||
# )
|
||||
# return super().clean(value)
|
||||
#
|
||||
# def bound_data(self, data, initial):
|
||||
# if self._created_instance and isinstance(data, str):
|
||||
# if data == self._created_instance.name:
|
||||
# return self._created_instance.pk
|
||||
# return super().bound_data(data, initial)
|
||||
|
||||
|
||||
class DynamicModelChoiceField(forms.ModelChoiceField):
|
||||
def __init__(self, model, *args, **kwargs):
|
||||
self.model = model
|
||||
self.queryset = kwargs.pop("queryset", model.objects.all())
|
||||
super().__init__(queryset=self.queryset, *args, **kwargs)
|
||||
self._created_instance = None
|
||||
|
||||
self.widget = TomSelect(clear_button=True, create=True)
|
||||
|
||||
def to_python(self, value):
|
||||
if value in self.empty_values:
|
||||
return None
|
||||
try:
|
||||
key = self.to_field_name or "pk"
|
||||
return self.model.objects.get(**{key: value})
|
||||
except (ValueError, TypeError, self.model.DoesNotExist):
|
||||
return value # Return the raw value; we'll handle creation in clean()
|
||||
|
||||
def clean(self, value):
|
||||
if value in self.empty_values:
|
||||
if self.required:
|
||||
raise ValidationError(self.error_messages["required"], code="required")
|
||||
return None
|
||||
|
||||
if isinstance(value, self.model):
|
||||
return value
|
||||
|
||||
if isinstance(value, str):
|
||||
value = value.strip()
|
||||
if not value:
|
||||
if self.required:
|
||||
raise ValidationError(
|
||||
self.error_messages["required"], code="required"
|
||||
)
|
||||
return None
|
||||
|
||||
try:
|
||||
if value.isdigit():
|
||||
return self.model.objects.get(id=value)
|
||||
else:
|
||||
raise self.model.DoesNotExist
|
||||
except self.model.DoesNotExist:
|
||||
try:
|
||||
with transaction.atomic():
|
||||
instance = self.model.objects.create(name=value)
|
||||
self._created_instance = instance
|
||||
return instance
|
||||
except Exception as e:
|
||||
raise ValidationError(
|
||||
self.error_messages["invalid_choice"], code="invalid_choice"
|
||||
)
|
||||
return super().clean(value)
|
||||
|
||||
def bound_data(self, data, initial):
|
||||
if self._created_instance and isinstance(data, str):
|
||||
if data == self._created_instance.name:
|
||||
return self._created_instance.pk
|
||||
return super().bound_data(data, initial)
|
||||
|
||||
|
||||
class DynamicModelMultipleChoiceField(forms.ModelMultipleChoiceField):
|
||||
"""
|
||||
A custom ModelMultipleChoiceField that creates new entries if they don't exist.
|
||||
|
||||
This field allows users to select multiple existing options or add new ones.
|
||||
If a selected option doesn't exist, it will be created in the database.
|
||||
|
||||
Attributes:
|
||||
create_field (str): The name of the field to use when creating new instances.
|
||||
"""
|
||||
|
||||
def __init__(self, model, **kwargs):
|
||||
"""
|
||||
Initialize the CreateIfNotExistsModelMultipleChoiceField.
|
||||
|
||||
Args:
|
||||
create_field (str): The name of the field to use when creating new instances.
|
||||
*args: Variable length argument list.
|
||||
**kwargs: Arbitrary keyword arguments.
|
||||
"""
|
||||
self.create_field = kwargs.pop("create_field", None)
|
||||
if not self.create_field:
|
||||
raise ValueError("The 'create_field' parameter is required.")
|
||||
self.model = model
|
||||
self.queryset = kwargs.pop("queryset", model.objects.all())
|
||||
super().__init__(queryset=self.queryset, **kwargs)
|
||||
|
||||
self.widget = TomSelectMultiple(
|
||||
remove_button=True, clear_button=True, create=True, checkboxes=True
|
||||
)
|
||||
|
||||
def _create_new_instance(self, value):
|
||||
"""
|
||||
Create a new instance of the model with the given value.
|
||||
|
||||
Args:
|
||||
value: The value to use for creating the new instance.
|
||||
|
||||
Returns:
|
||||
Model: The newly created model instance.
|
||||
|
||||
Raises:
|
||||
ValidationError: If there's an error creating the new instance.
|
||||
"""
|
||||
try:
|
||||
with transaction.atomic():
|
||||
new_instance = self.queryset.model(**{self.create_field: value})
|
||||
new_instance.full_clean()
|
||||
new_instance.save()
|
||||
return new_instance
|
||||
except Exception as e:
|
||||
raise ValidationError(f"Error creating new instance: {str(e)}")
|
||||
|
||||
def clean(self, value):
|
||||
"""
|
||||
Clean and validate the field value.
|
||||
|
||||
This method checks if each selected choice exists in the database.
|
||||
If a choice doesn't exist, it creates a new instance of the model.
|
||||
|
||||
Args:
|
||||
value (list): List of selected values.
|
||||
|
||||
Returns:
|
||||
list: A list containing all selected and newly created model instances.
|
||||
|
||||
Raises:
|
||||
ValidationError: If there's an error during the cleaning process.
|
||||
"""
|
||||
if not value:
|
||||
return []
|
||||
|
||||
print(value)
|
||||
|
||||
string_values = set(str(v) for v in value)
|
||||
existing_objects = list(
|
||||
self.queryset.filter(**{f"{self.create_field}__in": string_values})
|
||||
)
|
||||
existing_values = set(
|
||||
str(getattr(obj, self.create_field)) for obj in existing_objects
|
||||
)
|
||||
|
||||
new_values = string_values - existing_values
|
||||
new_objects = []
|
||||
|
||||
for new_value in new_values:
|
||||
try:
|
||||
new_objects.append(self._create_new_instance(new_value))
|
||||
except ValidationError as e:
|
||||
raise ValidationError(f"Error creating '{new_value}': {str(e)}")
|
||||
|
||||
return existing_objects + new_objects
|
||||
Reference in New Issue
Block a user