~cmiller/ubuntu/quantal/deluge/fix-parameter-move-storage

« back to all changes in this revision

Viewing changes to deluge/ui/webui/lib/newforms_portable/models.py

  • Committer: Bazaar Package Importer
  • Author(s): Cristian Greco
  • Date: 2009-11-13 02:39:45 UTC
  • mfrom: (4.1.7 squeeze)
  • Revision ID: james.westby@ubuntu.com-20091113023945-te1bybo2912ejzuc
Tags: 1.2.0~rc3-4
* debian/control: bump build-dep on python-setuptools to (>= 0.6c9).
* debian/patches:
  - 25_r5921_fastresume_files.patch
    new, should fix problems with fresh configs;
  - 30_r5931_ipc_lockfile.patch:
    new, should fix an issue where Deluge will fail to start if there is a
    stale ipc lockfile. (Closes: #555849)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
"""
2
 
Helper functions for creating Form classes from Django models
3
 
and database field objects.
4
 
"""
5
 
 
6
 
from warnings import warn
7
 
 
8
 
from django.utils.translation import ugettext_lazy as _
9
 
from django.utils.encoding import smart_unicode
10
 
from django.utils.datastructures import SortedDict
11
 
from django.core.exceptions import ImproperlyConfigured
12
 
 
13
 
from util import ValidationError, ErrorList
14
 
from forms import BaseForm, get_declared_fields
15
 
from fields import Field, ChoiceField, EMPTY_VALUES
16
 
from widgets import Select, SelectMultiple, MultipleHiddenInput
17
 
 
18
 
__all__ = (
19
 
    'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model',
20
 
    'save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields',
21
 
    'ModelChoiceField', 'ModelMultipleChoiceField'
22
 
)
23
 
 
24
 
def save_instance(form, instance, fields=None, fail_message='saved',
25
 
                  commit=True):
26
 
    """
27
 
    Saves bound Form ``form``'s cleaned_data into model instance ``instance``.
28
 
 
29
 
    If commit=True, then the changes to ``instance`` will be saved to the
30
 
    database. Returns ``instance``.
31
 
    """
32
 
    from django.db import models
33
 
    opts = instance.__class__._meta
34
 
    if form.errors:
35
 
        raise ValueError("The %s could not be %s because the data didn't"
36
 
                         " validate." % (opts.object_name, fail_message))
37
 
    cleaned_data = form.cleaned_data
38
 
    for f in opts.fields:
39
 
        if not f.editable or isinstance(f, models.AutoField) \
40
 
                or not f.name in cleaned_data:
41
 
            continue
42
 
        if fields and f.name not in fields:
43
 
            continue
44
 
        f.save_form_data(instance, cleaned_data[f.name])
45
 
    # Wrap up the saving of m2m data as a function.
46
 
    def save_m2m():
47
 
        opts = instance.__class__._meta
48
 
        cleaned_data = form.cleaned_data
49
 
        for f in opts.many_to_many:
50
 
            if fields and f.name not in fields:
51
 
                continue
52
 
            if f.name in cleaned_data:
53
 
                f.save_form_data(instance, cleaned_data[f.name])
54
 
    if commit:
55
 
        # If we are committing, save the instance and the m2m data immediately.
56
 
        instance.save()
57
 
        save_m2m()
58
 
    else:
59
 
        # We're not committing. Add a method to the form to allow deferred
60
 
        # saving of m2m data.
61
 
        form.save_m2m = save_m2m
62
 
    return instance
63
 
 
64
 
def make_model_save(model, fields, fail_message):
65
 
    """Returns the save() method for a Form."""
66
 
    def save(self, commit=True):
67
 
        return save_instance(self, model(), fields, fail_message, commit)
68
 
    return save
69
 
 
70
 
def make_instance_save(instance, fields, fail_message):
71
 
    """Returns the save() method for a Form."""
72
 
    def save(self, commit=True):
73
 
        return save_instance(self, instance, fields, fail_message, commit)
74
 
    return save
75
 
 
76
 
def form_for_model(model, form=BaseForm, fields=None,
77
 
                   formfield_callback=lambda f: f.formfield()):
78
 
    """
79
 
    Returns a Form class for the given Django model class.
80
 
 
81
 
    Provide ``form`` if you want to use a custom BaseForm subclass.
82
 
 
83
 
    Provide ``formfield_callback`` if you want to define different logic for
84
 
    determining the formfield for a given database field. It's a callable that
85
 
    takes a database Field instance and returns a form Field instance.
86
 
    """
87
 
    warn("form_for_model is deprecated. Use ModelForm instead.",
88
 
        PendingDeprecationWarning, stacklevel=3)
89
 
    opts = model._meta
90
 
    field_list = []
91
 
    for f in opts.fields + opts.many_to_many:
92
 
        if not f.editable:
93
 
            continue
94
 
        if fields and not f.name in fields:
95
 
            continue
96
 
        formfield = formfield_callback(f)
97
 
        if formfield:
98
 
            field_list.append((f.name, formfield))
99
 
    base_fields = SortedDict(field_list)
100
 
    return type(opts.object_name + 'Form', (form,),
101
 
        {'base_fields': base_fields, '_model': model,
102
 
         'save': make_model_save(model, fields, 'created')})
103
 
 
104
 
def form_for_instance(instance, form=BaseForm, fields=None,
105
 
                      formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)):
106
 
    """
107
 
    Returns a Form class for the given Django model instance.
108
 
 
109
 
    Provide ``form`` if you want to use a custom BaseForm subclass.
110
 
 
111
 
    Provide ``formfield_callback`` if you want to define different logic for
112
 
    determining the formfield for a given database field. It's a callable that
113
 
    takes a database Field instance, plus **kwargs, and returns a form Field
114
 
    instance with the given kwargs (i.e. 'initial').
115
 
    """
116
 
    warn("form_for_instance is deprecated. Use ModelForm instead.",
117
 
        PendingDeprecationWarning, stacklevel=3)
118
 
    model = instance.__class__
119
 
    opts = model._meta
120
 
    field_list = []
121
 
    for f in opts.fields + opts.many_to_many:
122
 
        if not f.editable:
123
 
            continue
124
 
        if fields and not f.name in fields:
125
 
            continue
126
 
        current_value = f.value_from_object(instance)
127
 
        formfield = formfield_callback(f, initial=current_value)
128
 
        if formfield:
129
 
            field_list.append((f.name, formfield))
130
 
    base_fields = SortedDict(field_list)
131
 
    return type(opts.object_name + 'InstanceForm', (form,),
132
 
        {'base_fields': base_fields, '_model': model,
133
 
         'save': make_instance_save(instance, fields, 'changed')})
134
 
 
135
 
def form_for_fields(field_list):
136
 
    """
137
 
    Returns a Form class for the given list of Django database field instances.
138
 
    """
139
 
    fields = SortedDict([(f.name, f.formfield())
140
 
                         for f in field_list if f.editable])
141
 
    return type('FormForFields', (BaseForm,), {'base_fields': fields})
142
 
 
143
 
 
144
 
# ModelForms #################################################################
145
 
 
146
 
def model_to_dict(instance, fields=None, exclude=None):
147
 
    """
148
 
    Returns a dict containing the data in ``instance`` suitable for passing as
149
 
    a Form's ``initial`` keyword argument.
150
 
 
151
 
    ``fields`` is an optional list of field names. If provided, only the named
152
 
    fields will be included in the returned dict.
153
 
 
154
 
    ``exclude`` is an optional list of field names. If provided, the named
155
 
    fields will be excluded from the returned dict, even if they are listed in
156
 
    the ``fields`` argument.
157
 
    """
158
 
    # avoid a circular import
159
 
    from django.db.models.fields.related import ManyToManyField
160
 
    opts = instance._meta
161
 
    data = {}
162
 
    for f in opts.fields + opts.many_to_many:
163
 
        if not f.editable:
164
 
            continue
165
 
        if fields and not f.name in fields:
166
 
            continue
167
 
        if exclude and f.name in exclude:
168
 
            continue
169
 
        if isinstance(f, ManyToManyField):
170
 
            # If the object doesn't have a primry key yet, just use an empty
171
 
            # list for its m2m fields. Calling f.value_from_object will raise
172
 
            # an exception.
173
 
            if instance.pk is None:
174
 
                data[f.name] = []
175
 
            else:
176
 
                # MultipleChoiceWidget needs a list of pks, not object instances.
177
 
                data[f.name] = [obj.pk for obj in f.value_from_object(instance)]
178
 
        else:
179
 
            data[f.name] = f.value_from_object(instance)
180
 
    return data
181
 
 
182
 
def fields_for_model(model, fields=None, exclude=None, formfield_callback=lambda f: f.formfield()):
183
 
    """
184
 
    Returns a ``SortedDict`` containing form fields for the given model.
185
 
 
186
 
    ``fields`` is an optional list of field names. If provided, only the named
187
 
    fields will be included in the returned fields.
188
 
 
189
 
    ``exclude`` is an optional list of field names. If provided, the named
190
 
    fields will be excluded from the returned fields, even if they are listed
191
 
    in the ``fields`` argument.
192
 
    """
193
 
    # TODO: if fields is provided, it would be nice to return fields in that order
194
 
    field_list = []
195
 
    opts = model._meta
196
 
    for f in opts.fields + opts.many_to_many:
197
 
        if not f.editable:
198
 
            continue
199
 
        if fields and not f.name in fields:
200
 
            continue
201
 
        if exclude and f.name in exclude:
202
 
            continue
203
 
        formfield = formfield_callback(f)
204
 
        if formfield:
205
 
            field_list.append((f.name, formfield))
206
 
    return SortedDict(field_list)
207
 
 
208
 
class ModelFormOptions(object):
209
 
    def __init__(self, options=None):
210
 
        self.model = getattr(options, 'model', None)
211
 
        self.fields = getattr(options, 'fields', None)
212
 
        self.exclude = getattr(options, 'exclude', None)
213
 
 
214
 
 
215
 
class ModelFormMetaclass(type):
216
 
    def __new__(cls, name, bases, attrs,
217
 
                formfield_callback=lambda f: f.formfield()):
218
 
        try:
219
 
            parents = [b for b in bases if issubclass(b, ModelForm)]
220
 
        except NameError:
221
 
            # We are defining ModelForm itself.
222
 
            parents = None
223
 
        if not parents:
224
 
            return super(ModelFormMetaclass, cls).__new__(cls, name, bases,
225
 
                    attrs)
226
 
 
227
 
        new_class = type.__new__(cls, name, bases, attrs)
228
 
        declared_fields = get_declared_fields(bases, attrs, False)
229
 
        opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None))
230
 
        if opts.model:
231
 
            # If a model is defined, extract form fields from it.
232
 
            fields = fields_for_model(opts.model, opts.fields,
233
 
                                      opts.exclude, formfield_callback)
234
 
            # Override default model fields with any custom declared ones
235
 
            # (plus, include all the other declared fields).
236
 
            fields.update(declared_fields)
237
 
        else:
238
 
            fields = declared_fields
239
 
        new_class.declared_fields = declared_fields
240
 
        new_class.base_fields = fields
241
 
        return new_class
242
 
 
243
 
class BaseModelForm(BaseForm):
244
 
    def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
245
 
                 initial=None, error_class=ErrorList, label_suffix=':',
246
 
                 instance=None):
247
 
        opts = self._meta
248
 
        if instance is None:
249
 
            # if we didn't get an instance, instantiate a new one
250
 
            self.instance = opts.model()
251
 
            object_data = {}
252
 
        else:
253
 
            self.instance = instance
254
 
            object_data = model_to_dict(instance, opts.fields, opts.exclude)
255
 
        # if initial was provided, it should override the values from instance
256
 
        if initial is not None:
257
 
            object_data.update(initial)
258
 
        BaseForm.__init__(self, data, files, auto_id, prefix, object_data, error_class, label_suffix)
259
 
 
260
 
    def save(self, commit=True):
261
 
        """
262
 
        Saves this ``form``'s cleaned_data into model instance
263
 
        ``self.instance``.
264
 
 
265
 
        If commit=True, then the changes to ``instance`` will be saved to the
266
 
        database. Returns ``instance``.
267
 
        """
268
 
        if self.instance.pk is None:
269
 
            fail_message = 'created'
270
 
        else:
271
 
            fail_message = 'changed'
272
 
        return save_instance(self, self.instance, self._meta.fields, fail_message, commit)
273
 
 
274
 
class ModelForm(BaseModelForm):
275
 
    __metaclass__ = ModelFormMetaclass
276
 
 
277
 
 
278
 
# Fields #####################################################################
279
 
 
280
 
class ModelChoiceIterator(object):
281
 
    def __init__(self, field):
282
 
        self.field = field
283
 
        self.queryset = field.queryset
284
 
 
285
 
    def __iter__(self):
286
 
        if self.field.empty_label is not None:
287
 
            yield (u"", self.field.empty_label)
288
 
        for obj in self.queryset:
289
 
            yield (obj.pk, self.field.label_from_instance(obj))
290
 
        # Clear the QuerySet cache if required.
291
 
        if not self.field.cache_choices:
292
 
            self.queryset._result_cache = None
293
 
 
294
 
class ModelChoiceField(ChoiceField):
295
 
    """A ChoiceField whose choices are a model QuerySet."""
296
 
    # This class is a subclass of ChoiceField for purity, but it doesn't
297
 
    # actually use any of ChoiceField's implementation.
298
 
    default_error_messages = {
299
 
        'invalid_choice': _(u'Select a valid choice. That choice is not one of'
300
 
                            u' the available choices.'),
301
 
    }
302
 
 
303
 
    def __init__(self, queryset, empty_label=u"---------", cache_choices=False,
304
 
                 required=True, widget=Select, label=None, initial=None,
305
 
                 help_text=None, *args, **kwargs):
306
 
        self.empty_label = empty_label
307
 
        self.cache_choices = cache_choices
308
 
 
309
 
        # Call Field instead of ChoiceField __init__() because we don't need
310
 
        # ChoiceField.__init__().
311
 
        Field.__init__(self, required, widget, label, initial, help_text,
312
 
                       *args, **kwargs)
313
 
        self.queryset = queryset
314
 
 
315
 
    def _get_queryset(self):
316
 
        return self._queryset
317
 
 
318
 
    def _set_queryset(self, queryset):
319
 
        self._queryset = queryset
320
 
        self.widget.choices = self.choices
321
 
 
322
 
    queryset = property(_get_queryset, _set_queryset)
323
 
 
324
 
    # this method will be used to create object labels by the QuerySetIterator.
325
 
    # Override it to customize the label.
326
 
    def label_from_instance(self, obj):
327
 
        """
328
 
        This method is used to convert objects into strings; it's used to
329
 
        generate the labels for the choices presented by this object. Subclasses
330
 
        can override this method to customize the display of the choices.
331
 
        """
332
 
        return smart_unicode(obj)
333
 
 
334
 
    def _get_choices(self):
335
 
        # If self._choices is set, then somebody must have manually set
336
 
        # the property self.choices. In this case, just return self._choices.
337
 
        if hasattr(self, '_choices'):
338
 
            return self._choices
339
 
 
340
 
        # Otherwise, execute the QuerySet in self.queryset to determine the
341
 
        # choices dynamically. Return a fresh QuerySetIterator that has not been
342
 
        # consumed. Note that we're instantiating a new QuerySetIterator *each*
343
 
        # time _get_choices() is called (and, thus, each time self.choices is
344
 
        # accessed) so that we can ensure the QuerySet has not been consumed. This
345
 
        # construct might look complicated but it allows for lazy evaluation of
346
 
        # the queryset.
347
 
        return ModelChoiceIterator(self)
348
 
 
349
 
    def _set_choices(self, value):
350
 
        # This method is copied from ChoiceField._set_choices(). It's necessary
351
 
        # because property() doesn't allow a subclass to overwrite only
352
 
        # _get_choices without implementing _set_choices.
353
 
        self._choices = self.widget.choices = list(value)
354
 
 
355
 
    choices = property(_get_choices, _set_choices)
356
 
 
357
 
    def clean(self, value):
358
 
        Field.clean(self, value)
359
 
        if value in EMPTY_VALUES:
360
 
            return None
361
 
        try:
362
 
            value = self.queryset.get(pk=value)
363
 
        except self.queryset.model.DoesNotExist:
364
 
            raise ValidationError(self.error_messages['invalid_choice'])
365
 
        return value
366
 
 
367
 
class ModelMultipleChoiceField(ModelChoiceField):
368
 
    """A MultipleChoiceField whose choices are a model QuerySet."""
369
 
    hidden_widget = MultipleHiddenInput
370
 
    default_error_messages = {
371
 
        'list': _(u'Enter a list of values.'),
372
 
        'invalid_choice': _(u'Select a valid choice. %s is not one of the'
373
 
                            u' available choices.'),
374
 
    }
375
 
 
376
 
    def __init__(self, queryset, cache_choices=False, required=True,
377
 
                 widget=SelectMultiple, label=None, initial=None,
378
 
                 help_text=None, *args, **kwargs):
379
 
        super(ModelMultipleChoiceField, self).__init__(queryset, None,
380
 
            cache_choices, required, widget, label, initial, help_text,
381
 
            *args, **kwargs)
382
 
 
383
 
    def clean(self, value):
384
 
        if self.required and not value:
385
 
            raise ValidationError(self.error_messages['required'])
386
 
        elif not self.required and not value:
387
 
            return []
388
 
        if not isinstance(value, (list, tuple)):
389
 
            raise ValidationError(self.error_messages['list'])
390
 
        final_values = []
391
 
        for val in value:
392
 
            try:
393
 
                obj = self.queryset.get(pk=val)
394
 
            except self.queryset.model.DoesNotExist:
395
 
                raise ValidationError(self.error_messages['invalid_choice'] % val)
396
 
            else:
397
 
                final_values.append(obj)
398
 
        return final_values