123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- # coding=utf-8
- from __future__ import absolute_import
- from __future__ import division
- from __future__ import print_function
- from __future__ import unicode_literals
- import collections
- from django import forms
- from django.db import models
- from django.conf import settings
- from django.contrib.contenttypes.models import ContentType
- from django.core.exceptions import ValidationError
- from django.db import transaction
- from django.utils.encoding import force_text
- from datetimewidget import widgets as dtwidgets
- from rapid.widgets import RapidSelector, rapid_alternatives_widget, rapid_dependent_widget, \
- getRapidReadonlyRelation
- from rapid.wrappers import FieldData, ModelData
- import gettext
- _ = gettext.gettext
- class RapidAlternativesField(forms.Field):
- """
- Form field for AlternativeData.
- """
- def __init__(self, field_name, alternatives, selector_name, form, request, instance=None, *args, **kwargs):
- model = None
- if instance:
- model = getattr(instance, selector_name)
- if form.base_fields[selector_name].initial:
- model = ContentType.objects.get_for_id(form.base_fields[selector_name].initial)
- alt = []
- for a in alternatives:
- md = ModelData(a)
- ft = for_model(request, a)
- prefix = field_name + '_' + str(md.content_type().pk)
- selected = ContentType.objects.get_for_model(a) == model
- inst = getattr(instance, field_name) if selected else None
- if request.method == 'POST':
- fm = ft(request.POST, request.FILES, prefix=prefix, instance=inst)
- else:
- fm = ft(prefix=prefix, instance=inst)
- alt.append((md.content_type().pk, (md, fm, selected)))
- alt = dict(alt)
- kwargs['widget'] = rapid_alternatives_widget(alt, selector_name)
- # noinspection PyArgumentList
- super(RapidAlternativesField, self).__init__(field_name, *args, **kwargs)
- def to_python(self, value):
- if value is None:
- return None
- if value.is_valid():
- obj = value.save(commit=False)
- obj.save_m2m = value.save_m2m
- return obj
- raise ValidationError(_("Invalid value"), code='invalid')
- class RapidDependentField(forms.Field):
- """
- Form fields for dependent instances.
- This field will be displayed for reverse relations where the
- foreign key of this model is required. That is, for related models
- that can not exist unless when related to this one.
- """
- def __init__(self, field, originator, model, request, instance=None, multiple=False, *args, **kwargs):
- self.multiple = multiple
- md = ModelData(model)
- prefix = field.bare_name() + '_' + str(md.content_type().pk)
- if multiple:
- ftt = for_model(request, model, [(field.related_field().bare_name(), originator)])
- ft = forms.modelformset_factory(model, form=ftt)
- else:
- ft = for_model(request, model, [(field.related_field().bare_name(), originator)])
- if request.method == 'POST':
- if multiple:
- fm = ft(request.POST, request.FILES, prefix=prefix, queryset=instance)
- else:
- fm = ft(request.POST, request.FILES, prefix=prefix, instance=instance)
- else:
- if multiple:
- fm = ft(prefix=prefix, queryset=instance)
- else:
- fm = ft(instance=instance, prefix=prefix)
- kwargs['widget'] = rapid_dependent_widget(md, fm, bool(instance), multiple)
- # noinspection PyArgumentList
- super(RapidDependentField, self).__init__(field, *args, **kwargs)
- self.required = False
- def to_python(self, value):
- if self.multiple:
- (f, ii) = value
- if f is None: return None
- if f.is_valid():
- class ValList:
- def __init__(self, vals):
- self.vals = vals
- def save_m2m(self):
- for v in self.vals:
- v.save_m2m()
- def __iter__(self):
- return self.vals.__iter__()
- objs = [x.save(commit=False) for x in f.forms]
- return ValList([objs[i] for i in ii])
- else:
- if value is None: return None
- if value.is_valid():
- obj = value.save(commit=False)
- obj.save_m2m = value.save_m2m
- return obj
- raise ValidationError(_("Invalid value"), code='invalid')
- def for_model(request, model, default_relations=()):
- hasDtWidgets = 'datetimewidget' in settings.INSTALLED_APPS
- default_relations = list(default_relations)
- default_relations_request = request.GET.get('default')
- widgets = []
- default_relations_fields = []
- if default_relations_request:
- default_relations_fields = default_relations_request.split(",")
- default_relations += ((x, int(y)) for (x, y) in (f.split(":") for f in default_relations_fields))
- default_relations_fields = [x for x, y in default_relations]
- for (x, y) in default_relations:
- f = FieldData(getattr(model, x).field, request)
- widgets.append((x, getRapidReadonlyRelation(f.related_model().model, y)))
- default_relations_fields.append(x)
- for f in ModelData(model).local_fields():
- if f.is_relation():
- if force_text(f.bare_name()) not in default_relations_fields and \
- f.related_model().has_permission(request, 'select'):
- widgets.append((f.bare_name(), RapidSelector(f)))
- elif force_text(f.bare_name()) in default_relations_fields:
- pass
- if type(f.field) == models.DateField:
- widgets.append((f.bare_name(), dtwidgets.DateWidget(usel10n = True, bootstrap_version=3)))
- if type(f.field) == models.DateTimeField:
- widgets.append((f.bare_name(), dtwidgets.DateTimeWidget(usel10n = True, bootstrap_version=3)))
- if type(f.field) == models.TimeField:
- widgets.append((f.bare_name(), dtwidgets.TimeWidget(usel10n = True, bootstrap_version=3)))
- #if force_text(model._meta.db_table) == 'testproject_singledepontest1':
- # import pdb; pdb.set_trace()
- # ModelForm.Meta has attributes with the same names, thus I'll rename them
- form_model = model
- form_widgets = dict(widgets)
- # noinspection PyTypeChecker
- class CForm(forms.ModelForm):
- def __init__(self, *args, **kwargs):
- # Sets alternative data:
- initial = kwargs.get('initial', {})
- for (k, v) in default_relations:
- initial[k] = v
- if initial:
- kwargs['initial'] = initial
- instance = kwargs.get('instance')
- for n, fd in ModelData(model).rapid_alternative_data():
- ct = ModelData(model).field_by_name(fd.ct_field).field
- fk = ModelData(model).field_by_name(fd.fk_field).field
- # noinspection PyTypeChecker
- fl = RapidAlternativesField(n, ct.alternatives, ct.name, self, request, instance)
- # noinspection PyArgumentList
- type(self.__class__).__setattr__(self.__class__, n, fl)
- nd = collections.OrderedDict()
- for k, v in self.__class__.base_fields.items():
- if k == fk.name:
- nd[n] = fl
- else:
- nd[k] = v
- self.__class__.base_fields = nd
- # Sets fields for reverse relations:
- for f in ModelData(model).related_fields():
- if f.is_existential_dependency():
- md = f.related_model().model
- ft = {f.related_field().bare_name(): instance}
- dt = md.objects.filter(**ft)
- if f.is_multiple():
- fl = RapidDependentField(f, instance, md, request, dt, True)
- else:
- o = dt[0] if dt else None
- fl = RapidDependentField(f, instance, md, request,
- o, False)
- self.__class__.base_fields[f.bare_name()] = fl
- super(CForm, self).__init__(*args, **kwargs)
- @transaction.atomic
- def save(self, commit=True):
- if not commit:
- return super(CForm, self).save(commit)
- else:
- obj = super(CForm, self).save(commit=False)
- # Saving alternative data:
- for n, fd in ModelData(model).rapid_alternative_data():
- if self.instance:
- old_t = getattr(self.instance, fd.ct_field)
- new_t = getattr(obj, fd.ct_field)
- if old_t != new_t:
- getattr(self.instance, fd.bare_name).delete()
- fob = self.cleaned_data[n]
- fob.save()
- if hasattr(fob, 'save_m2m'):
- fob.save_m2m()
- setattr(obj, fd.fk_field, fob.pk)
- obj.save()
- # Saving reverse relations:
- for f in ModelData(model).related_fields():
- if f.is_existential_dependency():
- fob = self.cleaned_data[f.bare_name()]
- ft = {f.related_field().bare_name(): obj}
- if fob:
- if f.is_multiple():
- ex = {'pk__in': [ob.pk for ob in fob if ob.pk]}
- f.related_model().model.objects.filter(**ft).exclude(**ex).delete()
- for ob in fob:
- setattr(ob, f.related_field().bare_name(), obj)
- ob.save()
- if hasattr(ob, 'save_m2m'):
- ob.save_m2m()
- else:
- ex = {'pk__exact': fob.pk}
- f.related_model().model.objects.filter(**ft).exclude(**ex).delete()
- setattr(fob, f.related_field().bare_name(), obj)
- fob.save()
- if hasattr(fob, 'save_m2m'):
- fob.save_m2m()
- else:
- f.related_model().model.objects.filter(**ft).delete()
- self.save_m2m()
- return obj
- class Meta(object):
- model = form_model
- fields = '__all__'
- widgets = form_widgets
- return CForm
|