# -*- coding: utf-8 -*- import re from calendar import TimeEncoding, month_name from datetime import date from django import forms from django.conf import settings from django.core import exceptions, validators from django.core.serializers.json import DjangoJSONEncoder from django.db import models from django.utils.safestring import mark_safe from django.utils.text import capfirst from django.utils.translation import ugettext_lazy as _ from south.modelsinspector import add_introspection_rules try: import json except: from django.utils import simplejson as json FILE_TYPES = ('PDF', 'DOC', 'TXT', 'OTHER') IMG_TYPES = ('JPG', 'BMP', 'PNG', 'GIF',) PURPOSES = ('photo', 'flat') # dont delete class EnumField(models.Field): """ A field class that maps to MySQL's ENUM type. Usage: Class Card(models.Model): suit = EnumField(values=('Clubs', 'Diamonds', 'Spades', 'Hearts')) c = Card() c.suit = 'Clubs' c.save() """ def __init__(self, *args, **kwargs): self.values = kwargs.pop('values') kwargs['choices'] = [(v, v) for v in self.values] kwargs['default'] = self.values[0] super(EnumField, self).__init__(*args, **kwargs) def db_type(self, connection): return "enum({0})".format( ','.join("'%s'" % v for v in self.values) ) #snippet http://djangosnippets.org/snippets/1478/ class JSONField(models.TextField): """JSONField is a generic textfield that neatly serializes/unserializes JSON objects seamlessly""" # Used so to_python() is called __metaclass__ = models.SubfieldBase def to_python(self, value): """Convert our string value to JSON after we load it from the DB""" if not isinstance(value, basestring): return value try: return json.loads(value) except ValueError, e: # If string could not parse as JSON it's means that it's Python # string saved to JSONField. return value def get_db_prep_value(self, value, connection, prepared=True): """Convert our JSON object to a string before we save""" return json.dumps(value) ''' def get_db_prep_save(self, value, connection): """Convert our JSON object to a string before we save""" if value == "": return None if isinstance(value, dict): value = json.dumps(value, cls=DjangoJSONEncoder) return value ''' DEFAULT_WIDTH = 590 DEFAULT_HEIGHT = 200 DEFAULT_LAT = 55.75 DEFAULT_LNG = 37.62 DEFAULT_ADDRESS = u'(Не задано)' class LocationWidget(forms.TextInput): def __init__(self, *args, **kw): self.map_width = kw.get("map_width", DEFAULT_WIDTH) self.map_height = kw.get("map_height", DEFAULT_HEIGHT) super(LocationWidget, self).__init__(*args, **kw) self.inner_widget = forms.widgets.HiddenInput() def render(self, name, value, *args, **kwargs): """ handle on the rendering on the server """ if value is None or value==u'': lat, lng, address = DEFAULT_LAT, DEFAULT_LNG, DEFAULT_ADDRESS value = {'lat': lat, 'lng': lng, 'address': address} else: if isinstance(value, basestring): a = json.loads(value) lat, lng, address = float(a['lat']), float(a['lng']), a['address'] else: lat, lng, address = float(value['lat']), float(value['lng']), value['address'] if isinstance(value, basestring): curLocation = value else: curLocation = json.dumps(value, cls=DjangoJSONEncoder) js = ''' ''' % dict(functionName=name.replace('-', '_'), name=name, lat=lat, lng=lng, defAddress=DEFAULT_ADDRESS) html = self.inner_widget.render("%s" % name, "%s" % curLocation, dict(id='id_%s' % name)) html += '
' % ( name) html += '
' % (name, self.map_width, self.map_height) html += '
%s' % (u'Текущий адрес', address) return mark_safe(js + html) class Media: css = {'all': ( 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/redmond/jquery-ui.css', settings.MEDIA_URL+'css/main.css', )} js = ( 'http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js', 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js', 'http://maps.google.com/maps/api/js?sensor=false', ) class LocationField(JSONField): def formfield(self, **kwargs): defaults = {'widget': LocationWidget} return super(LocationField, self).formfield(**defaults) _regex_validator = validators.RegexValidator( regex=re.compile('^\[(u(\'|\")([1-9]|1[0-2])/[0-9]{4}(\'|\"),?\s?)+\]$')) class MonthMultiSelectField(models.CharField): __metaclass__ = models.SubfieldBase _saved_month_names = None _saved_choices_month = None _saved_choices = None _saved_choices_values = None _inst_saved_choices = set() _inst_saved_choices_values = set() def __init__(self, *args, **kwargs): # FIXME: need to reset if choices are given # _saved_choices # _saved_choices_values # _saved_choices_month choices = kwargs.pop('choices', None) self.value_type = kwargs.pop('value_type', None) super(MonthMultiSelectField, self).__init__(*args, **kwargs) self.validators.append(_regex_validator) if choices is None: self._choices = self.make_choices() @property def flatchoices(self): return None @classmethod def get_month_names(cls, month_num, locale=('ru_RU', 'UTF-8')): '''Usage: get_months_names(1, ('ru_RU', 'UTF-8')) ''' with TimeEncoding(locale) as encoding: if not isinstance(cls._saved_month_names, list): cls._saved_month_names = list(month_name) m = cls._saved_month_names[month_num].decode(encoding or 'utf-8') return m @classmethod def make_choice_display(cls, m, y): return _(u'%(month)s %(year)s') % {'month': cls.get_month_names(m), 'year': y} @staticmethod def get_data_from_string(s): p = re.compile('^(?P([1-9]|[1][0-2]))/(?P[0-9]{4})$') m = p.match(s) return m.group('month'), m.group('year') @classmethod def get_month_from_string_display(cls, s): return cls.make_choice_display(*self.get_month_from_string(s)) @classmethod def _make_choice_map_func(cls, *args): c = ('{m}/{y}'.format(m=args[0], y=args[1]), cls.make_choice_display(*args)) cls._saved_choices_values.add(c[0]) return c @classmethod def _make_choices(cls): today = date.today() #years = [today.year, today.year + 1] # map(lambda x: today.year + x, xrange(0, 2)) cls._saved_choices_month == today.replace(day=1) choices = [] cls._saved_choices_values = set() for year in [today.year, today.year + 1]: year_choices = map( lambda m: cls._make_choice_map_func(m, year), xrange(today.month if year == today.year else 1, 13) ) choices.append((year, year_choices)) return choices @classmethod def _check_relevance(cls): return cls._saved_choices_month == date.today().replace(day=1) def make_choices(self): if self._saved_choices is not None and self._check_relevance(): return self._saved_choices return self._make_choices() @property def flatchoices(self): return None def get_choices_default(self): return self.get_choices(include_blank=False) def value_to_string(self, obj): value = self._get_val_from_obj(obj) return self.get_prep_value(value) def validate(self, value, model_instance): if not self.editable: # Skip validation for non-editable fields. return for opt_select in value: if not self.valid_value(opt_select): if django.VERSION[0] >= 1 and django.VERSION[1] >= 6: raise exceptions.ValidationError(self.error_messages['invalid_choice'] % {"value": value}) else: raise exceptions.ValidationError(self.error_messages['invalid_choice'] % value) def valid_value(self, value): "Check to see if the provided value is a valid choice" if value in self._saved_choices_values: return True return False def formfield(self, **kwargs): """ Returns a django.forms.Field instance for this database Field. """ defaults = { 'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text, 'initial': self.get_default(), 'validators': [_regex_validator], } defaults.update(kwargs) return forms.MultipleChoiceField(**defaults) def get_prep_value(self, value): if value in validators.EMPTY_VALUES: return self.get_default() return ",".join(value) def to_python(self, value): if any([value is None, not value]): return [] else: if isinstance(value, set): value = sorted(list(value)) value_list = value if isinstance(value, list) else value.split(',') if self.value_type is not None: value_list = map(self.value_type, value_list) return value_list def contribute_to_class(self, cls, name): super(MonthMultiSelectField, self).contribute_to_class(cls, name) if self.choices: def get_list(obj): fieldname = name choicedict = dict(self.choices) display = [] values = getattr(obj, fieldname) if values: display = map( lambda x: self.get_month_from_string_display(x), values) return display def get_display(obj): return ", ".join(get_list(obj)) setattr(cls, 'get_%s_list' % self.name, get_list) setattr(cls, 'get_%s_display' % self.name, get_display) add_introspection_rules([ ( [EnumField], # Class(es) these apply to [], # Positional arguments (not used) { # Keyword argument "values": ["values", {'default': None}], }, ), ], ["^functions\.custom_fields\.EnumField", "^functions\.custom_fields\.LocationField"]) add_introspection_rules([], ["^functions\.custom_fields\.MonthMultiSelectField"])