mirror of
				https://github.com/SectorLabs/django-localized-fields.git
				synced 2025-10-30 18:48:56 +03:00 
			
		
		
		
	added new LocalizedCharField, LocalizedTextField and LocalizedFileField fields
This commit is contained in:
		| @@ -1,6 +1,7 @@ | |||||||
| from .forms import LocalizedFieldForm, LocalizedFieldWidget | from .forms import LocalizedFieldForm, LocalizedFieldWidget | ||||||
| from .fields import (LocalizedAutoSlugField, LocalizedField, | from .fields import (LocalizedAutoSlugField, LocalizedField, | ||||||
|                      LocalizedUniqueSlugField) |                      LocalizedUniqueSlugField, LocalizedCharField, | ||||||
|  |                      LocalizedTextField, LocalizedFileField) | ||||||
| from .localized_value import LocalizedValue | from .localized_value import LocalizedValue | ||||||
| from .mixins import AtomicSlugRetryMixin | from .mixins import AtomicSlugRetryMixin | ||||||
| from .models import LocalizedModel | from .models import LocalizedModel | ||||||
| @@ -14,6 +15,9 @@ __all__ = [ | |||||||
|     'LocalizedAutoSlugField', |     'LocalizedAutoSlugField', | ||||||
|     'LocalizedUniqueSlugField', |     'LocalizedUniqueSlugField', | ||||||
|     'LocalizedBleachField', |     'LocalizedBleachField', | ||||||
|  |     'LocalizedCharField', | ||||||
|  |     'LocalizedTextField', | ||||||
|  |     'LocalizedFileField', | ||||||
|     'LocalizedFieldWidget', |     'LocalizedFieldWidget', | ||||||
|     'LocalizedFieldForm', |     'LocalizedFieldForm', | ||||||
|     'AtomicSlugRetryMixin' |     'AtomicSlugRetryMixin' | ||||||
|   | |||||||
| @@ -1,11 +1,15 @@ | |||||||
| from django.contrib.admin import ModelAdmin | from django.contrib.admin import ModelAdmin | ||||||
|  |  | ||||||
| from .fields import LocalizedField | from .fields import LocalizedField, LocalizedCharField, LocalizedTextField, \ | ||||||
|  |     LocalizedFileField | ||||||
| from . import widgets | from . import widgets | ||||||
|  |  | ||||||
|  |  | ||||||
| FORMFIELD_FOR_LOCALIZED_FIELDS_DEFAULTS = { | FORMFIELD_FOR_LOCALIZED_FIELDS_DEFAULTS = { | ||||||
|     LocalizedField: {'widget': widgets.AdminLocalizedFieldWidget}, |     LocalizedField: {'widget': widgets.AdminLocalizedFieldWidget}, | ||||||
|  |     LocalizedCharField: {'widget': widgets.AdminLocalizedCharFieldWidget}, | ||||||
|  |     LocalizedTextField: {'widget': widgets.AdminLocalizedFieldWidget}, | ||||||
|  |     LocalizedFileField: {'widget': widgets.AdminLocalizedFileFieldWidget}, | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,12 +1,18 @@ | |||||||
| from .localized_field import LocalizedField | from .localized_field import LocalizedField | ||||||
| from .localized_autoslug_field import LocalizedAutoSlugField | from .localized_autoslug_field import LocalizedAutoSlugField | ||||||
| from .localized_uniqueslug_field import LocalizedUniqueSlugField | from .localized_uniqueslug_field import LocalizedUniqueSlugField | ||||||
|  | from .localized_char_field import LocalizedCharField | ||||||
|  | from .localized_text_field import LocalizedTextField | ||||||
|  | from .localized_file_field import LocalizedFileField | ||||||
|  |  | ||||||
|  |  | ||||||
| __all__ = [ | __all__ = [ | ||||||
|     'LocalizedField', |     'LocalizedField', | ||||||
|     'LocalizedAutoSlugField', |     'LocalizedAutoSlugField', | ||||||
|     'LocalizedUniqueSlugField', |     'LocalizedUniqueSlugField', | ||||||
|  |     'LocalizedCharField', | ||||||
|  |     'LocalizedTextField', | ||||||
|  |     'LocalizedFileField' | ||||||
| ] | ] | ||||||
|  |  | ||||||
| try: | try: | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								localized_fields/fields/localized_char_field.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								localized_fields/fields/localized_char_field.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | from ..forms import LocalizedCharFieldForm | ||||||
|  | from .localized_field import LocalizedField | ||||||
|  | from ..localized_value import LocalizedStingValue | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedCharField(LocalizedField): | ||||||
|  |     attr_class = LocalizedStingValue | ||||||
|  |  | ||||||
|  |     def formfield(self, **kwargs): | ||||||
|  |         """Gets the form field associated with this field.""" | ||||||
|  |         defaults = { | ||||||
|  |             'form_class': LocalizedCharFieldForm | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         defaults.update(kwargs) | ||||||
|  |         return super().formfield(**defaults) | ||||||
| @@ -187,7 +187,10 @@ class LocalizedField(HStoreField): | |||||||
|         # are any of the language fiels None/empty? |         # are any of the language fiels None/empty? | ||||||
|         is_all_null = True |         is_all_null = True | ||||||
|         for lang_code, _ in settings.LANGUAGES: |         for lang_code, _ in settings.LANGUAGES: | ||||||
|             if value.get(lang_code): |             # NOTE(seroy): use check for None, instead of | ||||||
|  |             # `bool(value.get(lang_code))==True` condition, cause in this way | ||||||
|  |             # we can not save '' value | ||||||
|  |             if value.get(lang_code) is not None: | ||||||
|                 is_all_null = False |                 is_all_null = False | ||||||
|                 break |                 break | ||||||
|  |  | ||||||
| @@ -215,7 +218,9 @@ class LocalizedField(HStoreField): | |||||||
|  |  | ||||||
|         primary_lang_val = getattr(value, settings.LANGUAGE_CODE) |         primary_lang_val = getattr(value, settings.LANGUAGE_CODE) | ||||||
|  |  | ||||||
|         if not primary_lang_val: |         # NOTE(seroy): use check for None, instead of `not primary_lang_val` | ||||||
|  |         # condition, cause in this way we can not save '' value | ||||||
|  |         if primary_lang_val is None: | ||||||
|             raise IntegrityError( |             raise IntegrityError( | ||||||
|                 'null value in column "%s.%s" violates not-null constraint' % ( |                 'null value in column "%s.%s" violates not-null constraint' % ( | ||||||
|                     self.name, |                     self.name, | ||||||
|   | |||||||
							
								
								
									
										151
									
								
								localized_fields/fields/localized_file_field.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								localized_fields/fields/localized_file_field.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,151 @@ | |||||||
|  | import datetime | ||||||
|  | import posixpath | ||||||
|  |  | ||||||
|  | from django.core.files import File | ||||||
|  | from django.db.models.fields.files import FieldFile | ||||||
|  | from django.utils import six | ||||||
|  | from django.core.files.storage import default_storage | ||||||
|  | from django.utils.encoding import force_str, force_text | ||||||
|  |  | ||||||
|  | from localized_fields.fields import LocalizedField | ||||||
|  | from localized_fields.fields.localized_field import LocalizedValueDescriptor | ||||||
|  | from localized_fields.localized_value import LocalizedValue | ||||||
|  |  | ||||||
|  | from ..localized_value import LocalizedFileValue | ||||||
|  | from ..forms import LocalizedFileFieldForm | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedFieldFile(FieldFile): | ||||||
|  |     def __init__(self, instance, field, name): | ||||||
|  |         super(FieldFile, self).__init__(None, name) | ||||||
|  |         self.instance = instance | ||||||
|  |         self.field = field | ||||||
|  |         self.storage = field.storage | ||||||
|  |         self._committed = True | ||||||
|  |  | ||||||
|  |     def save(self, name, content, lang, save=True): | ||||||
|  |         name = self.field.generate_filename(self.instance, name, lang) | ||||||
|  |         self.name = self.storage.save(name, content, | ||||||
|  |                                       max_length=self.field.max_length) | ||||||
|  |         self._committed = True | ||||||
|  |  | ||||||
|  |         if save: | ||||||
|  |             self.instance.save() | ||||||
|  |  | ||||||
|  |     save.alters_data = True | ||||||
|  |  | ||||||
|  |     def delete(self, save=True): | ||||||
|  |         if not self: | ||||||
|  |             return | ||||||
|  |  | ||||||
|  |         if hasattr(self, '_file'): | ||||||
|  |             self.close() | ||||||
|  |             del self.file | ||||||
|  |  | ||||||
|  |         self.storage.delete(self.name) | ||||||
|  |  | ||||||
|  |         self.name = None | ||||||
|  |         self._committed = False | ||||||
|  |  | ||||||
|  |         if save: | ||||||
|  |             self.instance.save() | ||||||
|  |  | ||||||
|  |     delete.alters_data = True | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedFileValueDescriptor(LocalizedValueDescriptor): | ||||||
|  |     def __get__(self, instance, cls=None): | ||||||
|  |         value = super().__get__(instance, cls) | ||||||
|  |         for k, file in value.__dict__.items(): | ||||||
|  |             if isinstance(file, six.string_types) or file is None: | ||||||
|  |                 file = self.field.value_class(instance, self.field, file) | ||||||
|  |                 value.set(k, file) | ||||||
|  |  | ||||||
|  |             elif isinstance(file, File) and \ | ||||||
|  |                     not isinstance(file, LocalizedFieldFile): | ||||||
|  |                 file_copy = self.field.value_class(instance, self.field, | ||||||
|  |                                                    file.name) | ||||||
|  |                 file_copy.file = file | ||||||
|  |                 file_copy._committed = False | ||||||
|  |                 value.set(k, file_copy) | ||||||
|  |  | ||||||
|  |             elif isinstance(file, LocalizedFieldFile) and \ | ||||||
|  |                     not hasattr(file, 'field'): | ||||||
|  |                 file.instance = instance | ||||||
|  |                 file.field = self.field | ||||||
|  |                 file.storage = self.field.storage | ||||||
|  |  | ||||||
|  |             # Make sure that the instance is correct. | ||||||
|  |             elif isinstance(file, LocalizedFieldFile) \ | ||||||
|  |                     and instance is not file.instance: | ||||||
|  |                 file.instance = instance | ||||||
|  |         return value | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedFileField(LocalizedField): | ||||||
|  |     descriptor_class = LocalizedFileValueDescriptor | ||||||
|  |     attr_class = LocalizedFileValue | ||||||
|  |     value_class = LocalizedFieldFile | ||||||
|  |  | ||||||
|  |     def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, | ||||||
|  |                  **kwargs): | ||||||
|  |  | ||||||
|  |         self.storage = storage or default_storage | ||||||
|  |         self.upload_to = upload_to | ||||||
|  |  | ||||||
|  |         super().__init__(verbose_name, name, **kwargs) | ||||||
|  |  | ||||||
|  |     def deconstruct(self): | ||||||
|  |         name, path, args, kwargs = super(LocalizedFileField, self).deconstruct() | ||||||
|  |         kwargs['upload_to'] = self.upload_to | ||||||
|  |         if self.storage is not default_storage: | ||||||
|  |             kwargs['storage'] = self.storage | ||||||
|  |         return name, path, args, kwargs | ||||||
|  |  | ||||||
|  |     def get_prep_value(self, value): | ||||||
|  |         """Returns field's value prepared for saving into a database.""" | ||||||
|  |  | ||||||
|  |         if isinstance(value, LocalizedValue): | ||||||
|  |             prep_value = LocalizedValue() | ||||||
|  |             for k, v in value.__dict__.items(): | ||||||
|  |                 if v is None: | ||||||
|  |                     prep_value.set(k, '') | ||||||
|  |                 else: | ||||||
|  |                     # Need to convert File objects provided via a form to | ||||||
|  |                     # unicode for database insertion | ||||||
|  |                     prep_value.set(k, six.text_type(v)) | ||||||
|  |             return super().get_prep_value(prep_value) | ||||||
|  |         return super().get_prep_value(value) | ||||||
|  |  | ||||||
|  |     def pre_save(self, model_instance, add): | ||||||
|  |         """Returns field's value just before saving.""" | ||||||
|  |         value = super().pre_save(model_instance, add) | ||||||
|  |         if isinstance(value, LocalizedValue): | ||||||
|  |             for lang, file in value.__dict__.items(): | ||||||
|  |                 if file and not file._committed: | ||||||
|  |                     file.save(file.name, file, lang, save=False) | ||||||
|  |         return value | ||||||
|  |  | ||||||
|  |     def generate_filename(self, instance, filename, lang): | ||||||
|  |         if callable(self.upload_to): | ||||||
|  |             filename = self.upload_to(instance, filename, lang) | ||||||
|  |         else: | ||||||
|  |             now = datetime.datetime.now() | ||||||
|  |             dirname = force_text(now.strftime(force_str(self.upload_to))) | ||||||
|  |             dirname = dirname.format(lang=lang) | ||||||
|  |             filename = posixpath.join(dirname, filename) | ||||||
|  |         return self.storage.generate_filename(filename) | ||||||
|  |  | ||||||
|  |     def save_form_data(self, instance, data): | ||||||
|  |         if isinstance(data, LocalizedValue): | ||||||
|  |             for k, v in data.__dict__.items(): | ||||||
|  |                 if v is not None and not v: | ||||||
|  |                     data.set(k, '') | ||||||
|  |             setattr(instance, self.attname, data) | ||||||
|  |  | ||||||
|  |     def formfield(self, **kwargs): | ||||||
|  |         defaults = {'form_class': LocalizedFileFieldForm} | ||||||
|  |         if 'initial' in kwargs: | ||||||
|  |             defaults['required'] = False | ||||||
|  |         defaults.update(kwargs) | ||||||
|  |         return super().formfield(**defaults) | ||||||
							
								
								
									
										14
									
								
								localized_fields/fields/localized_text_field.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								localized_fields/fields/localized_text_field.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | from ..forms import LocalizedTextFieldForm | ||||||
|  | from .localized_char_field import LocalizedCharField | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedTextField(LocalizedCharField): | ||||||
|  |     def formfield(self, **kwargs): | ||||||
|  |         """Gets the form field associated with this field.""" | ||||||
|  |  | ||||||
|  |         defaults = { | ||||||
|  |             'form_class': LocalizedTextFieldForm | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         defaults.update(kwargs) | ||||||
|  |         return super().formfield(**defaults) | ||||||
| @@ -2,10 +2,13 @@ from typing import List | |||||||
|  |  | ||||||
| from django import forms | from django import forms | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
|  | from django.core.exceptions import ValidationError | ||||||
|  | from django.forms.widgets import FILE_INPUT_CONTRADICTION | ||||||
|  |  | ||||||
| from .localized_value import LocalizedValue | from .localized_value import LocalizedValue, LocalizedStingValue, \ | ||||||
| from .widgets import LocalizedFieldWidget |     LocalizedFileValue | ||||||
|  | from .widgets import LocalizedFieldWidget, LocalizedCharFieldWidget, \ | ||||||
|  |     LocalizedFileWidget | ||||||
|  |  | ||||||
|  |  | ||||||
| class LocalizedFieldForm(forms.MultiValueField): | class LocalizedFieldForm(forms.MultiValueField): | ||||||
| @@ -13,6 +16,7 @@ class LocalizedFieldForm(forms.MultiValueField): | |||||||
|     the field in multiple languages.""" |     the field in multiple languages.""" | ||||||
|  |  | ||||||
|     widget = LocalizedFieldWidget |     widget = LocalizedFieldWidget | ||||||
|  |     field_class = forms.fields.CharField | ||||||
|     value_class = LocalizedValue |     value_class = LocalizedValue | ||||||
|  |  | ||||||
|     def __init__(self, *args, **kwargs): |     def __init__(self, *args, **kwargs): | ||||||
| @@ -27,7 +31,7 @@ class LocalizedFieldForm(forms.MultiValueField): | |||||||
|                 field_options['required'] = kwargs.get('required', True) |                 field_options['required'] = kwargs.get('required', True) | ||||||
|  |  | ||||||
|             field_options['label'] = lang_code |             field_options['label'] = lang_code | ||||||
|             fields.append(forms.fields.CharField(**field_options)) |             fields.append(self.field_class(**field_options)) | ||||||
|  |  | ||||||
|         super(LocalizedFieldForm, self).__init__( |         super(LocalizedFieldForm, self).__init__( | ||||||
|             fields, |             fields, | ||||||
| @@ -57,3 +61,106 @@ class LocalizedFieldForm(forms.MultiValueField): | |||||||
|             localized_value.set(lang_code, value) |             localized_value.set(lang_code, value) | ||||||
|  |  | ||||||
|         return localized_value |         return localized_value | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedCharFieldForm(LocalizedFieldForm): | ||||||
|  |     """Form for a localized char field, allows editing | ||||||
|  |     the field in multiple languages.""" | ||||||
|  |  | ||||||
|  |     widget = LocalizedCharFieldWidget | ||||||
|  |     value_class = LocalizedStingValue | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedTextFieldForm(LocalizedFieldForm): | ||||||
|  |     """Form for a localized text field, allows editing | ||||||
|  |     the field in multiple languages.""" | ||||||
|  |  | ||||||
|  |     value_class = LocalizedStingValue | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedFileFieldForm(LocalizedFieldForm, forms.FileField): | ||||||
|  |     """Form for a localized file field, allows editing | ||||||
|  |     the field in multiple languages.""" | ||||||
|  |  | ||||||
|  |     widget = LocalizedFileWidget | ||||||
|  |     field_class = forms.fields.FileField | ||||||
|  |     value_class = LocalizedFileValue | ||||||
|  |  | ||||||
|  |     def clean(self, value, initial=None): | ||||||
|  |         """ | ||||||
|  |         Most part of this method is a copy of  | ||||||
|  |         django.forms.MultiValueField.clean, with the exception of initial | ||||||
|  |         value handling (this need for correct processing FileField's). | ||||||
|  |         All original comments saved. | ||||||
|  |         """ | ||||||
|  |         if initial is None: | ||||||
|  |             initial = [None for x in range(0, len(value))] | ||||||
|  |         else: | ||||||
|  |             if not isinstance(initial, list): | ||||||
|  |                 initial = self.widget.decompress(initial) | ||||||
|  |  | ||||||
|  |         clean_data = [] | ||||||
|  |         errors = [] | ||||||
|  |         if not value or isinstance(value, (list, tuple)): | ||||||
|  |             if (not value or not [v for v in value if | ||||||
|  |                                   v not in self.empty_values]) \ | ||||||
|  |                     and (not initial or not [v for v in initial if | ||||||
|  |                                              v not in self.empty_values]): | ||||||
|  |                 if self.required: | ||||||
|  |                     raise ValidationError(self.error_messages['required'], | ||||||
|  |                                           code='required') | ||||||
|  |         else: | ||||||
|  |             raise ValidationError(self.error_messages['invalid'], | ||||||
|  |                                   code='invalid') | ||||||
|  |         for i, field in enumerate(self.fields): | ||||||
|  |             try: | ||||||
|  |                 field_value = value[i] | ||||||
|  |             except IndexError: | ||||||
|  |                 field_value = None | ||||||
|  |             try: | ||||||
|  |                 field_initial = initial[i] | ||||||
|  |             except IndexError: | ||||||
|  |                 field_initial = None | ||||||
|  |             if field_value in self.empty_values and \ | ||||||
|  |                             field_initial in self.empty_values: | ||||||
|  |                 if self.require_all_fields: | ||||||
|  |                     # Raise a 'required' error if the MultiValueField is | ||||||
|  |                     # required and any field is empty. | ||||||
|  |                     if self.required: | ||||||
|  |                         raise ValidationError(self.error_messages['required'], | ||||||
|  |                                               code='required') | ||||||
|  |                 elif field.required: | ||||||
|  |                     # Otherwise, add an 'incomplete' error to the list of | ||||||
|  |                     # collected errors and skip field cleaning, if a required | ||||||
|  |                     # field is empty. | ||||||
|  |                     if field.error_messages['incomplete'] not in errors: | ||||||
|  |                         errors.append(field.error_messages['incomplete']) | ||||||
|  |                     continue | ||||||
|  |             try: | ||||||
|  |                 clean_data.append(field.clean(field_value, field_initial)) | ||||||
|  |             except ValidationError as e: | ||||||
|  |                 # Collect all validation errors in a single list, which we'll | ||||||
|  |                 # raise at the end of clean(), rather than raising a single | ||||||
|  |                 # exception for the first error we encounter. Skip duplicates. | ||||||
|  |                 errors.extend(m for m in e.error_list if m not in errors) | ||||||
|  |         if errors: | ||||||
|  |             raise ValidationError(errors) | ||||||
|  |  | ||||||
|  |         out = self.compress(clean_data) | ||||||
|  |         self.validate(out) | ||||||
|  |         self.run_validators(out) | ||||||
|  |         return out | ||||||
|  |  | ||||||
|  |     def bound_data(self, data, initial): | ||||||
|  |         bound_data = [] | ||||||
|  |         if initial is None: | ||||||
|  |             initial = [None for x in range(0, len(data))] | ||||||
|  |         else: | ||||||
|  |             if not isinstance(initial, list): | ||||||
|  |                 initial = self.widget.decompress(initial) | ||||||
|  |         for d, i in zip(data, initial): | ||||||
|  |             if d in (None, FILE_INPUT_CONTRADICTION): | ||||||
|  |                 bound_data.append(i) | ||||||
|  |             else: | ||||||
|  |                 bound_data.append(d) | ||||||
|  |         return bound_data | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ from django.utils import translation | |||||||
|  |  | ||||||
| class LocalizedValue(dict): | class LocalizedValue(dict): | ||||||
|     """Represents the value of a :see:LocalizedField.""" |     """Represents the value of a :see:LocalizedField.""" | ||||||
|  |     default_value = None | ||||||
|  |  | ||||||
|     def __init__(self, keys: dict=None): |     def __init__(self, keys: dict=None): | ||||||
|         """Initializes a new instance of :see:LocalizedValue. |         """Initializes a new instance of :see:LocalizedValue. | ||||||
| @@ -15,12 +16,15 @@ class LocalizedValue(dict): | |||||||
|                 different language. |                 different language. | ||||||
|         """ |         """ | ||||||
|  |  | ||||||
|  |         # NOTE(seroy): First fill all the keys with default value, | ||||||
|  |         # in order to attributes will be for each language | ||||||
|  |         for lang_code, _ in settings.LANGUAGES: | ||||||
|  |             value = keys.get(lang_code) if isinstance(keys, dict) else \ | ||||||
|  |                 self.default_value | ||||||
|  |             self.set(lang_code, value) | ||||||
|  |  | ||||||
|         if isinstance(keys, str): |         if isinstance(keys, str): | ||||||
|             setattr(self, settings.LANGUAGE_CODE, keys) |             setattr(self, settings.LANGUAGE_CODE, keys) | ||||||
|         else: |  | ||||||
|             for lang_code, _ in settings.LANGUAGES: |  | ||||||
|                 value = keys.get(lang_code) if keys else None |  | ||||||
|                 self.set(lang_code, value) |  | ||||||
|  |  | ||||||
|     def get(self, language: str=None) -> str: |     def get(self, language: str=None) -> str: | ||||||
|         """Gets the underlying value in the specified or |         """Gets the underlying value in the specified or | ||||||
| @@ -62,7 +66,7 @@ class LocalizedValue(dict): | |||||||
|             contained in this instance. |             contained in this instance. | ||||||
|         """ |         """ | ||||||
|  |  | ||||||
|         path = 'localized_fields.fields.LocalizedValue' |         path = 'localized_fields.localized_value.%s' % self.__class__.__name__ | ||||||
|         return path, [self.__dict__], {} |         return path, [self.__dict__], {} | ||||||
|  |  | ||||||
|     def __str__(self) -> str: |     def __str__(self) -> str: | ||||||
| @@ -124,4 +128,25 @@ class LocalizedValue(dict): | |||||||
|     def __repr__(self):  # pragma: no cover |     def __repr__(self):  # pragma: no cover | ||||||
|         """Gets a textual representation of this object.""" |         """Gets a textual representation of this object.""" | ||||||
|  |  | ||||||
|         return 'LocalizedValue<%s> 0x%s' % (dict(self), id(self)) |         return '%s<%s> 0x%s' % (self.__class__.__name__, | ||||||
|  |                                 self.__dict__, id(self)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedStingValue(LocalizedValue): | ||||||
|  |     default_value = '' | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedFileValue(LocalizedValue): | ||||||
|  |  | ||||||
|  |     def __getattr__(self, name): | ||||||
|  |         value = self.get(translation.get_language()) | ||||||
|  |         if hasattr(value, name): | ||||||
|  |             return getattr(value, name) | ||||||
|  |         raise AttributeError("'{}' object has no attribute '{}'". | ||||||
|  |                              format(self.__class__.__name__, name)) | ||||||
|  |  | ||||||
|  |     def __str__(self): | ||||||
|  |         return str(super().__str__()) | ||||||
|  |  | ||||||
|  |     def localized(self): | ||||||
|  |         return self.get(translation.get_language()) | ||||||
|   | |||||||
| @@ -46,6 +46,16 @@ class LocalizedFieldWidget(forms.MultiWidget): | |||||||
|         return result |         return result | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedCharFieldWidget(LocalizedFieldWidget): | ||||||
|  |     """Widget that has an input box for every language.""" | ||||||
|  |     widget = forms.TextInput | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LocalizedFileWidget(LocalizedFieldWidget): | ||||||
|  |     """Widget that has an file input box for every language.""" | ||||||
|  |     widget = forms.ClearableFileInput | ||||||
|  |  | ||||||
|  |  | ||||||
| class AdminLocalizedFieldWidget(LocalizedFieldWidget): | class AdminLocalizedFieldWidget(LocalizedFieldWidget): | ||||||
|     widget = widgets.AdminTextareaWidget |     widget = widgets.AdminTextareaWidget | ||||||
|     template = 'localized_fields/admin/widget.html' |     template = 'localized_fields/admin/widget.html' | ||||||
| @@ -84,3 +94,11 @@ class AdminLocalizedFieldWidget(LocalizedFieldWidget): | |||||||
|                 and 'required' in attrs: |                 and 'required' in attrs: | ||||||
|             del attrs['required'] |             del attrs['required'] | ||||||
|         return attrs |         return attrs | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class AdminLocalizedCharFieldWidget(AdminLocalizedFieldWidget): | ||||||
|  |     widget = widgets.AdminTextInputWidget | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class AdminLocalizedFileFieldWidget(AdminLocalizedFieldWidget): | ||||||
|  |     widget = widgets.AdminFileWidget | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user