1
1
from django .utils .datastructures import SortedDict
2
2
3
- from django .forms .forms import BaseForm , get_declared_fields , NON_FIELD_ERRORS
3
+ from django .forms .forms import BaseForm , get_declared_fields , NON_FIELD_ERRORS , pretty_name
4
4
from django .forms .widgets import media_property
5
5
from django .core .exceptions import FieldError , ValidationError as DjangoValidationError
6
6
from django .core .validators import EMPTY_VALUES
@@ -108,7 +108,7 @@ def document_to_dict(instance, fields=None, exclude=None):
108
108
data [f .name ] = getattr (instance , f .name )
109
109
return data
110
110
111
- def fields_for_document (document , fields = None , exclude = None , widgets = None , formfield_callback = None ):
111
+ def fields_for_document (document , fields = None , exclude = None , widgets = None , formfield_callback = None , field_generator = MongoFormFieldGenerator ):
112
112
"""
113
113
Returns a ``SortedDict`` containing form fields for the given model.
114
114
@@ -121,7 +121,8 @@ def fields_for_document(document, fields=None, exclude=None, widgets=None, formf
121
121
"""
122
122
field_list = []
123
123
ignored = []
124
- field_generator = MongoFormFieldGenerator ()
124
+ if isinstance (field_generator , type ):
125
+ field_generator = field_generator ()
125
126
for f in document ._fields .itervalues ():
126
127
if isinstance (f , (ObjectIdField , ListField )):
127
128
continue
@@ -158,7 +159,6 @@ class ModelFormOptions(object):
158
159
def __init__ (self , options = None ):
159
160
self .document = getattr (options , 'document' , None )
160
161
self .model = self .document
161
- # TODO: Move AdminOptions to mongoforms
162
162
if isinstance (self .model ._meta , dict ):
163
163
self .model ._admin_opts = AdminOptions (self .model )
164
164
self .model ._meta = self .model ._admin_opts
@@ -186,9 +186,11 @@ def __new__(cls, name, bases, attrs):
186
186
187
187
opts = new_class ._meta = ModelFormOptions (getattr (new_class , 'Meta' , None ))
188
188
if opts .document :
189
+ formfield_generator = getattr (opts , 'formfield_generator' , MongoFormFieldGenerator )
190
+
189
191
# If a model is defined, extract form fields from it.
190
192
fields = fields_for_document (opts .document , opts .fields ,
191
- opts .exclude , opts .widgets , formfield_callback )
193
+ opts .exclude , opts .widgets , formfield_callback , formfield_generator )
192
194
# make sure opts.fields doesn't specify an invalid field
193
195
none_document_fields = [k for k , v in fields .iteritems () if not v ]
194
196
missing_fields = set (none_document_fields ) - \
@@ -337,12 +339,32 @@ def _post_clean(self):
337
339
338
340
def validate_unique (self ):
339
341
"""
340
- Calls the instance's validate_unique() method and updates the form's
341
- validation errors if any were raised.
342
-
343
- FIXME: Does nothing at the moment.
342
+ Validates unique constrains on the document.
343
+ unique_with is not checked at the moment.
344
344
"""
345
- return
345
+ errors = []
346
+ exclude = self ._get_validation_exclusions ()
347
+ for f in self .instance ._fields .itervalues ():
348
+ if f .unique and f .name not in exclude :
349
+ filter_kwargs = {
350
+ f .name : getattr (self .instance , f .name )
351
+ }
352
+ qs = self .instance .__class__ .objects ().filter (** filter_kwargs )
353
+ # Exclude the current object from the query if we are editing an
354
+ # instance (as opposed to creating a new one)
355
+ if self .instance .pk is not None :
356
+ qs = qs .filter (pk__ne = self .instance .pk )
357
+ if len (qs ) > 0 :
358
+ message = _ (u"%(model_name)s with this %(field_label)s already exists." ) % {
359
+ 'model_name' : unicode (capfirst (self .instance ._meta .verbose_name )),
360
+ 'field_label' : unicode (pretty_name (f .name ))
361
+ }
362
+ err_dict = {f .name : [message ]}
363
+ self ._update_errors (err_dict )
364
+ errors .append (err_dict )
365
+
366
+ return errors
367
+
346
368
347
369
348
370
def save (self , commit = True ):
@@ -501,84 +523,14 @@ def clean(self):
501
523
self .validate_unique ()
502
524
503
525
def validate_unique (self ):
504
- return
505
- # # Collect unique_checks and date_checks to run from all the forms.
506
- # all_unique_checks = set()
507
- # all_date_checks = set()
508
- # for form in self.forms:
509
- # if not hasattr(form, 'cleaned_data'):
510
- # continue
511
- # exclude = form._get_validation_exclusions()
512
- # unique_checks, date_checks = form.instance._get_unique_checks(exclude=exclude)
513
- # all_unique_checks = all_unique_checks.union(set(unique_checks))
514
- # all_date_checks = all_date_checks.union(set(date_checks))
515
- #
516
- # errors = []
517
- # # Do each of the unique checks (unique and unique_together)
518
- # for uclass, unique_check in all_unique_checks:
519
- # seen_data = set()
520
- # for form in self.forms:
521
- # # if the form doesn't have cleaned_data then we ignore it,
522
- # # it's already invalid
523
- # if not hasattr(form, "cleaned_data"):
524
- # continue
525
- # # get data for each field of each of unique_check
526
- # row_data = tuple([form.cleaned_data[field] for field in unique_check if field in form.cleaned_data])
527
- # if row_data and not None in row_data:
528
- # # if we've aready seen it then we have a uniqueness failure
529
- # if row_data in seen_data:
530
- # # poke error messages into the right places and mark
531
- # # the form as invalid
532
- # errors.append(self.get_unique_error_message(unique_check))
533
- # form._errors[NON_FIELD_ERRORS] = self.error_class([self.get_form_error()])
534
- # del form.cleaned_data
535
- # break
536
- # # mark the data as seen
537
- # seen_data.add(row_data)
538
- # # iterate over each of the date checks now
539
- # for date_check in all_date_checks:
540
- # seen_data = set()
541
- # uclass, lookup, field, unique_for = date_check
542
- # for form in self.forms:
543
- # # if the form doesn't have cleaned_data then we ignore it,
544
- # # it's already invalid
545
- # if not hasattr(self, 'cleaned_data'):
546
- # continue
547
- # # see if we have data for both fields
548
- # if (form.cleaned_data and form.cleaned_data[field] is not None
549
- # and form.cleaned_data[unique_for] is not None):
550
- # # if it's a date lookup we need to get the data for all the fields
551
- # if lookup == 'date':
552
- # date = form.cleaned_data[unique_for]
553
- # date_data = (date.year, date.month, date.day)
554
- # # otherwise it's just the attribute on the date/datetime
555
- # # object
556
- # else:
557
- # date_data = (getattr(form.cleaned_data[unique_for], lookup),)
558
- # data = (form.cleaned_data[field],) + date_data
559
- # # if we've aready seen it then we have a uniqueness failure
560
- # if data in seen_data:
561
- # # poke error messages into the right places and mark
562
- # # the form as invalid
563
- # errors.append(self.get_date_error_message(date_check))
564
- # form._errors[NON_FIELD_ERRORS] = self.error_class([self.get_form_error()])
565
- # del form.cleaned_data
566
- # break
567
- # seen_data.add(data)
568
- # if errors:
569
- # raise ValidationError(errors)
570
-
571
- def get_unique_error_message (self , unique_check ):
572
- if len (unique_check ) == 1 :
573
- return ugettext ("Please correct the duplicate data for %(field)s." ) % {
574
- "field" : unique_check [0 ],
575
- }
576
- else :
577
- return ugettext ("Please correct the duplicate data for %(field)s, "
578
- "which must be unique." ) % {
579
- "field" : get_text_list (unique_check , unicode (_ ("and" ))),
580
- }
581
-
526
+ errors = []
527
+ for form in self .forms :
528
+ if not hasattr (form , 'cleaned_data' ):
529
+ continue
530
+ errors += form .validate_unique ()
531
+
532
+ if errors :
533
+ raise ValidationError (errors )
582
534
def get_date_error_message (self , date_check ):
583
535
return ugettext ("Please correct the duplicate data for %(field_name)s "
584
536
"which must be unique for the %(lookup)s in %(date_field)s." ) % {
0 commit comments