Changeset 8215 for django/branches/gis/django/db/models
- Timestamp:
- 08/05/08 12:15:33 (5 months ago)
- Files:
-
- django/branches/gis (modified) (1 prop)
- django/branches/gis/django/db/models/base.py (modified) (7 diffs)
- django/branches/gis/django/db/models/fields/__init__.py (modified) (25 diffs)
- django/branches/gis/django/db/models/fields/related.py (modified) (25 diffs)
- django/branches/gis/django/db/models/fields/subclassing.py (modified) (2 diffs)
- django/branches/gis/django/db/models/__init__.py (modified) (1 diff)
- django/branches/gis/django/db/models/query.py (modified) (4 diffs)
- django/branches/gis/django/db/models/sql/query.py (modified) (6 diffs)
- django/branches/gis/django/db/models/sql/where.py (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/gis
- Property svnmerge-integrated changed from /django/trunk:1-7978 to /django/trunk:1-8214
django/branches/gis/django/db/models/base.py
r7979 r8215 13 13 from django.core import validators 14 14 from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError 15 from django.db.models.fields import AutoField, ImageField , FieldDoesNotExist15 from django.db.models.fields import AutoField, ImageField 16 16 from django.db.models.fields.related import OneToOneRel, ManyToOneRel, OneToOneField 17 17 from django.db.models.query import delete_objects, Q, CollectedObjects … … 21 21 from django.db.models.loading import register_models, get_model 22 22 from django.dispatch import dispatcher 23 from django.utils.datastructures import SortedDict24 23 from django.utils.functional import curry 25 24 from django.utils.encoding import smart_str, force_unicode, smart_unicode … … 202 201 203 202 for field in fields_iter: 203 rel_obj = None 204 204 if kwargs: 205 205 if isinstance(field.rel, ManyToOneRel): … … 218 218 if rel_obj is None and field.null: 219 219 val = None 220 else:221 try:222 val = getattr(rel_obj, field.rel.get_related_field().attname)223 except AttributeError:224 raise TypeError("Invalid value: %r should be a %s instance, not a %s" %225 (field.name, field.rel.to, type(rel_obj)))226 220 else: 227 221 val = kwargs.pop(field.attname, field.get_default()) 228 222 else: 229 223 val = field.get_default() 230 setattr(self, field.attname, val) 224 # If we got passed a related instance, set it using the field.name 225 # instead of field.attname (e.g. "user" instead of "user_id") so 226 # that the object gets properly cached (and type checked) by the 227 # RelatedObjectDescriptor. 228 if rel_obj: 229 setattr(self, field.name, rel_obj) 230 else: 231 setattr(self, field.attname, val) 231 232 232 233 if kwargs: … … 300 301 if not raw: 301 302 for parent, field in meta.parents.items(): 303 # At this point, parent's primary key field may be unknown 304 # (for example, from administration form which doesn't fill 305 # this field). If so, fill it. 306 if getattr(self, parent._meta.pk.attname) is None and getattr(self, field.attname) is not None: 307 setattr(self, parent._meta.pk.attname, getattr(self, field.attname)) 308 302 309 self.save_base(raw, parent) 303 310 setattr(self, field.attname, self._get_pk_val(parent._meta)) … … 473 480 474 481 def _save_FIELD_file(self, field, filename, raw_field, save=True): 475 directory = field.get_directory_name() 476 try: # Create the date-based directory if it doesn't exist. 477 os.makedirs(os.path.join(settings.MEDIA_ROOT, directory)) 478 except OSError: # Directory probably already exists. 479 pass 482 # Create the upload directory if it doesn't already exist 483 directory = os.path.join(settings.MEDIA_ROOT, field.get_directory_name()) 484 if not os.path.exists(directory): 485 os.makedirs(directory) 486 elif not os.path.isdir(directory): 487 raise IOError('%s exists and is not a directory' % directory) 480 488 481 489 # Check for old-style usage (files-as-dictionaries). Warn here first … … 495 503 import warnings 496 504 warnings.warn( 497 message = "Representing uploaded files as dictionaries is deprecated. Use django.core.files.uploadedfile.SimpleUploadedFile instead.",505 message = "Representing uploaded files as strings is deprecated. Use django.core.files.uploadedfile.SimpleUploadedFile instead.", 498 506 category = DeprecationWarning, 499 507 stacklevel = 2 django/branches/gis/django/db/models/fields/__init__.py
r8002 r8215 23 23 from django.utils.translation import ugettext_lazy, ugettext as _ 24 24 from django.utils.encoding import smart_unicode, force_unicode, smart_str 25 from django.utils.maxlength import LegacyMaxlength26 25 from django.utils import datetime_safe 27 26 … … 63 62 64 63 class Field(object): 65 # Provide backwards compatibility for the maxlength attribute and66 # argument for this class and all subclasses.67 __metaclass__ = LegacyMaxlength68 69 64 # Designates whether empty strings fundamentally are allowed at the 70 65 # database level. … … 192 187 self.name = name 193 188 self.attname, self.column = self.get_attname_column() 194 self.verbose_name = self.verbose_name or (name and name.replace('_', ' ')) 189 if self.verbose_name is None and name: 190 self.verbose_name = name.replace('_', ' ') 195 191 196 192 def contribute_to_class(self, cls, name): … … 218 214 return getattr(model_instance, self.attname) 219 215 216 def get_db_prep_value(self, value): 217 """Returns field's value prepared for interacting with the database 218 backend. 219 220 Used by the default implementations of ``get_db_prep_save``and 221 `get_db_prep_lookup``` 222 """ 223 return value 224 220 225 def get_db_prep_save(self, value): 221 226 "Returns field's value prepared for saving into a database." 222 return value227 return self.get_db_prep_value(value) 223 228 224 229 def get_db_prep_lookup(self, lookup_type, value): … … 227 232 sql, params = value.as_sql() 228 233 return QueryWrapper(('(%s)' % sql), params) 229 if lookup_type in (' exact', 'regex', 'iregex', 'gt', 'gte', 'lt', 'lte', 'month', 'day', 'search'):234 if lookup_type in ('regex', 'iregex', 'month', 'day', 'search'): 230 235 return [value] 236 elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'): 237 return [self.get_db_prep_value(value)] 231 238 elif lookup_type in ('range', 'in'): 232 return value239 return [self.get_db_prep_value(v) for v in value] 233 240 elif lookup_type in ('contains', 'icontains'): 234 241 return ["%%%s%%" % connection.ops.prep_for_like_query(value)] … … 246 253 except ValueError: 247 254 raise ValueError("The __year lookup type requires an integer argument") 248 if settings.DATABASE_ENGINE == 'sqlite3': 249 first = '%s-01-01' 250 second = '%s-12-31 23:59:59.999999' 251 elif not connection.features.date_field_supports_time_value and self.get_internal_type() == 'DateField': 252 first = '%s-01-01' 253 second = '%s-12-31' 254 elif not connection.features.supports_usecs: 255 first = '%s-01-01 00:00:00' 256 second = '%s-12-31 23:59:59.99' 255 256 if self.get_internal_type() == 'DateField': 257 return connection.ops.year_lookup_bounds_for_date_field(value) 257 258 else: 258 first = '%s-01-01 00:00:00' 259 second = '%s-12-31 23:59:59.999999' 260 return [first % value, second % value] 259 return connection.ops.year_lookup_bounds(value) 260 261 261 raise TypeError("Field has invalid lookup: %s" % lookup_type) 262 262 … … 289 289 field_objs = [oldforms.SelectField] 290 290 291 params['choices'] = self. flatchoices291 params['choices'] = self.get_flatchoices() 292 292 else: 293 293 field_objs = self.get_manipulator_field_objs() … … 363 363 364 364 def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH): 365 "Returns a list of tuples used as SelectField choices for this field." 365 """Returns choices with a default blank choices included, for use 366 as SelectField choices for this field.""" 366 367 first_choice = include_blank and blank_choice or [] 367 368 if self.choices: … … 377 378 return self.get_choices() 378 379 380 def get_flatchoices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH): 381 "Returns flattened choices with a default blank choice included." 382 first_choice = include_blank and blank_choice or [] 383 return first_choice + list(self.flatchoices) 384 379 385 def _get_val_from_obj(self, obj): 380 386 if obj: … … 409 415 410 416 def _get_flatchoices(self): 417 """Flattened version of choices tuple.""" 411 418 flat = [] 412 for choice, value in self. get_choices_default():419 for choice, value in self.choices: 413 420 if type(value) in (list, tuple): 414 421 flat.extend(value) … … 417 424 return flat 418 425 flatchoices = property(_get_flatchoices) 419 426 420 427 def save_form_data(self, instance, data): 421 428 setattr(instance, self.name, data) … … 450 457 raise validators.ValidationError, _("This value must be an integer.") 451 458 459 def get_db_prep_value(self, value): 460 if value is None: 461 return None 462 return int(value) 463 452 464 def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True): 453 465 if not rel: … … 478 490 def __init__(self, *args, **kwargs): 479 491 kwargs['blank'] = True 492 if 'default' not in kwargs and not kwargs.get('null'): 493 kwargs['default'] = False 480 494 Field.__init__(self, *args, **kwargs) 481 495 … … 488 502 if value in ('f', 'False', '0'): return False 489 503 raise validators.ValidationError, _("This value must be either True or False.") 504 505 def get_db_prep_value(self, value): 506 if value is None: 507 return None 508 return bool(value) 490 509 491 510 def get_manipulator_field_objs(self): … … 550 569 raise validators.ValidationError, _('Enter a valid date in YYYY-MM-DD format.') 551 570 552 def get_db_prep_lookup(self, lookup_type, value):553 if lookup_type in ('range', 'in'):554 value = [smart_unicode(v) for v in value]555 elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte') and hasattr(value, 'strftime'):556 value = datetime_safe.new_date(value).strftime('%Y-%m-%d')557 else:558 value = smart_unicode(value)559 return Field.get_db_prep_lookup(self, lookup_type, value)560 561 571 def pre_save(self, model_instance, add): 562 572 if self.auto_now or (self.auto_now_add and add): … … 582 592 return self.editable or self.auto_now or self.auto_now_add 583 593 584 def get_db_prep_save(self, value): 585 # Casts dates into string format for entry into database. 586 if value is not None: 587 try: 588 value = datetime_safe.new_date(value).strftime('%Y-%m-%d') 589 except AttributeError: 590 # If value is already a string it won't have a strftime method, 591 # so we'll just let it pass through. 592 pass 593 return Field.get_db_prep_save(self, value) 594 def get_db_prep_value(self, value): 595 # Casts dates into the format expected by the backend 596 return connection.ops.value_to_db_date(self.to_python(value)) 594 597 595 598 def get_manipulator_field_objs(self): … … 620 623 if isinstance(value, datetime.date): 621 624 return datetime.datetime(value.year, value.month, value.day) 625 626 # Attempt to parse a datetime: 627 value = smart_str(value) 628 # split usecs, because they are not recognized by strptime. 629 if '.' in value: 630 try: 631 value, usecs = value.split('.') 632 usecs = int(usecs) 633 except ValueError: 634 raise validators.ValidationError, _('Enter a valid date/time in YYYY-MM-DD HH:MM[ss[.uuuuuu]] format.') 635 else: 636 usecs = 0 637 kwargs = {'microsecond': usecs} 622 638 try: # Seconds are optional, so try converting seconds first. 623 return datetime.datetime(*time.strptime(value, '%Y-%m-%d %H:%M:%S')[:6]) 639 return datetime.datetime(*time.strptime(value, '%Y-%m-%d %H:%M:%S')[:6], 640 **kwargs) 641 624 642 except ValueError: 625 643 try: # Try without seconds. 626 return datetime.datetime(*time.strptime(value, '%Y-%m-%d %H:%M')[:5]) 644 return datetime.datetime(*time.strptime(value, '%Y-%m-%d %H:%M')[:5], 645 **kwargs) 627 646 except ValueError: # Try without hour/minutes/seconds. 628 647 try: 629 return datetime.datetime(*time.strptime(value, '%Y-%m-%d')[:3]) 648 return datetime.datetime(*time.strptime(value, '%Y-%m-%d')[:3], 649 **kwargs) 630 650 except ValueError: 631 raise validators.ValidationError, _('Enter a valid date/time in YYYY-MM-DD HH:MM format.') 632 633 def get_db_prep_save(self, value): 634 # Casts dates into string format for entry into database. 635 if value is not None: 636 # MySQL will throw a warning if microseconds are given, because it 637 # doesn't support microseconds. 638 if not connection.features.supports_usecs and hasattr(value, 'microsecond'): 639 value = value.replace(microsecond=0) 640 value = smart_unicode(value) 641 return Field.get_db_prep_save(self, value) 642 643 def get_db_prep_lookup(self, lookup_type, value): 644 if lookup_type in ('range', 'in'): 645 value = [smart_unicode(v) for v in value] 646 else: 647 value = smart_unicode(value) 648 return Field.get_db_prep_lookup(self, lookup_type, value) 651 raise validators.ValidationError, _('Enter a valid date/time in YYYY-MM-DD HH:MM[ss[.uuuuuu]] format.') 652 653 def get_db_prep_value(self, value): 654 # Casts dates into the format expected by the backend 655 return connection.ops.value_to_db_datetime(self.to_python(value)) 649 656 650 657 def get_manipulator_field_objs(self): … … 711 718 decimal places. 712 719 """ 713 num_chars = self.max_digits 714 # Allow for a decimal point 715 if self.decimal_places > 0: 716 num_chars += 1 717 # Allow for a minus sign 718 if value < 0: 719 num_chars += 1 720 721 return u"%.*f" % (self.decimal_places, value) 722 723 def get_db_prep_save(self, value): 724 value = self._format(value) 725 return super(DecimalField, self).get_db_prep_save(value) 726 727 def get_db_prep_lookup(self, lookup_type, value): 728 if lookup_type in ('range', 'in'): 729 value = [self._format(v) for v in value] 730 else: 731 value = self._format(value) 732 return super(DecimalField, self).get_db_prep_lookup(lookup_type, value) 720 # Method moved to django.db.backends.util. 721 # 722 # It is preserved because it is used by the oracle backend 723 # (django.db.backends.oracle.query), and also for 724 # backwards-compatibility with any external code which may have used 725 # this method. 726 from django.db.backends import util 727 return util.format_number(value, self.max_digits, self.decimal_places) 728 729 def get_db_prep_value(self, value): 730 return connection.ops.value_to_db_decimal(self.to_python(value), 731 self.max_digits, self.decimal_places) 733 732 734 733 def get_manipulator_field_objs(self): … … 769 768 return "FileField" 770 769 771 def get_db_prep_ save(self, value):770 def get_db_prep_value(self, value): 772 771 "Returns field's value prepared for saving into a database." 773 772 # Need to convert UploadedFile objects provided via a form to unicode for database insertion … … 910 909 empty_strings_allowed = False 911 910 911 def get_db_prep_value(self, value): 912 if value is None: 913 return None 914 return float(value) 915 912 916 def get_manipulator_field_objs(self): 913 917 return [oldforms.FloatField] … … 957 961 class IntegerField(Field): 958 962 empty_strings_allowed = False 963 def get_db_prep_value(self, value): 964 if value is None: 965 return None 966 return int(value) 967 959 968 def get_manipulator_field_objs(self): 960 969 return [oldforms.IntegerField] … … 1004 1013 raise validators.ValidationError, _("This value must be either None, True or False.") 1005 1014 1015 def get_db_prep_value(self, value): 1016 if value is None: 1017 return None 1018 return bool(value) 1019 1006 1020 def get_manipulator_field_objs(self): 1007 1021 return [oldforms.NullBooleanField] … … 1016 1030 return super(NullBooleanField, self).formfield(**defaults) 1017 1031 1018 class PhoneNumberField( IntegerField):1032 class PhoneNumberField(Field): 1019 1033 def get_manipulator_field_objs(self): 1020 1034 return [oldforms.PhoneNumberField] … … 1098 1112 return "TimeField" 1099 1113 1100 def get_db_prep_lookup(self, lookup_type, value): 1101 if connection.features.time_field_needs_date: 1102 # Oracle requires a date in order to parse. 1103 def prep(value): 1104 if isinstance(value, datetime.time): 1105 value = datetime.datetime.combine(datetime.date(1900, 1, 1), value) 1106 return smart_unicode(value) 1107 else: 1108 prep = smart_unicode 1109 if lookup_type in ('range', 'in'): 1110 value = [prep(v) for v in value] 1111 else: 1112 value = prep(value) 1113 return Field.get_db_prep_lookup(self, lookup_type, value) 1114 def to_python(self, value): 1115 if value is None: 1116 return None 1117 if isinstance(value, datetime.time): 1118 return value 1119 1120 # Attempt to parse a datetime: 1121 value = smart_str(value) 1122 # split usecs, because they are not recognized by strptime. 1123 if '.' in value: 1124 try: 1125 value, usecs = value.split('.') 1126 usecs = int(usecs) 1127 except ValueError: 1128 raise validators.ValidationError, _('Enter a valid time in HH:MM[:ss[.uuuuuu]] format.') 1129 else: 1130 usecs = 0 1131 kwargs = {'microsecond': usecs} 1132 1133 try: # Seconds are optional, so try converting seconds first. 1134 return datetime.time(*time.strptime(value, '%H:%M:%S')[3:6], 1135 **kwargs) 1136 except ValueError: 1137 try: # Try without seconds. 1138 return datetime.time(*time.strptime(value, '%H:%M')[3:5], 1139 **kwargs) 1140 except ValueError: 1141 raise validators.ValidationError, _('Enter a valid time in HH:MM[:ss[.uuuuuu]] format.') 1114 1142 1115 1143 def pre_save(self, model_instance, add): … … 1121 1149 return super(TimeField, self).pre_save(model_instance, add) 1122 1150 1123 def get_db_prep_save(self, value): 1124 # Casts dates into string format for entry into database. 1125 if value is not None: 1126 # MySQL will throw a warning if microseconds are given, because it 1127 # doesn't support microseconds. 1128 if not connection.features.supports_usecs and hasattr(value, 'microsecond'): 1129 value = value.replace(microsecond=0) 1130 if connection.features.time_field_needs_date: 1131 # cx_Oracle expects a datetime.datetime to persist into TIMESTAMP field. 1132 if isinstance(value, datetime.time): 1133 value = datetime.datetime(1900, 1, 1, value.hour, value.minute, 1134 value.second, value.microsecond) 1135 elif isinstance(value, basestring): 1136 value = datetime.datetime(*(time.strptime(value, '%H:%M:%S')[:6])) 1137 else: 1138 value = smart_unicode(value) 1139 return Field.get_db_prep_save(self, value) 1151 def get_db_prep_value(self, value): 1152 # Casts times into the format expected by the backend 1153 return connection.ops.value_to_db_time(self.to_python(value)) 1140 1154 1141 1155 def get_manipulator_field_objs(self): django/branches/gis/django/db/models/fields/related.py
r8002 r8215 3 3 from django.db.models.fields import AutoField, Field, IntegerField, PositiveIntegerField, PositiveSmallIntegerField, FieldDoesNotExist 4 4 from django.db.models.related import RelatedObject 5 from django.db.models.query import QuerySet 5 6 from django.db.models.query_utils import QueryWrapper 6 from django.utils.text import capfirst7 7 from django.utils.translation import ugettext_lazy, string_concat, ungettext, ugettext as _ 8 8 from django.utils.functional import curry 9 from django.utils.encoding import smart_unicode10 9 from django.core import validators 11 10 from django import oldforms … … 25 24 pending_lookups = {} 26 25 27 def add_lazy_relation(cls, field, relation ):26 def add_lazy_relation(cls, field, relation, operation): 28 27 """ 29 28 Adds a lookup on ``cls`` when a related field is defined using a string, … … 47 46 lazy relationships -- then the relation won't be set up until the 48 47 class_prepared signal fires at the end of model initialization. 48 49 operation is the work that must be performed once the relation can be resolved. 49 50 """ 50 51 # Check for recursive relations … … 68 69 model = get_model(app_label, model_name, False) 69 70 if model: 70 field.rel.to = model 71 field.do_related_class(model, cls) 71 operation(field, model, cls) 72 72 else: 73 73 key = (app_label, model_name) 74 value = (cls, field )74 value = (cls, field, operation) 75 75 pending_lookups.setdefault(key, []).append(value) 76 76 … … 80 80 """ 81 81 key = (sender._meta.app_label, sender.__name__) 82 for cls, field in pending_lookups.pop(key, []): 83 field.rel.to = sender 84 field.do_related_class(sender, cls) 82 for cls, field, operation in pending_lookups.pop(key, []): 83 operation(field, sender, cls) 85 84 86 85 dispatcher.connect(do_pending_lookups, signal=signals.class_prepared) … … 110 109 other = self.rel.to 111 110 if isinstance(other, basestring): 112 add_lazy_relation(cls, self, other) 111 def resolve_related_class(field, model, cls): 112 field.rel.to = model 113 field.do_related_class(model, cls) 114 add_lazy_relation(cls, self, other, resolve_related_class) 113 115 else: 114 116 self.do_related_class(other, cls) … … 116 118 def set_attributes_from_rel(self): 117 119 self.name = self.name or (self.rel.to._meta.object_name.lower() + '_' + self.rel.to._meta.pk.name) 118 self.verbose_name = self.verbose_name or self.rel.to._meta.verbose_name 120 if self.verbose_name is None: 121 self.verbose_name = self.rel.to._meta.verbose_name 119 122 self.rel.field_name = self.rel.field_name or self.rel.to._meta.pk.name 120 123 … … 237 240 else: 238 241 params = {'%s__exact' % self.field.rel.field_name: val} 239 rel_obj = self.field.rel.to._default_manager.get(**params) 242 243 # If the related manager indicates that it should be used for 244 # related fields, respect that. 245 rel_mgr = self.field.rel.to._default_manager 246 if getattr(rel_mgr, 'use_for_related_fields', False): 247 rel_obj = rel_mgr.get(**params) 248 else: 249 rel_obj = QuerySet(self.field.rel.to).get(**params) 240 250 setattr(instance, cache_name, rel_obj) 241 251 return rel_obj … … 341 351 manager.add(*value) 342 352 343 def create_many_related_manager(superclass ):353 def create_many_related_manager(superclass, through=False): 344 354 """Creates a manager that subclasses 'superclass' (which is a Manager) 345 355 and adds behavior for many-to-many related objects.""" … … 355 365 self.source_col_name = source_col_name 356 366 self.target_col_name = target_col_name 367 self.through = through 357 368 self._pk_val = self.instance._get_pk_val() 358 369 if self._pk_val is None: … … 362 373 return superclass.get_query_set(self).filter(**(self.core_filters)) 363 374 364 def add(self, *objs): 365 self._add_items(self.source_col_name, self.target_col_name, *objs) 366 367 # If this is a symmetrical m2m relation to self, add the mirror entry in the m2m table 368 if self.symmetrical: 369 self._add_items(self.target_col_name, self.source_col_name, *objs) 370 add.alters_data = True 371 372 def remove(self, *objs): 373 self._remove_items(self.source_col_name, self.target_col_name, *objs) 374 375 # If this is a symmetrical m2m relation to self, remove the mirror entry in the m2m table 376 if self.symmetrical: 377 self._remove_items(self.target_col_name, self.source_col_name, *objs) 378 remove.alters_data = True 375 # If the ManyToMany relation has an intermediary model, 376 # the add and remove methods do not exist. 377 if through is None: 378 def add(self, *objs): 379 self._add_items(self.source_col_name, self.target_col_name, *objs) 380 381 # If this is a symmetrical m2m relation to self, add the mirror entry in the m2m table 382 if self.symmetrical: 383 self._add_items(self.target_col_name, self.source_col_name, *objs) 384 add.alters_data = True 385 386 def remove(self, *objs): 387 self._remove_items(self.source_col_name, self.target_col_name, *objs) 388 389 # If this is a symmetrical m2m relation to self, remove the mirror entry in the m2m table 390 if self.symmetrical: 391 self._remove_items(self.target_col_name, self.source_col_name, *objs) 392 remove.alters_data = True 379 393 380 394 def clear(self): … … 387 401 388 402 def create(self, **kwargs): 403 # This check needs to be done here, since we can't later remove this 404 # from the method lookup table, as we do with add and remove. 405 if through is not None: 406 raise AttributeError, "Cannot use create() on a ManyToManyField which specifies an intermediary model. Use %s's Manager instead." % through 389 407 new_obj = self.model(**kwargs) 390 408 new_obj.save() … … 474 492 rel_model = self.related.model 475 493 superclass = rel_model._default_manager.__class__ 476 RelatedManager = create_many_related_manager(superclass )494 RelatedManager = create_many_related_manager(superclass, self.related.field.rel.through) 477 495 478 496 qn = connection.ops.quote_name … … 493 511 raise AttributeError, "Manager must be accessed via instance" 494 512 513 through = getattr(self.related.field.rel, 'through', None) 514 if through is not None: 515 raise AttributeError, "Cannot set values on a ManyToManyField which specifies an intermediary model. Use %s's Manager instead." % through 516 495 517 manager = self.__get__(instance) 496 518 manager.clear() … … 515 537 rel_model=self.field.rel.to 516 538 superclass = rel_model._default_manager.__class__ 517 RelatedManager = create_many_related_manager(superclass )539 RelatedManager = create_many_related_manager(superclass, self.field.rel.through) 518 540 519 541 qn = connection.ops.quote_name … … 533 555 if instance is None: 534 556 raise AttributeError, "Manager must be accessed via instance" 557 558 through = getattr(self.field.rel, 'through', None) 559 if through is not None: 560 raise AttributeError, "Cannot set values on a ManyToManyField which specifies an intermediary model. Use %s's Manager instead." % through 535 561 536 562 manager = self.__get__(instance) … … 585 611 class ManyToManyRel(object): 586 612 def __init__(self, to, num_in_admin=0, related_name=None, 587 limit_choices_to=None, symmetrical=True ):613 limit_choices_to=None, symmetrical=True, through=None): 588 614 self.to = to 589 615 self.num_in_admin = num_in_admin … … 595 621 self.symmetrical = symmetrical 596 622 self.multiple = True 623 self.through = through 597 624 598 625 class ForeignKey(RelatedField, Field): … … 605 632 else: 606 633 to_field = to_field or to._meta.pk.name 607 kwargs['verbose_name'] = kwargs.get('verbose_name', '') 608 609 if 'edit_inline_type' in kwargs: 610 import warnings 611 warnings.warn("edit_inline_type is deprecated. Use edit_inline instead.", DeprecationWarning) 612 kwargs['edit_inline'] = kwargs.pop('edit_inline_type') 634 kwargs['verbose_name'] = kwargs.get('verbose_name', None) 613 635 614 636 kwargs['rel'] = rel_class(to, to_field, … … 706 728 def __init__(self, to, to_field=None, **kwargs): 707 729 kwargs['unique'] = True 730 kwargs['editable'] = False 708 731 if 'num_in_admin' not in kwargs: 709 732 kwargs['num_in_admin'] = 0 … … 723 746 related_name=kwargs.pop('related_name', None), 724 747 limit_choices_to=kwargs.pop('limit_choices_to', None), 725 symmetrical=kwargs.pop('symmetrical', True)) 748 symmetrical=kwargs.pop('symmetrical', True), 749 through=kwargs.pop('through', None)) 750 726 751 self.db_table = kwargs.pop('db_table', None) 752 if kwargs['rel'].through is not None: 753 self.creates_table = False 754 assert self.db_table is None, "Cannot specify a db_table if an intermediary model is used." 755 else: 756 self.creates_table = True 757 727 758 Field.__init__(self, **kwargs) 728 759 … … 739 770 def _get_m2m_db_table(self, opts): 740 771 "Function that can be curried to provide the m2m table name for this relation" 741 if self.db_table: 772 if self.rel.through is not None: 773 return self.rel.through_model._meta.db_table 774 elif self.db_table: 742 775 return self.db_table 743 776 else: … … 746 779 def _get_m2m_column_name(self, related): 747 780 "Function that can be curried to provide the source column name for the m2m table" 748 # If this is an m2m relation to self, avoid the inevitable name clash 749 if related.model == related.parent_model: 750 return 'from_' + related.model._meta.object_name.lower() + '_id' 751 else: 752 return related.model._meta.object_name.lower() + '_id' 781 try: 782 return self._m2m_column_name_cache 783 except: 784 if self.rel.through is not None: 785 for f in self.rel.through_model._meta.fields: 786 if hasattr(f,'rel') and f.rel and f.rel.to == related.model: 787 self._m2m_column_name_cache = f.column 788 break 789 # If this is an m2m relation to self, avoid the inevitable name clash 790 elif related.model == related.parent_model: 791 self._m2m_column_name_cache = 'from_' + related.model._meta.object_name.lower() + '_id' 792 else: 793 self._m2m_column_name_cache = related.model._meta.object_name.lower() + '_id' 794 795 # Return the newly cached value 796 return self._m2m_column_name_cache 753 797 754 798 def _get_m2m_reverse_name(self, related): 755 799 "Function that can be curried to provide the related column name for the m2m table" 756 # If this is an m2m relation to self, avoid the inevitable name clash 757 if related.model == related.parent_model: 758 return 'to_' + related.parent_model._meta.object_name.lower() + '_id' 759 else: 760 return related.parent_model._meta.object_name.lower() + '_id' 800 try: 801 return self._m2m_reverse_name_cache 802 except: 803 if self.rel.through is not None: 804 found = False 805 for f in self.rel.through_model._meta.fields: 806 if hasattr(f,'rel') and f.rel and f.rel.to == related.parent_model: 807 if related.model == related.parent_model: 808 # If this is an m2m-intermediate to self, 809 # the first foreign key you find will be 810 # the source column. Keep searching for 811 # the second foreign key. 812 if found: 813 self._m2m_reverse_name_cache = f.column 814 break 815 else: 816 found = True 817 else: 818 self._m2m_reverse_name_cache = f.column 819 break 820 # If this is an m2m relation to self, avoid the inevitable name clash 821 elif related.model == related.parent_model: 822 self._m2m_reverse_name_cache = 'to_' + related.parent_model._meta.object_name.lower() + '_id' 823 else: 824 self._m2m_reverse_name_cache = related.parent_model._meta.object_name.lower() + '_id' 825 826 # Return the newly cached value 827 return self._m2m_reverse_name_cache 761 828 762 829 def isValidIDList(self, field_data, all_data): … … 792 859 793 860 def contribute_to_class(self, cls, name): 794 super(ManyToManyField, self).contribute_to_class(cls, name) 861 super(ManyToManyField, self).contribute_to_class(cls, name) 795 862 # Add the descriptor for the m2m relation 796 863 setattr(cls, self.name, ReverseManyRelatedObjectsDescriptor(self)) … … 798 865 # Set up the accessor for the m2m table name for the relation 799 866 self.m2m_db_table = curry(self._get_m2m_db_table, cls._meta) 800 867 868 # Populate some necessary rel arguments so that cross-app relations 869 # work correctly. 870 if isinstance(self.rel.through, basestring): 871 def resolve_through_model(field, model, cls): 872 field.rel.through_model = model 873 add_lazy_relation(cls, self, self.rel.through, resolve_through_model) 874 elif self.rel.through: 875 self.rel.through_model = self.rel.through 876 self.rel.through = self.rel.through._meta.object_name 877 801 878 if isinstance(self.rel.to, basestring): 802 879 target = self.rel.to django/branches/gis/django/db/models/fields/subclassing.py
r7354 r8215 6 6 """ 7 7 8 from django.utils.maxlength import LegacyMaxlength 9 10 class SubfieldBase(LegacyMaxlength): 8 class SubfieldBase(type): 11 9 """ 12 10 A metaclass for custom Field subclasses. This ensures the model's attribute … … 51 49 52 50 return contribute_to_class 53 django/branches/gis/django/db/models/__init__.py
r7979 r8215 11 11 from django.db.models.fields.related import ForeignKey, OneToOneField, ManyToManyField, ManyToOneRel, ManyToManyRel, OneToOneRel, TABULAR, STACKED 12 12 from django.db.models import signals 13 from django.utils.functional import curry14 from django.utils.text import capfirst15 13 16 14 # Admin stages. django/branches/gis/django/db/models/query.py
r7979 r8215 1 import warnings2 1 try: 3 2 set … … 758 757 759 758 760 # QOperator, QNot, QAnd and QOr are temporarily retained for backwards761 # compatibility. All the old functionality is now part of the 'Q' class.762 class QOperator(Q):763 def __init__(self, *args, **kwargs):764 warnings.warn('Use Q instead of QOr, QAnd or QOperation.',765 DeprecationWarning, stacklevel=2)766 super(QOperator, self).__init__(*args, **kwargs)767 768 QOr = QAnd = QOperator769 770 &n
