| 1 |
from django.db import connection, transaction |
|---|
| 2 |
from django.db.backends import util |
|---|
| 3 |
from django.db.models import signals, get_model |
|---|
| 4 |
from django.db.models.fields import AutoField, Field, IntegerField, PositiveIntegerField, PositiveSmallIntegerField, FieldDoesNotExist |
|---|
| 5 |
from django.db.models.related import RelatedObject |
|---|
| 6 |
from django.db.models.query import QuerySet |
|---|
| 7 |
from django.db.models.query_utils import QueryWrapper |
|---|
| 8 |
from django.utils.encoding import smart_unicode |
|---|
| 9 |
from django.utils.translation import ugettext_lazy, string_concat, ungettext, ugettext as _ |
|---|
| 10 |
from django.utils.functional import curry |
|---|
| 11 |
from django.core import exceptions |
|---|
| 12 |
from django import forms |
|---|
| 13 |
|
|---|
| 14 |
try: |
|---|
| 15 |
set |
|---|
| 16 |
except NameError: |
|---|
| 17 |
from sets import Set as set # Python 2.3 fallback |
|---|
| 18 |
|
|---|
| 19 |
RECURSIVE_RELATIONSHIP_CONSTANT = 'self' |
|---|
| 20 |
|
|---|
| 21 |
pending_lookups = {} |
|---|
| 22 |
|
|---|
| 23 |
def add_lazy_relation(cls, field, relation, operation): |
|---|
| 24 |
""" |
|---|
| 25 |
Adds a lookup on ``cls`` when a related field is defined using a string, |
|---|
| 26 |
i.e.:: |
|---|
| 27 |
|
|---|
| 28 |
class MyModel(Model): |
|---|
| 29 |
fk = ForeignKey("AnotherModel") |
|---|
| 30 |
|
|---|
| 31 |
This string can be: |
|---|
| 32 |
|
|---|
| 33 |
* RECURSIVE_RELATIONSHIP_CONSTANT (i.e. "self") to indicate a recursive |
|---|
| 34 |
relation. |
|---|
| 35 |
|
|---|
| 36 |
* The name of a model (i.e "AnotherModel") to indicate another model in |
|---|
| 37 |
the same app. |
|---|
| 38 |
|
|---|
| 39 |
* An app-label and model name (i.e. "someapp.AnotherModel") to indicate |
|---|
| 40 |
another model in a different app. |
|---|
| 41 |
|
|---|
| 42 |
If the other model hasn't yet been loaded -- almost a given if you're using |
|---|
| 43 |
lazy relationships -- then the relation won't be set up until the |
|---|
| 44 |
class_prepared signal fires at the end of model initialization. |
|---|
| 45 |
|
|---|
| 46 |
operation is the work that must be performed once the relation can be resolved. |
|---|
| 47 |
""" |
|---|
| 48 |
# Check for recursive relations |
|---|
| 49 |
if relation == RECURSIVE_RELATIONSHIP_CONSTANT: |
|---|
| 50 |
app_label = cls._meta.app_label |
|---|
| 51 |
model_name = cls.__name__ |
|---|
| 52 |
|
|---|
| 53 |
else: |
|---|
| 54 |
# Look for an "app.Model" relation |
|---|
| 55 |
try: |
|---|
| 56 |
app_label, model_name = relation.split(".") |
|---|
| 57 |
except ValueError: |
|---|
| 58 |
# If we can't split, assume a model in current app |
|---|
| 59 |
app_label = cls._meta.app_label |
|---|
| 60 |
model_name = relation |
|---|
| 61 |
|
|---|
| 62 |
# Try to look up the related model, and if it's already loaded resolve the |
|---|
| 63 |
# string right away. If get_model returns None, it means that the related |
|---|
| 64 |
# model isn't loaded yet, so we need to pend the relation until the class |
|---|
| 65 |
# is prepared. |
|---|
| 66 |
model = get_model(app_label, model_name, False) |
|---|
| 67 |
if model: |
|---|
| 68 |
operation(field, model, cls) |
|---|
| 69 |
else: |
|---|
| 70 |
key = (app_label, model_name) |
|---|
| 71 |
value = (cls, field, operation) |
|---|
| 72 |
pending_lookups.setdefault(key, []).append(value) |
|---|
| 73 |
|
|---|
| 74 |
def do_pending_lookups(sender, **kwargs): |
|---|
| 75 |
""" |
|---|
| 76 |
Handle any pending relations to the sending model. Sent from class_prepared. |
|---|
| 77 |
""" |
|---|
| 78 |
key = (sender._meta.app_label, sender.__name__) |
|---|
| 79 |
for cls, field, operation in pending_lookups.pop(key, []): |
|---|
| 80 |
operation(field, sender, cls) |
|---|
| 81 |
|
|---|
| 82 |
signals.class_prepared.connect(do_pending_lookups) |
|---|
| 83 |
|
|---|
| 84 |
#HACK |
|---|
| 85 |
class RelatedField(object): |
|---|
| 86 |
def contribute_to_class(self, cls, name): |
|---|
| 87 |
sup = super(RelatedField, self) |
|---|
| 88 |
|
|---|
| 89 |
# Add an accessor to allow easy determination of the related query path for this field |
|---|
| 90 |
self.related_query_name = curry(self._get_related_query_name, cls._meta) |
|---|
| 91 |
|
|---|
| 92 |
if hasattr(sup, 'contribute_to_class'): |
|---|
| 93 |
sup.contribute_to_class(cls, name) |
|---|
| 94 |
|
|---|
| 95 |
if not cls._meta.abstract and self.rel.related_name: |
|---|
| 96 |
self.rel.related_name = self.rel.related_name % {'class': cls.__name__.lower()} |
|---|
| 97 |
|
|---|
| 98 |
other = self.rel.to |
|---|
| 99 |
if isinstance(other, basestring): |
|---|
| 100 |
def resolve_related_class(field, model, cls): |
|---|
| 101 |
field.rel.to = model |
|---|
| 102 |
field.do_related_class(model, cls) |
|---|
| 103 |
add_lazy_relation(cls, self, other, resolve_related_class) |
|---|
| 104 |
else: |
|---|
| 105 |
self.do_related_class(other, cls) |
|---|
| 106 |
|
|---|
| 107 |
def set_attributes_from_rel(self): |
|---|
| 108 |
self.name = self.name or (self.rel.to._meta.object_name.lower() + '_' + self.rel.to._meta.pk.name) |
|---|
| 109 |
if self.verbose_name is None: |
|---|
| 110 |
self.verbose_name = self.rel.to._meta.verbose_name |
|---|
| 111 |
self.rel.field_name = self.rel.field_name or self.rel.to._meta.pk.name |
|---|
| 112 |
|
|---|
| 113 |
def do_related_class(self, other, cls): |
|---|
| 114 |
self.set_attributes_from_rel() |
|---|
| 115 |
self.related = RelatedObject(other, cls, self) |
|---|
| 116 |
if not cls._meta.abstract: |
|---|
| 117 |
self.contribute_to_related_class(other, self.related) |
|---|
| 118 |
|
|---|
| 119 |
def get_db_prep_lookup(self, lookup_type, value): |
|---|
| 120 |
# If we are doing a lookup on a Related Field, we must be |
|---|
| 121 |
# comparing object instances. The value should be the PK of value, |
|---|
| 122 |
# not value itself. |
|---|
| 123 |
def pk_trace(value): |
|---|
| 124 |
# Value may be a primary key, or an object held in a relation. |
|---|
| 125 |
# If it is an object, then we need to get the primary key value for |
|---|
| 126 |
# that object. In certain conditions (especially one-to-one relations), |
|---|
| 127 |
# the primary key may itself be an object - so we need to keep drilling |
|---|
| 128 |
# down until we hit a value that can be used for a comparison. |
|---|
| 129 |
v, field = value, None |
|---|
| 130 |
try: |
|---|
| 131 |
while True: |
|---|
| 132 |
v, field = getattr(v, v._meta.pk.name), v._meta.pk |
|---|
| 133 |
except AttributeError: |
|---|
| 134 |
pass |
|---|
| 135 |
|
|---|
| 136 |
if field: |
|---|
| 137 |
if lookup_type in ('range', 'in'): |
|---|
| 138 |
v = [v] |
|---|
| 139 |
v = field.get_db_prep_lookup(lookup_type, v) |
|---|
| 140 |
if isinstance(v, list): |
|---|
| 141 |
v = v[0] |
|---|
| 142 |
return v |
|---|
| 143 |
|
|---|
| 144 |
if hasattr(value, 'as_sql') or hasattr(value, '_as_sql'): |
|---|
| 145 |
# If the value has a relabel_aliases method, it will need to |
|---|
| 146 |
# be invoked before the final SQL is evaluated |
|---|
| 147 |
if hasattr(value, 'relabel_aliases'): |
|---|
| 148 |
return value |
|---|
| 149 |
if hasattr(value, 'as_sql'): |
|---|
| 150 |
sql, params = value.as_sql() |
|---|
| 151 |
else: |
|---|
| 152 |
sql, params = value._as_sql() |
|---|
| 153 |
return QueryWrapper(('(%s)' % sql), params) |
|---|
| 154 |
|
|---|
| 155 |
# FIXME: lt and gt are explicitally allowed to make |
|---|
| 156 |
# get_(next/prev)_by_date work; other lookups are not allowed since that |
|---|
| 157 |
# gets messy pretty quick. This is a good candidate for some refactoring |
|---|
| 158 |
# in the future. |
|---|
| 159 |
if lookup_type in ['exact', 'gt', 'lt', 'gte', 'lte']: |
|---|
| 160 |
return [pk_trace(value)] |
|---|
| 161 |
if lookup_type in ('range', 'in'): |
|---|
| 162 |
return [pk_trace(v) for v in value] |
|---|
| 163 |
elif lookup_type == 'isnull': |
|---|
| 164 |
return [] |
|---|
| 165 |
raise TypeError, "Related Field has invalid lookup: %s" % lookup_type |
|---|
| 166 |
|
|---|
| 167 |
def _get_related_query_name(self, opts): |
|---|
| 168 |
# This method defines the name that can be used to identify this |
|---|
| 169 |
# related object in a table-spanning query. It uses the lower-cased |
|---|
| 170 |
# object_name by default, but this can be overridden with the |
|---|
| 171 |
# "related_name" option. |
|---|
| 172 |
return self.rel.related_name or opts.object_name.lower() |
|---|
| 173 |
|
|---|
| 174 |
class SingleRelatedObjectDescriptor(object): |
|---|
| 175 |
# This class provides the functionality that makes the related-object |
|---|
| 176 |
# managers available as attributes on a model class, for fields that have |
|---|
| 177 |
# a single "remote" value, on the class pointed to by a related field. |
|---|
| 178 |
# In the example "place.restaurant", the restaurant attribute is a |
|---|
| 179 |
# SingleRelatedObjectDescriptor instance. |
|---|
| 180 |
def __init__(self, related): |
|---|
| 181 |
self.related = related |
|---|
| 182 |
self.cache_name = '_%s_cache' % related.get_accessor_name() |
|---|
| 183 |
|
|---|
| 184 |
def __get__(self, instance, instance_type=None): |
|---|
| 185 |
if instance is None: |
|---|
| 186 |
return self |
|---|
| 187 |
try: |
|---|
| 188 |
return getattr(instance, self.cache_name) |
|---|
| 189 |
except AttributeError: |
|---|
| 190 |
params = {'%s__pk' % self.related.field.name: instance._get_pk_val()} |
|---|
| 191 |
rel_obj = self.related.model._base_manager.get(**params) |
|---|
| 192 |
setattr(instance, self.cache_name, rel_obj) |
|---|
| 193 |
return rel_obj |
|---|
| 194 |
|
|---|
| 195 |
def __set__(self, instance, value): |
|---|
| 196 |
if instance is None: |
|---|
| 197 |
raise AttributeError, "%s must be accessed via instance" % self.related.opts.object_name |
|---|
| 198 |
|
|---|
| 199 |
# The similarity of the code below to the code in |
|---|
| 200 |
# ReverseSingleRelatedObjectDescriptor is annoying, but there's a bunch |
|---|
| 201 |
# of small differences that would make a common base class convoluted. |
|---|
| 202 |
|
|---|
| 203 |
# If null=True, we can assign null here, but otherwise the value needs |
|---|
| 204 |
# to be an instance of the related class. |
|---|
| 205 |
if value is None and self.related.field.null == False: |
|---|
| 206 |
raise ValueError('Cannot assign None: "%s.%s" does not allow null values.' % |
|---|
| 207 |
(instance._meta.object_name, self.related.get_accessor_name())) |
|---|
| 208 |
elif value is not None and not isinstance(value, self.related.model): |
|---|
| 209 |
raise ValueError('Cannot assign "%r": "%s.%s" must be a "%s" instance.' % |
|---|
| 210 |
(value, instance._meta.object_name, |
|---|
| 211 |
self.related.get_accessor_name(), self.related.opts.object_name)) |
|---|
| 212 |
|
|---|
| 213 |
# Set the value of the related field to the value of the related object's related field |
|---|
| 214 |
setattr(value, self.related.field.attname, getattr(instance, self.related.field.rel.get_related_field().attname)) |
|---|
| 215 |
|
|---|
| 216 |
# Since we already know what the related object is, seed the related |
|---|
| 217 |
# object caches now, too. This avoids another db hit if you get the |
|---|
| 218 |
# object you just set. |
|---|
| 219 |
setattr(instance, self.cache_name, value) |
|---|
| 220 |
setattr(value, self.related.field.get_cache_name(), instance) |
|---|
| 221 |
|
|---|
| 222 |
class ReverseSingleRelatedObjectDescriptor(object): |
|---|
| 223 |
# This class provides the functionality that makes the related-object |
|---|
| 224 |
# managers available as attributes on a model class, for fields that have |
|---|
| 225 |
# a single "remote" value, on the class that defines the related field. |
|---|
| 226 |
# In the example "choice.poll", the poll attribute is a |
|---|
| 227 |
# ReverseSingleRelatedObjectDescriptor instance. |
|---|
| 228 |
def __init__(self, field_with_rel): |
|---|
| 229 |
self.field = field_with_rel |
|---|
| 230 |
|
|---|
| 231 |
def __get__(self, instance, instance_type=None): |
|---|
| 232 |
if instance is None: |
|---|
| 233 |
return self |
|---|
| 234 |
|
|---|
| 235 |
cache_name = self.field.get_cache_name() |
|---|
| 236 |
try: |
|---|
| 237 |
return getattr(instance, cache_name) |
|---|
| 238 |
except AttributeError: |
|---|
| 239 |
val = getattr(instance, self.field.attname) |
|---|
| 240 |
if val is None: |
|---|
| 241 |
# If NULL is an allowed value, return it. |
|---|
| 242 |
if self.field.null: |
|---|
| 243 |
return None |
|---|
| 244 |
raise self.field.rel.to.DoesNotExist |
|---|
| 245 |
other_field = self.field.rel.get_related_field() |
|---|
| 246 |
if other_field.rel: |
|---|
| 247 |
params = {'%s__pk' % self.field.rel.field_name: val} |
|---|
| 248 |
else: |
|---|
| 249 |
params = {'%s__exact' % self.field.rel.field_name: val} |
|---|
| 250 |
|
|---|
| 251 |
# If the related manager indicates that it should be used for |
|---|
| 252 |
# related fields, respect that. |
|---|
| 253 |
rel_mgr = self.field.rel.to._default_manager |
|---|
| 254 |
if getattr(rel_mgr, 'use_for_related_fields', False): |
|---|
| 255 |
rel_obj = rel_mgr.get(**params) |
|---|
| 256 |
else: |
|---|
| 257 |
rel_obj = QuerySet(self.field.rel.to).get(**params) |
|---|
| 258 |
setattr(instance, cache_name, rel_obj) |
|---|
| 259 |
return rel_obj |
|---|
| 260 |
|
|---|
| 261 |
def __set__(self, instance, value): |
|---|
| 262 |
if instance is None: |
|---|
| 263 |
raise AttributeError, "%s must be accessed via instance" % self._field.name |
|---|
| 264 |
|
|---|
| 265 |
# If null=True, we can assign null here, but otherwise the value needs |
|---|
| 266 |
# to be an instance of the related class. |
|---|
| 267 |
if value is None and self.field.null == False: |
|---|
| 268 |
raise ValueError('Cannot assign None: "%s.%s" does not allow null values.' % |
|---|
| 269 |
(instance._meta.object_name, self.field.name)) |
|---|
| 270 |
elif value is not None and not isinstance(value, self.field.rel.to): |
|---|
| 271 |
raise ValueError('Cannot assign "%r": "%s.%s" must be a "%s" instance.' % |
|---|
| 272 |
(value, instance._meta.object_name, |
|---|
| 273 |
self.field.name, self.field.rel.to._meta.object_name)) |
|---|
| 274 |
|
|---|
| 275 |
# If we're setting the value of a OneToOneField to None, we need to clear |
|---|
| 276 |
# out the cache on any old related object. Otherwise, deleting the |
|---|
| 277 |
# previously-related object will also cause this object to be deleted, |
|---|
| 278 |
# which is wrong. |
|---|
| 279 |
if value is None: |
|---|
| 280 |
# Look up the previously-related object, which may still be available |
|---|
| 281 |
# since we've not yet cleared out the related field. |
|---|
| 282 |
# Use the cache directly, instead of the accessor; if we haven't |
|---|
| 283 |
# populated the cache, then we don't care - we're only accessing |
|---|
| 284 |
# the object to invalidate the accessor cache, so there's no |
|---|
| 285 |
# need to populate the cache just to expire it again. |
|---|
| 286 |
related = getattr(instance, self.field.get_cache_name(), None) |
|---|
| 287 |
|
|---|
| 288 |
# If we've got an old related object, we need to clear out its |
|---|
| 289 |
# cache. This cache also might not exist if the related object |
|---|
| 290 |
# hasn't been accessed yet. |
|---|
| 291 |
if related: |
|---|
| 292 |
cache_name = '_%s_cache' % self.field.related.get_accessor_name() |
|---|
| 293 |
try: |
|---|
| 294 |
delattr(related, cache_name) |
|---|
| 295 |
except AttributeError: |
|---|
| 296 |
pass |
|---|
| 297 |
|
|---|
| 298 |
# Set the value of the related field |
|---|
| 299 |
try: |
|---|
| 300 |
val = getattr(value, self.field.rel.get_related_field().attname) |
|---|
| 301 |
except AttributeError: |
|---|
| 302 |
val = None |
|---|
| 303 |
setattr(instance, self.field.attname, val) |
|---|
| 304 |
|
|---|
| 305 |
# Since we already know what the related object is, seed the related |
|---|
| 306 |
# object cache now, too. This avoids another db hit if you get the |
|---|
| 307 |
# object you just set. |
|---|
| 308 |
setattr(instance, self.field.get_cache_name(), value) |
|---|
| 309 |
|
|---|
| 310 |
class ForeignRelatedObjectsDescriptor(object): |
|---|
| 311 |
# This class provides the functionality that makes the related-object |
|---|
| 312 |
# managers available as attributes on a model class, for fields that have |
|---|
| 313 |
# multiple "remote" values and have a ForeignKey pointed at them by |
|---|
| 314 |
# some other model. In the example "poll.choice_set", the choice_set |
|---|
| 315 |
# attribute is a ForeignRelatedObjectsDescriptor instance. |
|---|
| 316 |
def __init__(self, related): |
|---|
| 317 |
self.related = related # RelatedObject instance |
|---|
| 318 |
|
|---|
| 319 |
def __get__(self, instance, instance_type=None): |
|---|
| 320 |
if instance is None: |
|---|
| 321 |
return self |
|---|
| 322 |
|
|---|
| 323 |
return self.create_manager(instance, |
|---|
| 324 |
self.related.model._default_manager.__class__) |
|---|
| 325 |
|
|---|
| 326 |
def __set__(self, instance, value): |
|---|
| 327 |
if instance is None: |
|---|
| 328 |
raise AttributeError, "Manager must be accessed via instance" |
|---|
| 329 |
|
|---|
| 330 |
manager = self.__get__(instance) |
|---|
| 331 |
# If the foreign key can support nulls, then completely clear the related set. |
|---|
| 332 |
# Otherwise, just move the named objects into the set. |
|---|
| 333 |
if self.related.field.null: |
|---|
| 334 |
manager.clear() |
|---|
| 335 |
manager.add(*value) |
|---|
| 336 |
|
|---|
| 337 |
def delete_manager(self, instance): |
|---|
| 338 |
""" |
|---|
| 339 |
Returns a queryset based on the related model's base manager (rather |
|---|
| 340 |
than the default manager, as returned by __get__). Used by |
|---|
| 341 |
Model.delete(). |
|---|
| 342 |
""" |
|---|
| 343 |
return self.create_manager(instance, |
|---|
| 344 |
self.related.model._base_manager.__class__) |
|---|
| 345 |
|
|---|
| 346 |
def create_manager(self, instance, superclass): |
|---|
| 347 |
""" |
|---|
| 348 |
Creates the managers used by other methods (__get__() and delete()). |
|---|
| 349 |
""" |
|---|
| 350 |
rel_field = self.related.field |
|---|
| 351 |
rel_model = self.related.model |
|---|
| 352 |
|
|---|
| 353 |
class RelatedManager(superclass): |
|---|
| 354 |
def get_query_set(self): |
|---|
| 355 |
return superclass.get_query_set(self).filter(**(self.core_filters)) |
|---|
| 356 |
|
|---|
| 357 |
def add(self, *objs): |
|---|
| 358 |
for obj in objs: |
|---|
| 359 |
if not isinstance(obj, self.model): |
|---|
| 360 |
raise TypeError, "'%s' instance expected" % self.model._meta.object_name |
|---|
| 361 |
setattr(obj, rel_field.name, instance) |
|---|
| 362 |
obj.save() |
|---|
| 363 |
add.alters_data = True |
|---|
| 364 |
|
|---|
| 365 |
def create(self, **kwargs): |
|---|
| 366 |
kwargs.update({rel_field.name: instance}) |
|---|
| 367 |
return super(RelatedManager, self).create(**kwargs) |
|---|
| 368 |
create.alters_data = True |
|---|
| 369 |
|
|---|
| 370 |
def get_or_create(self, **kwargs): |
|---|
| 371 |
# Update kwargs with the related object that this |
|---|
| 372 |
# ForeignRelatedObjectsDescriptor knows about. |
|---|
| 373 |
kwargs.update({rel_field.name: instance}) |
|---|
| 374 |
return super(RelatedManager, self).get_or_create(**kwargs) |
|---|
| 375 |
get_or_create.alters_data = True |
|---|
| 376 |
|
|---|
| 377 |
# remove() and clear() are only provided if the ForeignKey can have a value of null. |
|---|
| 378 |
if rel_field.null: |
|---|
| 379 |
def remove(self, *objs): |
|---|
| 380 |
val = getattr(instance, rel_field.rel.get_related_field().attname) |
|---|
| 381 |
for obj in objs: |
|---|
| 382 |
# Is obj actually part of this descriptor set? |
|---|
| 383 |
if getattr(obj, rel_field.attname) == val: |
|---|
| 384 |
setattr(obj, rel_field.name, None) |
|---|
| 385 |
obj.save() |
|---|
| 386 |
else: |
|---|
| 387 |
raise rel_field.rel.to.DoesNotExist, "%r is not related to %r." % (obj, instance) |
|---|
| 388 |
remove.alters_data = True |
|---|
| 389 |
|
|---|
| 390 |
def clear(self): |
|---|
| 391 |
for obj in self.all(): |
|---|
| 392 |
setattr(obj, rel_field.name, None) |
|---|
| 393 |
obj.save() |
|---|
| 394 |
clear.alters_data = True |
|---|
| 395 |
|
|---|
| 396 |
manager = RelatedManager() |
|---|
| 397 |
attname = rel_field.rel.get_related_field().name |
|---|
| 398 |
manager.core_filters = {'%s__%s' % (rel_field.name, attname): |
|---|
| 399 |
getattr(instance, attname)} |
|---|
| 400 |
manager.model = self.related.model |
|---|
| 401 |
|
|---|
| 402 |
return manager |
|---|
| 403 |
|
|---|
| 404 |
def create_many_related_manager(superclass, through=False): |
|---|
| 405 |
"""Creates a manager that subclasses 'superclass' (which is a Manager) |
|---|
| 406 |
and adds behavior for many-to-many related objects.""" |
|---|
| 407 |
class ManyRelatedManager(superclass): |
|---|
| 408 |
def __init__(self, model=None, core_filters=None, instance=None, symmetrical=None, |
|---|
| 409 |
join_table=None, source_col_name=None, target_col_name=None): |
|---|
| 410 |
super(ManyRelatedManager, self).__init__() |
|---|
| 411 |
self.core_filters = core_filters |
|---|
| 412 |
self.model = model |
|---|
| 413 |
self.symmetrical = symmetrical |
|---|
| 414 |
self.instance = instance |
|---|
| 415 |
self.join_table = join_table |
|---|
| 416 |
self.source_col_name = source_col_name |
|---|
| 417 |
self.target_col_name = target_col_name |
|---|
| 418 |
self.through = through |
|---|
| 419 |
self._pk_val = self.instance._get_pk_val() |
|---|
| 420 |
if self._pk_val is None: |
|---|
| 421 |
raise ValueError("%r instance needs to have a primary key value before a many-to-many relationship can be used." % instance.__class__.__name__) |
|---|
| 422 |
|
|---|
| 423 |
def get_query_set(self): |
|---|
| 424 |
return superclass.get_query_set(self)._next_is_sticky().filter(**(self.core_filters)) |
|---|
| 425 |
|
|---|
| 426 |
# If the ManyToMany relation has an intermediary model, |
|---|
| 427 |
# the add and remove methods do not exist. |
|---|
| 428 |
if through is None: |
|---|
| 429 |
def add(self, *objs): |
|---|
| 430 |
self._add_items(self.source_col_name, self.target_col_name, *objs) |
|---|
| 431 |
|
|---|
| 432 |
# If this is a symmetrical m2m relation to self, add the mirror entry in the m2m table |
|---|
| 433 |
if self.symmetrical: |
|---|
| 434 |
self._add_items(self.target_col_name, self.source_col_name, *objs) |
|---|
| 435 |
add.alters_data = True |
|---|
| 436 |
|
|---|
| 437 |
def remove(self, *objs): |
|---|
| 438 |
self._remove_items(self.source_col_name, self.target_col_name, *objs) |
|---|
| 439 |
|
|---|
| 440 |
# If this is a symmetrical m2m relation to self, remove the mirror entry in the m2m table |
|---|
| 441 |
if self.symmetrical: |
|---|
| 442 |
self._remove_items(self.target_col_name, self.source_col_name, *objs) |
|---|
| 443 |
remove.alters_data = True |
|---|
| 444 |
|
|---|
| 445 |
def clear(self): |
|---|
| 446 |
self._clear_items(self.source_col_name) |
|---|
| 447 |
|
|---|
| 448 |
# If this is a symmetrical m2m relation to self, clear the mirror entry in the m2m table |
|---|
| 449 |
if self.symmetrical: |
|---|
| 450 |
self._clear_items(self.target_col_name) |
|---|
| 451 |
clear.alters_data = True |
|---|
| 452 |
|
|---|
| 453 |
def create(self, **kwargs): |
|---|
| 454 |
# This check needs to be done here, since we can't later remove this |
|---|
| 455 |
# from the method lookup table, as we do with add and remove. |
|---|
| 456 |
if through is not None: |
|---|
| 457 |
raise AttributeError, "Cannot use create() on a ManyToManyField which specifies an intermediary model. Use %s's Manager instead." % through |
|---|
| 458 |
new_obj = super(ManyRelatedManager, self).create(**kwargs) |
|---|
| 459 |
self.add(new_obj) |
|---|
| 460 |
return new_obj |
|---|
| 461 |
create.alters_data = True |
|---|
| 462 |
|
|---|
| 463 |
def get_or_create(self, **kwargs): |
|---|
| 464 |
obj, created = \ |
|---|
| 465 |
super(ManyRelatedManager, self).get_or_create(**kwargs) |
|---|
| 466 |
# We only need to add() if created because if we got an object back |
|---|
| 467 |
# from get() then the relationship already exists. |
|---|
| 468 |
if created: |
|---|
| 469 |
self.add(obj) |
|---|
| 470 |
return obj, created |
|---|
| 471 |
get_or_create.alters_data = True |
|---|
| 472 |
|
|---|
| 473 |
def _add_items(self, source_col_name, target_col_name, *objs): |
|---|
| 474 |
# join_table: name of the m2m link table |
|---|
| 475 |
# source_col_name: the PK colname in join_table for the source object |
|---|
| 476 |
# target_col_name: the PK colname in join_table for the target object |
|---|
| 477 |
# *objs - objects to add. Either object instances, or primary keys of object instances. |
|---|
| 478 |
|
|---|
| 479 |
# If there aren't any objects, there is nothing to do. |
|---|
| 480 |
if objs: |
|---|
| 481 |
from django.db.models.base import Model |
|---|
| 482 |
# Check that all the objects are of the right type |
|---|
| 483 |
new_ids = set() |
|---|
| 484 |
for obj in objs: |
|---|
| 485 |
if isinstance(obj, self.model): |
|---|
| 486 |
new_ids.add(obj._get_pk_val()) |
|---|
| 487 |
elif isinstance(obj, Model): |
|---|
| 488 |
raise TypeError, "'%s' instance expected" % self.model._meta.object_name |
|---|
| 489 |
else: |
|---|
| 490 |
new_ids.add(obj) |
|---|
| 491 |
# Add the newly created or already existing objects to the join table. |
|---|
| 492 |
# First find out which items are already added, to avoid adding them twice |
|---|
| 493 |
cursor = connection.cursor() |
|---|
| 494 |
cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s IN (%s)" % \ |
|---|
| 495 |
(target_col_name, self.join_table, source_col_name, |
|---|
| 496 |
target_col_name, ",".join(['%s'] * len(new_ids))), |
|---|
| 497 |
[self._pk_val] + list(new_ids)) |
|---|
| 498 |
existing_ids = set([row[0] for row in cursor.fetchall()]) |
|---|
| 499 |
|
|---|
| 500 |
# Add the ones that aren't there already |
|---|
| 501 |
for obj_id in (new_ids - existing_ids): |
|---|
| 502 |
cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \ |
|---|
| 503 |
(self.join_table, source_col_name, target_col_name), |
|---|
| 504 |
[self._pk_val, obj_id]) |
|---|
| 505 |
transaction.commit_unless_managed() |
|---|
| 506 |
|
|---|
| 507 |
def _remove_items(self, source_col_name, target_col_name, *objs): |
|---|
| 508 |
# source_col_name: the PK colname in join_table for the source object |
|---|
| 509 |
# target_col_name: the PK colname in join_table for the target object |
|---|
| 510 |
# *objs - objects to remove |
|---|
| 511 |
|
|---|
| 512 |
# If there aren't any objects, there is nothing to do. |
|---|
| 513 |
if objs: |
|---|
| 514 |
# Check that all the objects are of the right type |
|---|
| 515 |
old_ids = set() |
|---|
| 516 |
for obj in objs: |
|---|
| 517 |
if isinstance(obj, self.model): |
|---|
| 518 |
old_ids.add(obj._get_pk_val()) |
|---|
| 519 |
else: |
|---|
| 520 |
old_ids.add(obj) |
|---|
| 521 |
# Remove the specified objects from the join table |
|---|
| 522 |
cursor = connection.cursor() |
|---|
| 523 |
cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s IN (%s)" % \ |
|---|
| 524 |
(self.join_table, source_col_name, |
|---|
| 525 |
target_col_name, ",".join(['%s'] * len(old_ids))), |
|---|
| 526 |
[self._pk_val] + list(old_ids)) |
|---|
| 527 |
transaction.commit_unless_managed() |
|---|
| 528 |
|
|---|
| 529 |
def _clear_items(self, source_col_name): |
|---|
| 530 |
# source_col_name: the PK colname in join_table for the source object |
|---|
| 531 |
cursor = connection.cursor() |
|---|
| 532 |
cursor.execute("DELETE FROM %s WHERE %s = %%s" % \ |
|---|
| 533 |
(self.join_table, source_col_name), |
|---|
| 534 |
[self._pk_val]) |
|---|
| 535 |
transaction.commit_unless_managed() |
|---|
| 536 |
|
|---|
| 537 |
return ManyRelatedManager |
|---|
| 538 |
|
|---|
| 539 |
class ManyRelatedObjectsDescriptor(object): |
|---|
| 540 |
# This class provides the functionality that makes the related-object |
|---|
| 541 |
# managers available as attributes on a model class, for fields that have |
|---|
| 542 |
# multiple "remote" values and have a ManyToManyField pointed at them by |
|---|
| 543 |
# some other model (rather than having a ManyToManyField themselves). |
|---|
| 544 |
# In the example "publication.article_set", the article_set attribute is a |
|---|
| 545 |
# ManyRelatedObjectsDescriptor instance. |
|---|
| 546 |
def __init__(self, related): |
|---|
| 547 |
self.related = related # RelatedObject instance |
|---|
| 548 |
|
|---|
| 549 |
def __get__(self, instance, instance_type=None): |
|---|
| 550 |
if instance is None: |
|---|
| 551 |
return self |
|---|
| 552 |
|
|---|
| 553 |
# Dynamically create a class that subclasses the related |
|---|
| 554 |
# model's default manager. |
|---|
| 555 |
rel_model = self.related.model |
|---|
| 556 |
superclass = rel_model._default_manager.__class__ |
|---|
| 557 |
RelatedManager = create_many_related_manager(superclass, self.related.field.rel.through) |
|---|
| 558 |
|
|---|
| 559 |
qn = connection.ops.quote_name |
|---|
| 560 |
manager = RelatedManager( |
|---|
| 561 |
model=rel_model, |
|---|
| 562 |
core_filters={'%s__pk' % self.related.field.name: instance._get_pk_val()}, |
|---|
| 563 |
instance=instance, |
|---|
| 564 |
symmetrical=False, |
|---|
| 565 |
join_table=qn(self.related.field.m2m_db_table()), |
|---|
| 566 |
source_col_name=qn(self.related.field.m2m_reverse_name()), |
|---|
| 567 |
target_col_name=qn(self.related.field.m2m_column_name()) |
|---|
| 568 |
) |
|---|
| 569 |
|
|---|
| 570 |
return manager |
|---|
| 571 |
|
|---|
| 572 |
def __set__(self, instance, value): |
|---|
| 573 |
if instance is None: |
|---|
| 574 |
raise AttributeError, "Manager must be accessed via instance" |
|---|
| 575 |
|
|---|
| 576 |
through = getattr(self.related.field.rel, 'through', None) |
|---|
| 577 |
if through is not None: |
|---|
| 578 |
raise AttributeError, "Cannot set values on a ManyToManyField which specifies an intermediary model. Use %s's Manager instead." % through |
|---|
| 579 |
|
|---|
| 580 |
manager = self.__get__(instance) |
|---|
| 581 |
manager.clear() |
|---|
| 582 |
manager.add(*value) |
|---|
| 583 |
|
|---|
| 584 |
class ReverseManyRelatedObjectsDescriptor(object): |
|---|
| 585 |
# This class provides the functionality that makes the related-object |
|---|
| 586 |
# managers available as attributes on a model class, for fields that have |
|---|
| 587 |
# multiple "remote" values and have a ManyToManyField defined in their |
|---|
| 588 |
# model (rather than having another model pointed *at* them). |
|---|
| 589 |
# In the example "article.publications", the publications attribute is a |
|---|
| 590 |
# ReverseManyRelatedObjectsDescriptor instance. |
|---|
| 591 |
def __init__(self, m2m_field): |
|---|
| 592 |
self.field = m2m_field |
|---|
| 593 |
|
|---|
| 594 |
def __get__(self, instance, instance_type=None): |
|---|
| 595 |
if instance is None: |
|---|
| 596 |
return self |
|---|
| 597 |
|
|---|
| 598 |
# Dynamically create a class that subclasses the related |
|---|
| 599 |
# model's default manager. |
|---|
| 600 |
rel_model=self.field.rel.to |
|---|
| 601 |
superclass = rel_model._default_manager.__class__ |
|---|
| 602 |
RelatedManager = create_many_related_manager(superclass, self.field.rel.through) |
|---|
| 603 |
|
|---|
| 604 |
qn = connection.ops.quote_name |
|---|
| 605 |
manager = RelatedManager( |
|---|
| 606 |
model=rel_model, |
|---|
| 607 |
core_filters={'%s__pk' % self.field.related_query_name(): instance._get_pk_val()}, |
|---|
| 608 |
instance=instance, |
|---|
| 609 |
symmetrical=(self.field.rel.symmetrical and isinstance(instance, rel_model)), |
|---|
| 610 |
join_table=qn(self.field.m2m_db_table()), |
|---|
| 611 |
source_col_name=qn(self.field.m2m_column_name()), |
|---|
| 612 |
target_col_name=qn(self.field.m2m_reverse_name()) |
|---|
| 613 |
) |
|---|
| 614 |
|
|---|
| 615 |
return manager |
|---|
| 616 |
|
|---|
| 617 |
def __set__(self, instance, value): |
|---|
| 618 |
if instance is None: |
|---|
| 619 |
raise AttributeError, "Manager must be accessed via instance" |
|---|
| 620 |
|
|---|
| 621 |
through = getattr(self.field.rel, 'through', None) |
|---|
| 622 |
if through is not None: |
|---|
| 623 |
raise AttributeError, "Cannot set values on a ManyToManyField which specifies an intermediary model. Use %s's Manager instead." % through |
|---|
| 624 |
|
|---|
| 625 |
manager = self.__get__(instance) |
|---|
| 626 |
manager.clear() |
|---|
| 627 |
manager.add(*value) |
|---|
| 628 |
|
|---|
| 629 |
class ManyToOneRel(object): |
|---|
| 630 |
def __init__(self, to, field_name, related_name=None, |
|---|
| 631 |
limit_choices_to=None, lookup_overrides=None, parent_link=False): |
|---|
| 632 |
try: |
|---|
| 633 |
to._meta |
|---|
| 634 |
except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT |
|---|
| 635 |
assert isinstance(to, basestring), "'to' must be either a model, a model name or the string %r" % RECURSIVE_RELATIONSHIP_CONSTANT |
|---|
| 636 |
self.to, self.field_name = to, field_name |
|---|
| 637 |
self.related_name = related_name |
|---|
| 638 |
if limit_choices_to is None: |
|---|
| 639 |
limit_choices_to = {} |
|---|
| 640 |
self.limit_choices_to = limit_choices_to |
|---|
| 641 |
self.lookup_overrides = lookup_overrides or {} |
|---|
| 642 |
self.multiple = True |
|---|
| 643 |
self.parent_link = parent_link |
|---|
| 644 |
|
|---|
| 645 |
def get_related_field(self): |
|---|
| 646 |
""" |
|---|
| 647 |
Returns the Field in the 'to' object to which this relationship is |
|---|
| 648 |
tied. |
|---|
| 649 |
""" |
|---|
| 650 |
data = self.to._meta.get_field_by_name(self.field_name) |
|---|
| 651 |
if not data[2]: |
|---|
| 652 |
raise FieldDoesNotExist("No related field named '%s'" % |
|---|
| 653 |
self.field_name) |
|---|
| 654 |
return data[0] |
|---|
| 655 |
|
|---|
| 656 |
class OneToOneRel(ManyToOneRel): |
|---|
| 657 |
def __init__(self, to, field_name, related_name=None, |
|---|
| 658 |
limit_choices_to=None, lookup_overrides=None, parent_link=False): |
|---|
| 659 |
super(OneToOneRel, self).__init__(to, field_name, |
|---|
| 660 |
related_name=related_name, limit_choices_to=limit_choices_to, |
|---|
| 661 |
lookup_overrides=lookup_overrides, parent_link=parent_link) |
|---|
| 662 |
self.multiple = False |
|---|
| 663 |
|
|---|
| 664 |
class ManyToManyRel(object): |
|---|
| 665 |
def __init__(self, to, related_name=None, limit_choices_to=None, |
|---|
| 666 |
symmetrical=True, through=None): |
|---|
| 667 |
self.to = to |
|---|
| 668 |
self.related_name = related_name |
|---|
| 669 |
if limit_choices_to is None: |
|---|
| 670 |
limit_choices_to = {} |
|---|
| 671 |
self.limit_choices_to = limit_choices_to |
|---|
| 672 |
self.symmetrical = symmetrical |
|---|
| 673 |
self.multiple = True |
|---|
| 674 |
self.through = through |
|---|
| 675 |
|
|---|
| 676 |
def get_related_field(self): |
|---|
| 677 |
""" |
|---|
| 678 |
Returns the field in the to' object to which this relationship is tied |
|---|
| 679 |
(this is always the primary key on the target model). Provided for |
|---|
| 680 |
symmetry with ManyToOneRel. |
|---|
| 681 |
""" |
|---|
| 682 |
return self.to._meta.pk |
|---|
| 683 |
|
|---|
| 684 |
class ForeignKey(RelatedField, Field): |
|---|
| 685 |
empty_strings_allowed = False |
|---|
| 686 |
def __init__(self, to, to_field=None, rel_class=ManyToOneRel, **kwargs): |
|---|
| 687 |
try: |
|---|
| 688 |
to_name = to._meta.object_name.lower() |
|---|
| 689 |
except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT |
|---|
| 690 |
assert isinstance(to, basestring), "%s(%r) is invalid. First parameter to ForeignKey must be either a model, a model name, or the string %r" % (self.__class__.__name__, to, RECURSIVE_RELATIONSHIP_CONSTANT) |
|---|
| 691 |
else: |
|---|
| 692 |
assert not to._meta.abstract, "%s cannot define a relation with abstract class %s" % (self.__class__.__name__, to._meta.object_name) |
|---|
| 693 |
to_field = to_field or to._meta.pk.name |
|---|
| 694 |
kwargs['verbose_name'] = kwargs.get('verbose_name', None) |
|---|
| 695 |
|
|---|
| 696 |
kwargs['rel'] = rel_class(to, to_field, |
|---|
| 697 |
related_name=kwargs.pop('related_name', None), |
|---|
| 698 |
limit_choices_to=kwargs.pop('limit_choices_to', None), |
|---|
| 699 |
lookup_overrides=kwargs.pop('lookup_overrides', None), |
|---|
| 700 |
parent_link=kwargs.pop('parent_link', False)) |
|---|
| 701 |
Field.__init__(self, **kwargs) |
|---|
| 702 |
|
|---|
| 703 |
self.db_index = True |
|---|
| 704 |
|
|---|
| 705 |
def get_attname(self): |
|---|
| 706 |
return '%s_id' % self.name |
|---|
| 707 |
|
|---|
| 708 |
def get_validator_unique_lookup_type(self): |
|---|
| 709 |
return '%s__%s__exact' % (self.name, self.rel.get_related_field().name) |
|---|
| 710 |
|
|---|
| 711 |
def get_default(self): |
|---|
| 712 |
"Here we check if the default value is an object and return the to_field if so." |
|---|
| 713 |
field_default = super(ForeignKey, self).get_default() |
|---|
| 714 |
if isinstance(field_default, self.rel.to): |
|---|
| 715 |
return getattr(field_default, self.rel.get_related_field().attname) |
|---|
| 716 |
return field_default |
|---|
| 717 |
|
|---|
| 718 |
def get_db_prep_save(self, value): |
|---|
| 719 |
if value == '' or value == None: |
|---|
| 720 |
return None |
|---|
| 721 |
else: |
|---|
| 722 |
return self.rel.get_related_field().get_db_prep_save(value) |
|---|
| 723 |
|
|---|
| 724 |
def value_to_string(self, obj): |
|---|
| 725 |
if not obj: |
|---|
| 726 |
# In required many-to-one fields with only one available choice, |
|---|
| 727 |
# select that one available choice. Note: For SelectFields |
|---|
| 728 |
# we have to check that the length of choices is *2*, not 1, |
|---|
| 729 |
# because SelectFields always have an initial "blank" value. |
|---|
| 730 |
if not self.blank and self.choices: |
|---|
| 731 |
choice_list = self.get_choices_default() |
|---|
| 732 |
if len(choice_list) == 2: |
|---|
| 733 |
return smart_unicode(choice_list[1][0]) |
|---|
| 734 |
return Field.value_to_string(self, obj) |
|---|
| 735 |
|
|---|
| 736 |
def contribute_to_class(self, cls, name): |
|---|
| 737 |
super(ForeignKey, self).contribute_to_class(cls, name) |
|---|
| 738 |
setattr(cls, self.name, ReverseSingleRelatedObjectDescriptor(self)) |
|---|
| 739 |
if isinstance(self.rel.to, basestring): |
|---|
| 740 |
target = self.rel.to |
|---|
| 741 |
else: |
|---|
| 742 |
target = self.rel.to._meta.db_table |
|---|
| 743 |
cls._meta.duplicate_targets[self.column] = (target, "o2m") |
|---|
| 744 |
|
|---|
| 745 |
def contribute_to_related_class(self, cls, related): |
|---|
| 746 |
setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related)) |
|---|
| 747 |
|
|---|
| 748 |
def formfield(self, **kwargs): |
|---|
| 749 |
defaults = { |
|---|
| 750 |
'form_class': forms.ModelChoiceField, |
|---|
| 751 |
'queryset': self.rel.to._default_manager.complex_filter( |
|---|
| 752 |
self.rel.limit_choices_to), |
|---|
| 753 |
'to_field_name': self.rel.field_name, |
|---|
| 754 |
} |
|---|
| 755 |
defaults.update(kwargs) |
|---|
| 756 |
return super(ForeignKey, self).formfield(**defaults) |
|---|
| 757 |
|
|---|
| 758 |
def db_type(self): |
|---|
| 759 |
# The database column type of a ForeignKey is the column type |
|---|
| 760 |
# of the field to which it points. An exception is if the ForeignKey |
|---|
| 761 |
# points to an AutoField/PositiveIntegerField/PositiveSmallIntegerField, |
|---|
| 762 |
# in which case the column type is simply that of an IntegerField. |
|---|
| 763 |
# If the database needs similar types for key fields however, the only |
|---|
| 764 |
# thing we can do is making AutoField an IntegerField. |
|---|
| 765 |
rel_field = self.rel.get_related_field() |
|---|
| 766 |
if (isinstance(rel_field, AutoField) or |
|---|
| 767 |
(not connection.features.related_fields_match_type and |
|---|
| 768 |
isinstance(rel_field, (PositiveIntegerField, |
|---|
| 769 |
PositiveSmallIntegerField)))): |
|---|
| 770 |
return IntegerField().db_type() |
|---|
| 771 |
return rel_field.db_type() |
|---|
| 772 |
|
|---|
| 773 |
class OneToOneField(ForeignKey): |
|---|
| 774 |
""" |
|---|
| 775 |
A OneToOneField is essentially the same as a ForeignKey, with the exception |
|---|
| 776 |
that always carries a "unique" constraint with it and the reverse relation |
|---|
| 777 |
always returns the object pointed to (since there will only ever be one), |
|---|
| 778 |
rather than returning a list. |
|---|
| 779 |
""" |
|---|
| 780 |
def __init__(self, to, to_field=None, **kwargs): |
|---|
| 781 |
kwargs['unique'] = True |
|---|
| 782 |
super(OneToOneField, self).__init__(to, to_field, OneToOneRel, **kwargs) |
|---|
| 783 |
|
|---|
| 784 |
def contribute_to_related_class(self, cls, related): |
|---|
| 785 |
setattr(cls, related.get_accessor_name(), |
|---|
| 786 |
SingleRelatedObjectDescriptor(related)) |
|---|
| 787 |
|
|---|
| 788 |
def formfield(self, **kwargs): |
|---|
| 789 |
if self.rel.parent_link: |
|---|
| 790 |
return None |
|---|
| 791 |
return super(OneToOneField, self).formfield(**kwargs) |
|---|
| 792 |
|
|---|
| 793 |
class ManyToManyField(RelatedField, Field): |
|---|
| 794 |
def __init__(self, to, **kwargs): |
|---|
| 795 |
try: |
|---|
| 796 |
assert not to._meta.abstract, "%s cannot define a relation with abstract class %s" % (self.__class__.__name__, to._meta.object_name) |
|---|
| 797 |
except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT |
|---|
| 798 |
assert isinstance(to, basestring), "%s(%r) is invalid. First parameter to ManyToManyField must be either a model, a model name, or the string %r" % (self.__class__.__name__, to, RECURSIVE_RELATIONSHIP_CONSTANT) |
|---|
| 799 |
|
|---|
| 800 |
kwargs['verbose_name'] = kwargs.get('verbose_name', None) |
|---|
| 801 |
kwargs['rel'] = ManyToManyRel(to, |
|---|
| 802 |
related_name=kwargs.pop('related_name', None), |
|---|
| 803 |
limit_choices_to=kwargs.pop('limit_choices_to', None), |
|---|
| 804 |
symmetrical=kwargs.pop('symmetrical', True), |
|---|
| 805 |
through=kwargs.pop('through', None)) |
|---|
| 806 |
|
|---|
| 807 |
self.db_table = kwargs.pop('db_table', None) |
|---|
| 808 |
if kwargs['rel'].through is not None: |
|---|
| 809 |
self.creates_table = False |
|---|
| 810 |
assert self.db_table is None, "Cannot specify a db_table if an intermediary model is used." |
|---|
| 811 |
else: |
|---|
| 812 |
self.creates_table = True |
|---|
| 813 |
|
|---|
| 814 |
Field.__init__(self, **kwargs) |
|---|
| 815 |
|
|---|
| 816 |
msg = ugettext_lazy('Hold down "Control", or "Command" on a Mac, to select more than one.') |
|---|
| 817 |
self.help_text = string_concat(self.help_text, ' ', msg) |
|---|
| 818 |
|
|---|
| 819 |
def get_choices_default(self): |
|---|
| 820 |
return Field.get_choices(self, include_blank=False) |
|---|
| 821 |
|
|---|
| 822 |
def _get_m2m_db_table(self, opts): |
|---|
| 823 |
"Function that can be curried to provide the m2m table name for this relation" |
|---|
| 824 |
if self.rel.through is not None: |
|---|
| 825 |
return self.rel.through_model._meta.db_table |
|---|
| 826 |
elif self.db_table: |
|---|
| 827 |
return self.db_table |
|---|
| 828 |
else: |
|---|
| 829 |
return util.truncate_name('%s_%s' % (opts.db_table, self.name), |
|---|
| 830 |
connection.ops.max_name_length()) |
|---|
| 831 |
|
|---|
| 832 |
def _get_m2m_column_name(self, related): |
|---|
| 833 |
"Function that can be curried to provide the source column name for the m2m table" |
|---|
| 834 |
try: |
|---|
| 835 |
return self._m2m_column_name_cache |
|---|
| 836 |
except: |
|---|
| 837 |
if self.rel.through is not None: |
|---|
| 838 |
for f in self.rel.through_model._meta.fields: |
|---|
| 839 |
if hasattr(f,'rel') and f.rel and f.rel.to == related.model: |
|---|
| 840 |
self._m2m_column_name_cache = f.column |
|---|
| 841 |
break |
|---|
| 842 |
# If this is an m2m relation to self, avoid the inevitable name clash |
|---|
| 843 |
elif related.model == related.parent_model: |
|---|
| 844 |
self._m2m_column_name_cache = 'from_' + related.model._meta.object_name.lower() + '_id' |
|---|
| 845 |
else: |
|---|
| 846 |
self._m2m_column_name_cache = related.model._meta.object_name.lower() + '_id' |
|---|
| 847 |
|
|---|
| 848 |
# Return the newly cached value |
|---|
| 849 |
return self._m2m_column_name_cache |
|---|
| 850 |
|
|---|
| 851 |
def _get_m2m_reverse_name(self, related): |
|---|
| 852 |
"Function that can be curried to provide the related column name for the m2m table" |
|---|
| 853 |
try: |
|---|
| 854 |
return self._m2m_reverse_name_cache |
|---|
| 855 |
except: |
|---|
| 856 |
if self.rel.through is not None: |
|---|
| 857 |
found = False |
|---|
| 858 |
for f in self.rel.through_model._meta.fields: |
|---|
| 859 |
if hasattr(f,'rel') and f.rel and f.rel.to == related.parent_model: |
|---|
| 860 |
if related.model == related.parent_model: |
|---|
| 861 |
# If this is an m2m-intermediate to self, |
|---|
| 862 |
# the first foreign key you find will be |
|---|
| 863 |
# the source column. Keep searching for |
|---|
| 864 |
# the second foreign key. |
|---|
| 865 |
if found: |
|---|
| 866 |
self._m2m_reverse_name_cache = f.column |
|---|
| 867 |
break |
|---|
| 868 |
else: |
|---|
| 869 |
found = True |
|---|
| 870 |
else: |
|---|
| 871 |
self._m2m_reverse_name_cache = f.column |
|---|
| 872 |
break |
|---|
| 873 |
# If this is an m2m relation to self, avoid the inevitable name clash |
|---|
| 874 |
elif related.model == related.parent_model: |
|---|
| 875 |
self._m2m_reverse_name_cache = 'to_' + related.parent_model._meta.object_name.lower() + '_id' |
|---|
| 876 |
else: |
|---|
| 877 |
self._m2m_reverse_name_cache = related.parent_model._meta.object_name.lower() + '_id' |
|---|
| 878 |
|
|---|
| 879 |
# Return the newly cached value |
|---|
| 880 |
return self._m2m_reverse_name_cache |
|---|
| 881 |
|
|---|
| 882 |
def isValidIDList(self, field_data, all_data): |
|---|
| 883 |
"Validates that the value is a valid list of foreign keys" |
|---|
| 884 |
mod = self.rel.to |
|---|
| 885 |
try: |
|---|
| 886 |
pks = map(int, field_data.split(',')) |
|---|
| 887 |
except ValueError: |
|---|
| 888 |
# the CommaSeparatedIntegerField validator will catch this error |
|---|
| 889 |
return |
|---|
| 890 |
objects = mod._default_manager.in_bulk(pks) |
|---|
| 891 |
if len(objects) != len(pks): |
|---|
| 892 |
badkeys = [k for k in pks if k not in objects] |
|---|
| 893 |
raise exceptions.ValidationError( |
|---|
| 894 |
ungettext("Please enter valid %(self)s IDs. The value %(value)r is invalid.", |
|---|
| 895 |
"Please enter valid %(self)s IDs. The values %(value)r are invalid.", |
|---|
| 896 |
len(badkeys)) % { |
|---|
| 897 |
'self': self.verbose_name, |
|---|
| 898 |
'value': len(badkeys) == 1 and badkeys[0] or tuple(badkeys), |
|---|
| 899 |
}) |
|---|
| 900 |
|
|---|
| 901 |
def value_to_string(self, obj): |
|---|
| 902 |
data = '' |
|---|
| 903 |
if obj: |
|---|
| 904 |
qs = getattr(obj, self.name).all() |
|---|
| 905 |
data = [instance._get_pk_val() for instance in qs] |
|---|
| 906 |
else: |
|---|
| 907 |
# In required many-to-many fields with only one available choice, |
|---|
| 908 |
# select that one available choice. |
|---|
| 909 |
if not self.blank: |
|---|
| 910 |
choices_list = self.get_choices_default() |
|---|
| 911 |
if len(choices_list) == 1: |
|---|
| 912 |
data = [choices_list[0][0]] |
|---|
| 913 |
return smart_unicode(data) |
|---|
| 914 |
|
|---|
| 915 |
def contribute_to_class(self, cls, name): |
|---|
| 916 |
# To support multiple relations to self, it's useful to have a non-None |
|---|
| 917 |
# related name on symmetrical relations for internal reasons. The |
|---|
| 918 |
# concept doesn't make a lot of sense externally ("you want me to |
|---|
| 919 |
# specify *what* on my non-reversible relation?!"), so we set it up |
|---|
| 920 |
# automatically. The funky name reduces the chance of an accidental |
|---|
| 921 |
# clash. |
|---|
| 922 |
if self.rel.symmetrical and self.rel.to == "self" and self.rel.related_name is None: |
|---|
| 923 |
self.rel.related_name = "%s_rel_+" % name |
|---|
| 924 |
|
|---|
| 925 |
super(ManyToManyField, self).contribute_to_class(cls, name) |
|---|
| 926 |
# Add the descriptor for the m2m relation |
|---|
| 927 |
setattr(cls, self.name, ReverseManyRelatedObjectsDescriptor(self)) |
|---|
| 928 |
|
|---|
| 929 |
# Set up the accessor for the m2m table name for the relation |
|---|
| 930 |
self.m2m_db_table = curry(self._get_m2m_db_table, cls._meta) |
|---|
| 931 |
|
|---|
| 932 |
# Populate some necessary rel arguments so that cross-app relations |
|---|
| 933 |
# work correctly. |
|---|
| 934 |
if isinstance(self.rel.through, basestring): |
|---|
| 935 |
def resolve_through_model(field, model, cls): |
|---|
| 936 |
field.rel.through_model = model |
|---|
| 937 |
add_lazy_relation(cls, self, self.rel.through, resolve_through_model) |
|---|
| 938 |
elif self.rel.through: |
|---|
| 939 |
self.rel.through_model = self.rel.through |
|---|
| 940 |
self.rel.through = self.rel.through._meta.object_name |
|---|
| 941 |
|
|---|
| 942 |
if isinstance(self.rel.to, basestring): |
|---|
| 943 |
target = self.rel.to |
|---|
| 944 |
else: |
|---|
| 945 |
target = self.rel.to._meta.db_table |
|---|
| 946 |
cls._meta.duplicate_targets[self.column] = (target, "m2m") |
|---|
| 947 |
|
|---|
| 948 |
def contribute_to_related_class(self, cls, related): |
|---|
| 949 |
# m2m relations to self do not have a ManyRelatedObjectsDescriptor, |
|---|
| 950 |
# as it would be redundant - unless the field is non-symmetrical. |
|---|
| 951 |
if related.model != related.parent_model or not self.rel.symmetrical: |
|---|
| 952 |
# Add the descriptor for the m2m relation |
|---|
| 953 |
setattr(cls, related.get_accessor_name(), ManyRelatedObjectsDescriptor(related)) |
|---|
| 954 |
|
|---|
| 955 |
# Set up the accessors for the column names on the m2m table |
|---|
| 956 |
self.m2m_column_name = curry(self._get_m2m_column_name, related) |
|---|
| 957 |
self.m2m_reverse_name = curry(self._get_m2m_reverse_name, related) |
|---|
| 958 |
|
|---|
| 959 |
def set_attributes_from_rel(self): |
|---|
| 960 |
pass |
|---|
| 961 |
|
|---|
| 962 |
def value_from_object(self, obj): |
|---|
| 963 |
"Returns the value of this field in the given model instance." |
|---|
| 964 |
return getattr(obj, self.attname).all() |
|---|
| 965 |
|
|---|
| 966 |
def save_form_data(self, instance, data): |
|---|
| 967 |
setattr(instance, self.attname, data) |
|---|
| 968 |
|
|---|
| 969 |
def formfield(self, **kwargs): |
|---|
| 970 |
defaults = {'form_class': forms.ModelMultipleChoiceField, 'queryset': self.rel.to._default_manager.complex_filter(self.rel.limit_choices_to)} |
|---|
| 971 |
defaults.update(kwargs) |
|---|
| 972 |
# If initial is passed in, it's a list of related objects, but the |
|---|
| 973 |
# MultipleChoiceField takes a list of IDs. |
|---|
| 974 |
if defaults.get('initial') is not None: |
|---|
| 975 |
initial = defaults['initial'] |
|---|
| 976 |
if callable(initial): |
|---|
| 977 |
initial = initial() |
|---|
| 978 |
defaults['initial'] = [i._get_pk_val() for i in initial] |
|---|
| 979 |
return super(ManyToManyField, self).formfield(**defaults) |
|---|
| 980 |
|
|---|
| 981 |
def db_type(self): |
|---|
| 982 |
# A ManyToManyField is not represented by a single column, |
|---|
| 983 |
# so return None. |
|---|
| 984 |
return None |
|---|