mirror of
https://github.com/django/django.git
synced 2025-06-09 05:29:13 +00:00
git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@1800 bcc190cf-cafb-0310-a4f2-bffc1f526a37
529 lines
24 KiB
Python
529 lines
24 KiB
Python
import django.db.models.manipulators
|
|
import django.db.models.manager
|
|
from django.db.models.fields import AutoField, ImageField, Admin
|
|
from django.db.models.fields.related import OneToOne, ManyToOne
|
|
from django.db.models.related import RelatedObject
|
|
from django.db.models.query import orderlist2sql
|
|
from django.db.models.options import Options
|
|
from django.db import connection, backend
|
|
from django.db.models import signals
|
|
from django.dispatch import dispatcher
|
|
from django.core.exceptions import ObjectDoesNotExist
|
|
from django.utils.functional import curry
|
|
from django.conf import settings
|
|
import re
|
|
import types
|
|
import sys
|
|
import os
|
|
|
|
# For Python 2.3
|
|
if not hasattr(__builtins__, 'set'):
|
|
from sets import Set as set
|
|
|
|
attribute_transforms = {'Admin': lambda cls: Admin(**cls.__dict__)}
|
|
|
|
class ModelBase(type):
|
|
"Metaclass for all models"
|
|
def __new__(cls, name, bases, attrs):
|
|
# If this isn't a subclass of Model, don't do anything special.
|
|
if not bases or bases == (object,):
|
|
return type.__new__(cls, name, bases, attrs)
|
|
|
|
# Create the class.
|
|
new_class = type.__new__(cls, name, bases, {'__module__': attrs.pop('__module__')})
|
|
new_class.add_to_class('_meta', Options(attrs.pop('Meta', None)))
|
|
new_class.add_to_class('DoesNotExist', types.ClassType('DoesNotExist', (ObjectDoesNotExist,), {}))
|
|
|
|
#Figure out the app_label by looking one level up.
|
|
#FIXME: wrong for nested model modules
|
|
app_package = sys.modules.get(new_class.__module__)
|
|
app_label = app_package.__name__.replace('.models', '')
|
|
app_label = app_label[app_label.rfind('.')+1:]
|
|
|
|
# Cache the app label.
|
|
new_class._meta.app_label = app_label
|
|
|
|
# Add all attributes to the class.
|
|
for obj_name, obj in attrs.items():
|
|
new_class.add_to_class(obj_name, obj)
|
|
|
|
new_class._prepare()
|
|
|
|
# Populate the _MODELS member on the module the class is in.
|
|
app_package.__dict__.setdefault('_MODELS', []).append(new_class)
|
|
return new_class
|
|
|
|
def cmp_cls(x, y):
|
|
for field in x._meta.fields:
|
|
if field.rel and not field.null and field.rel.to == y:
|
|
return -1
|
|
for field in y._meta.fields:
|
|
if field.rel and not field.null and field.rel.to == x:
|
|
return 1
|
|
return 0
|
|
|
|
class Model(object):
|
|
__metaclass__ = ModelBase
|
|
|
|
def __repr__(self):
|
|
return '<%s object>' % self.__class__.__name__
|
|
|
|
def __eq__(self, other):
|
|
return isinstance(other, self.__class__) and getattr(self, self._meta.pk.attname) == getattr(other, self._meta.pk.attname)
|
|
|
|
def __ne__(self, other):
|
|
return not self.__eq__(other)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
dispatcher.send(signal=signals.pre_init, sender=self.__class__, args=args, kwargs=kwargs)
|
|
if kwargs:
|
|
for f in self._meta.fields:
|
|
if isinstance(f.rel, ManyToOne):
|
|
try:
|
|
# Assume object instance was passed in.
|
|
rel_obj = kwargs.pop(f.name)
|
|
except KeyError:
|
|
try:
|
|
# Object instance wasn't passed in -- must be an ID.
|
|
val = kwargs.pop(f.attname)
|
|
except KeyError:
|
|
val = f.get_default()
|
|
else:
|
|
# Object instance was passed in.
|
|
# Special case: You can pass in "None" for related objects if it's allowed.
|
|
if rel_obj is None and f.null:
|
|
val = None
|
|
else:
|
|
try:
|
|
val = getattr(rel_obj, f.rel.get_related_field().attname)
|
|
except AttributeError:
|
|
raise TypeError, "Invalid value: %r should be a %s instance, not a %s" % (f.name, f.rel.to, type(rel_obj))
|
|
setattr(self, f.attname, val)
|
|
else:
|
|
val = kwargs.pop(f.attname, f.get_default())
|
|
setattr(self, f.attname, val)
|
|
if kwargs:
|
|
raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0]
|
|
for i, arg in enumerate(args):
|
|
setattr(self, self._meta.fields[i].attname, arg)
|
|
dispatcher.send(signal=signals.post_init, sender=self.__class__, instance=self)
|
|
|
|
def add_to_class(cls, name, attribute):
|
|
transform = attribute_transforms.get(name, None)
|
|
if transform:
|
|
attribute = transform(attribute)
|
|
if hasattr(attribute, 'contribute_to_class'):
|
|
attribute.contribute_to_class(cls, name)
|
|
else:
|
|
setattr(cls, name, attribute)
|
|
add_to_class = classmethod(add_to_class)
|
|
|
|
def _prepare(cls):
|
|
# Creates some methods once self._meta has been populated.
|
|
opts = cls._meta
|
|
opts._prepare(cls)
|
|
|
|
if opts.order_with_respect_to:
|
|
cls.get_next_in_order = curry(cls._get_next_or_previous_in_order, is_next=True)
|
|
cls.get_previous_in_order = curry(cls._get_next_or_previous_in_order, is_next=False)
|
|
|
|
# Give the class a docstring -- its definition.
|
|
if cls.__doc__ is None:
|
|
cls.__doc__ = "%s.%s(%s)" % (opts.module_name, cls.__name__, ", ".join([f.name for f in opts.fields]))
|
|
|
|
if hasattr(cls, 'get_absolute_url'):
|
|
cls.get_absolute_url = curry(get_absolute_url, opts, cls.get_absolute_url)
|
|
|
|
dispatcher.send(signal=signals.class_prepared, sender=cls)
|
|
|
|
_prepare = classmethod(_prepare)
|
|
|
|
def save(self):
|
|
# Run any pre-save hooks.
|
|
if hasattr(self, '_pre_save'):
|
|
self._pre_save()
|
|
dispatcher.send(signal=signals.pre_save, sender=self.__class__, instance=self)
|
|
|
|
non_pks = [f for f in self._meta.fields if not f.primary_key]
|
|
cursor = connection.cursor()
|
|
|
|
# First, try an UPDATE. If that doesn't update anything, do an INSERT.
|
|
pk_val = getattr(self, self._meta.pk.attname)
|
|
pk_set = bool(pk_val)
|
|
record_exists = True
|
|
if pk_set:
|
|
# Determine whether a record with the primary key already exists.
|
|
cursor.execute("SELECT 1 FROM %s WHERE %s=%%s LIMIT 1" % \
|
|
(backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), [pk_val])
|
|
# If it does already exist, do an UPDATE.
|
|
if cursor.fetchone():
|
|
db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.attname), False)) for f in non_pks]
|
|
cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \
|
|
(backend.quote_name(self._meta.db_table),
|
|
','.join(['%s=%%s' % backend.quote_name(f.column) for f in non_pks]),
|
|
backend.quote_name(self._meta.pk.attname)),
|
|
db_values + [pk_val])
|
|
else:
|
|
record_exists = False
|
|
if not pk_set or not record_exists:
|
|
field_names = [backend.quote_name(f.column) for f in self._meta.fields if not isinstance(f, AutoField)]
|
|
db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.attname), True)) for f in self._meta.fields if not isinstance(f, AutoField)]
|
|
# If the PK has been manually set, respect that.
|
|
if pk_set:
|
|
field_names += [f.column for f in self._meta.fields if isinstance(f, AutoField)]
|
|
db_values += [f.get_db_prep_save(f.pre_save(getattr(self, f.column), True)) for f in self._meta.fields if isinstance(f, AutoField)]
|
|
placeholders = ['%s'] * len(field_names)
|
|
if self._meta.order_with_respect_to:
|
|
field_names.append(backend.quote_name('_order'))
|
|
# TODO: This assumes the database supports subqueries.
|
|
placeholders.append('(SELECT COUNT(*) FROM %s WHERE %s = %%s)' % \
|
|
(backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.order_with_respect_to.column)))
|
|
db_values.append(getattr(self, self._meta.order_with_respect_to.attname))
|
|
cursor.execute("INSERT INTO %s (%s) VALUES (%s)" % \
|
|
(backend.quote_name(self._meta.db_table), ','.join(field_names),
|
|
','.join(placeholders)), db_values)
|
|
if self._meta.has_auto_field and not pk_set:
|
|
setattr(self, self._meta.pk.attname, backend.get_last_insert_id(cursor, self._meta.db_table, self._meta.pk.column))
|
|
connection.commit()
|
|
|
|
# Run any post-save hooks.
|
|
dispatcher.send(signal=signals.pre_save, sender = self.__class__, instance = self )
|
|
|
|
if hasattr(self, '_post_save'):
|
|
self._post_save()
|
|
|
|
save.alters_data = True
|
|
|
|
def __get_pk_val(self):
|
|
return str(getattr(self, self._meta.pk.attname))
|
|
|
|
def __collect_sub_objects(self, seen_objs):
|
|
pk_val = self.__get_pk_val()
|
|
|
|
if pk_val in seen_objs.setdefault(self.__class__, {}):
|
|
return
|
|
seen_objs.setdefault(self.__class__, {})[pk_val] = (self, True)
|
|
|
|
for related in self._meta.get_all_related_objects():
|
|
rel_opts_name = related.get_method_name_part()
|
|
if isinstance(related.field.rel, OneToOne):
|
|
try:
|
|
sub_obj = getattr(self, 'get_%s' % rel_opts_name)()
|
|
except ObjectDoesNotExist:
|
|
pass
|
|
else:
|
|
sub_obj.__collect_sub_objects(seen_objs)
|
|
else:
|
|
for sub_obj in getattr(self, 'get_%s_list' % rel_opts_name)():
|
|
sub_obj.__collect_sub_objects(seen_objs)
|
|
|
|
def delete(self, ignore_objects=None):
|
|
assert getattr(self, self._meta.pk.attname) is not None, "%r can't be deleted because it doesn't have an ID."
|
|
seen_objs = {}
|
|
if ignore_objects:
|
|
for obj in ignore_objects:
|
|
ignore_objs.setdefault(self.__class__,{})[obj.__get_pk_val()] = (obj, False)
|
|
|
|
seen_objs = {}
|
|
self.__collect_sub_objects(seen_objs)
|
|
|
|
#TODO: create a total class ordering rather than this sorting, which is
|
|
# only a partial ordering, and also is done each delete..
|
|
seen_cls = set(seen_objs.keys())
|
|
cls_order = list(seen_cls)
|
|
cls_order.sort(cmp_cls)
|
|
|
|
cursor = connection.cursor()
|
|
|
|
for cls in cls_order:
|
|
seen_objs[cls] = seen_objs[cls].items()
|
|
seen_objs[cls].sort()
|
|
for pk_val,(instance, do_delete) in seen_objs[cls]:
|
|
|
|
# Run any pre-delete hooks.
|
|
if do_delete:
|
|
if hasattr(instance, '_pre_delete'):
|
|
instance._pre_delete()
|
|
|
|
dispatcher.send(signal=signals.pre_delete, sender=cls, instance=instance)
|
|
|
|
for related in cls._meta.get_all_related_many_to_many_objects():
|
|
cursor.execute("DELETE FROM %s WHERE %s=%%s" % \
|
|
(backend.quote_name(related.field.get_m2m_db_table(related.opts)),
|
|
backend.quote_name(cls._meta.object_name.lower() + '_id')),
|
|
[pk_val])
|
|
for f in cls._meta.many_to_many:
|
|
cursor.execute("DELETE FROM %s WHERE %s=%%s" % \
|
|
(backend.quote_name(f.get_m2m_db_table(cls._meta)),
|
|
backend.quote_name(cls._meta.object_name.lower() + '_id')),
|
|
[pk_val])
|
|
|
|
for field in cls._meta.fields:
|
|
if field.rel and field.null and field.rel.to in seen_cls:
|
|
cursor.execute("UPDATE %s SET %s = NULL WHERE %s=%%s" % \
|
|
(backend.quote_name(cls._meta.db_table), backend.quote_name(field.column),
|
|
backend.quote_name(cls._meta.pk.column)), [pk_val])
|
|
setattr(instance, field.attname, None)
|
|
|
|
for cls in cls_order:
|
|
seen_objs[cls].reverse()
|
|
|
|
for pk_val, (instance, do_delete) in seen_objs[cls]:
|
|
if do_delete:
|
|
cursor.execute("DELETE FROM %s WHERE %s=%%s" % \
|
|
(backend.quote_name(cls._meta.db_table), backend.quote_name(cls._meta.pk.column)),
|
|
[pk_val])
|
|
|
|
setattr(instance, cls._meta.pk.attname, None)
|
|
|
|
dispatcher.send(signal=signals.post_delete, sender=cls, instance=instance)
|
|
|
|
if hasattr(instance, '_post_delete'):
|
|
instance._post_delete()
|
|
|
|
connection.commit()
|
|
|
|
delete.alters_data = True
|
|
|
|
def _get_FIELD_display(self, field):
|
|
value = getattr(self, field.attname)
|
|
return dict(field.choices).get(value, value)
|
|
|
|
def _get_next_or_previous_by_FIELD(self, field, is_next, **kwargs):
|
|
op = is_next and '>' or '<'
|
|
kwargs.setdefault('where', []).append('(%s %s %%s OR (%s = %%s AND %s.%s %s %%s))' % \
|
|
(backend.quote_name(field.column), op, backend.quote_name(field.column),
|
|
backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column), op))
|
|
param = str(getattr(self, field.attname))
|
|
kwargs.setdefault('params', []).extend([param, param, getattr(self, self._meta.pk.attname)])
|
|
kwargs['order_by'] = [(not is_next and '-' or '') + field.name, (not is_next and '-' or '') + self._meta.pk.name]
|
|
kwargs['limit'] = 1
|
|
return self.__class__._default_manager.get_object(**kwargs)
|
|
|
|
def _get_next_or_previous_in_order(self, is_next):
|
|
cachename = "__%s_order_cache" % is_next
|
|
if not hasattr(self, cachename):
|
|
op = is_next and '>' or '<'
|
|
order_field = self.order_with_respect_to
|
|
obj = self._default_manager.get_object(order_by=('_order',),
|
|
where=['%s %s (SELECT %s FROM %s WHERE %s=%%s)' % \
|
|
(backend.quote_name('_order'), op, backend.quote_name('_order'),
|
|
backend.quote_name(opts.db_table), backend.quote_name(opts.pk.column)),
|
|
'%s=%%s' % backend.quote_name(order_field.column)],
|
|
limit=1,
|
|
params=[getattr(self, opts.pk.attname), getattr(self, order_field.attname)])
|
|
setattr(self, cachename, obj)
|
|
return getattr(self, cachename)
|
|
|
|
def _get_FIELD_filename(self, field):
|
|
return os.path.join(settings.MEDIA_ROOT, getattr(self, field.attname))
|
|
|
|
def _get_FIELD_url(self, field):
|
|
if getattr(self, field.attname): # value is not blank
|
|
import urlparse
|
|
return urlparse.urljoin(settings.MEDIA_URL, getattr(self, field.attname)).replace('\\', '/')
|
|
return ''
|
|
|
|
def _get_FIELD_size(self, field):
|
|
return os.path.getsize(self.__get_FIELD_filename(field))
|
|
|
|
def _save_FIELD_file(self, field, filename, raw_contents):
|
|
directory = field.get_directory_name()
|
|
try: # Create the date-based directory if it doesn't exist.
|
|
os.makedirs(os.path.join(settings.MEDIA_ROOT, directory))
|
|
except OSError: # Directory probably already exists.
|
|
pass
|
|
filename = field.get_filename(filename)
|
|
|
|
# If the filename already exists, keep adding an underscore to the name of
|
|
# the file until the filename doesn't exist.
|
|
while os.path.exists(os.path.join(settings.MEDIA_ROOT, filename)):
|
|
try:
|
|
dot_index = filename.rindex('.')
|
|
except ValueError: # filename has no dot
|
|
filename += '_'
|
|
else:
|
|
filename = filename[:dot_index] + '_' + filename[dot_index:]
|
|
|
|
# Write the file to disk.
|
|
setattr(self, field.attname, filename)
|
|
|
|
full_filename = self._get_FIELD_filename(field)
|
|
fp = open(full_filename, 'wb')
|
|
fp.write(raw_contents)
|
|
fp.close()
|
|
|
|
# Save the width and/or height, if applicable.
|
|
if isinstance(field, ImageField) and (field.width_field or field.height_field):
|
|
from django.utils.images import get_image_dimensions
|
|
width, height = get_image_dimensions(full_filename)
|
|
if field.width_field:
|
|
setattr(self, field.width_field, width)
|
|
if field.height_field:
|
|
setattr(self, field.height_field, height)
|
|
|
|
# Save the object, because it has changed.
|
|
self.save()
|
|
|
|
_save_FIELD_file.alters_data = True
|
|
|
|
def _get_FIELD_width(self, field):
|
|
return self.__get_image_dimensions(field)[0]
|
|
|
|
def _get_FIELD_height(self, field):
|
|
return self.__get_image_dimensions(field)[1]
|
|
|
|
def _get_image_dimensions(self, field):
|
|
cachename = "__%s_dimensions_cache" % field.name
|
|
if not hasattr(self, cachename):
|
|
from django.utils.images import get_image_dimensions
|
|
filename = self.__get_FIELD_filename(field)()
|
|
setattr(self, cachename, get_image_dimensions(filename))
|
|
return getattr(self, cachename)
|
|
|
|
def _get_foreign_key_object(self, field_with_rel):
|
|
cache_var = field_with_rel.get_cache_name()
|
|
if not hasattr(self, cache_var):
|
|
val = getattr(self, field_with_rel.attname)
|
|
if val is None:
|
|
raise field_with_rel.rel.to.DoesNotExist
|
|
other_field = field_with_rel.rel.get_related_field()
|
|
if other_field.rel:
|
|
params = {'%s__%s__exact' % (field_with_rel.rel.field_name, other_field.rel.field_name): val}
|
|
else:
|
|
params = {'%s__exact' % field_with_rel.rel.field_name: val}
|
|
retrieved_obj = field_with_rel.rel.to._default_manager.get_object(**params)
|
|
setattr(self, cache_var, retrieved_obj)
|
|
return getattr(self, cache_var)
|
|
|
|
def _get_many_to_many_objects(self, field_with_rel):
|
|
cache_var = '_%s_cache' % field_with_rel.name
|
|
if not hasattr(self, cache_var):
|
|
rel_opts = field_with_rel.rel.to._meta
|
|
sql = "SELECT %s FROM %s a, %s b WHERE a.%s = b.%s AND b.%s = %%s %s" % \
|
|
(','.join(['a.%s' % backend.quote_name(f.column) for f in rel_opts.fields]),
|
|
backend.quote_name(rel_opts.db_table),
|
|
backend.quote_name(field_with_rel.get_m2m_db_table(self._meta)),
|
|
backend.quote_name(rel_opts.pk.column),
|
|
backend.quote_name(rel_opts.object_name.lower() + '_id'),
|
|
backend.quote_name(self._meta.object_name.lower() + '_id'), rel_opts.get_order_sql('a'))
|
|
cursor = connection.cursor()
|
|
cursor.execute(sql, [getattr(self, self._meta.pk.attname)])
|
|
setattr(self, cache_var, [field_with_rel.rel.to(*row) for row in cursor.fetchall()])
|
|
return getattr(self, cache_var)
|
|
|
|
def _set_many_to_many_objects(self, id_list, field_with_rel):
|
|
current_ids = [obj.id for obj in self._get_many_to_many_objects(field_with_rel)]
|
|
ids_to_add, ids_to_delete = dict([(i, 1) for i in id_list]), []
|
|
for current_id in current_ids:
|
|
if current_id in id_list:
|
|
del ids_to_add[current_id]
|
|
else:
|
|
ids_to_delete.append(current_id)
|
|
ids_to_add = ids_to_add.keys()
|
|
# Now ids_to_add is a list of IDs to add, and ids_to_delete is a list of IDs to delete.
|
|
if not ids_to_delete and not ids_to_add:
|
|
return False # No change
|
|
rel = field_with_rel.rel.to._meta
|
|
m2m_table = field_with_rel.get_m2m_db_table(self._meta)
|
|
cursor = connection.cursor()
|
|
this_id = getattr(self, self._meta.pk.attname)
|
|
if ids_to_delete:
|
|
sql = "DELETE FROM %s WHERE %s = %%s AND %s IN (%s)" % \
|
|
(backend.quote_name(m2m_table),
|
|
backend.quote_name(self._meta.object_name.lower() + '_id'),
|
|
backend.quote_name(rel.object_name.lower() + '_id'), ','.join(map(str, ids_to_delete)))
|
|
cursor.execute(sql, [this_id])
|
|
if ids_to_add:
|
|
sql = "INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
|
|
(backend.quote_name(m2m_table),
|
|
backend.quote_name(self._meta.object_name.lower() + '_id'),
|
|
backend.quote_name(rel.object_name.lower() + '_id'))
|
|
cursor.executemany(sql, [(this_id, i) for i in ids_to_add])
|
|
connection.commit()
|
|
try:
|
|
delattr(self, '_%s_cache' % field_with_rel.name) # clear cache, if it exists
|
|
except AttributeError:
|
|
pass
|
|
return True
|
|
|
|
_set_many_to_many_objects.alters_data = True
|
|
|
|
def _get_related(self, method_name, rel_class, rel_field, **kwargs):
|
|
kwargs['%s__%s__exact' % (rel_field.name, rel_field.rel.to._meta.pk.name)] = getattr(self, rel_field.rel.get_related_field().attname)
|
|
kwargs.update(rel_field.rel.lookup_overrides)
|
|
return getattr(rel_class._default_manager, method_name)(**kwargs)
|
|
|
|
def _add_related(self, rel_class, rel_field, *args, **kwargs):
|
|
init_kwargs = dict(zip([f.attname for f in rel_class._meta.fields if f != rel_field and not isinstance(f, AutoField)], args))
|
|
init_kwargs.update(kwargs)
|
|
for f in rel_class._meta.fields:
|
|
if isinstance(f, AutoField):
|
|
init_kwargs[f.attname] = None
|
|
init_kwargs[rel_field.name] = self
|
|
obj = rel_class(**init_kwargs)
|
|
obj.save()
|
|
return obj
|
|
|
|
_add_related.alters_data = True
|
|
|
|
# Handles related many-to-many object retrieval.
|
|
# Examples: Album.get_song(), Album.get_song_list(), Album.get_song_count()
|
|
def _get_related_many_to_many(self, method_name, rel_class, rel_field, **kwargs):
|
|
kwargs['%s__%s__exact' % (rel_field.name, self._meta.pk.name)] = getattr(self, self._meta.pk.attname)
|
|
return getattr(rel_class._default_manager, method_name)(**kwargs)
|
|
|
|
# Handles setting many-to-many related objects.
|
|
# Example: Album.set_songs()
|
|
def _set_related_many_to_many(self, rel_class, rel_field, id_list):
|
|
id_list = map(int, id_list) # normalize to integers
|
|
rel = rel_field.rel.to
|
|
m2m_table = rel_field.get_m2m_db_table(rel_opts)
|
|
this_id = getattr(self, self._meta.pk.attname)
|
|
cursor = connection.cursor()
|
|
cursor.execute("DELETE FROM %s WHERE %s = %%s" % \
|
|
(backend.quote_name(m2m_table),
|
|
backend.quote_name(rel.object_name.lower() + '_id')), [this_id])
|
|
sql = "INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
|
|
(backend.quote_name(m2m_table),
|
|
backend.quote_name(rel.object_name.lower() + '_id'),
|
|
backend.quote_name(rel_opts.object_name.lower() + '_id'))
|
|
cursor.executemany(sql, [(this_id, i) for i in id_list])
|
|
connection.commit()
|
|
|
|
############################################
|
|
# HELPER FUNCTIONS (CURRIED MODEL METHODS) #
|
|
############################################
|
|
|
|
# ORDERING METHODS #########################
|
|
|
|
def method_set_order(ordered_obj, self, id_list):
|
|
cursor = connection.cursor()
|
|
# Example: "UPDATE poll_choices SET _order = %s WHERE poll_id = %s AND id = %s"
|
|
sql = "UPDATE %s SET %s = %%s WHERE %s = %%s AND %s = %%s" % \
|
|
(backend.quote_name(ordered_obj.db_table), backend.quote_name('_order'),
|
|
backend.quote_name(ordered_obj.order_with_respect_to.column),
|
|
backend.quote_name(ordered_obj.pk.column))
|
|
rel_val = getattr(self, ordered_obj.order_with_respect_to.rel.field_name)
|
|
cursor.executemany(sql, [(i, rel_val, j) for i, j in enumerate(id_list)])
|
|
connection.commit()
|
|
|
|
def method_get_order(ordered_obj, self):
|
|
cursor = connection.cursor()
|
|
# Example: "SELECT id FROM poll_choices WHERE poll_id = %s ORDER BY _order"
|
|
sql = "SELECT %s FROM %s WHERE %s = %%s ORDER BY %s" % \
|
|
(backend.quote_name(ordered_obj.pk.column),
|
|
backend.quote_name(ordered_obj.db_table),
|
|
backend.quote_name(ordered_obj.order_with_respect_to.column),
|
|
backend.quote_name('_order'))
|
|
rel_val = getattr(self, ordered_obj.order_with_respect_to.rel.field_name)
|
|
cursor.execute(sql, [rel_val])
|
|
return [r[0] for r in cursor.fetchall()]
|
|
|
|
##############################################
|
|
# HELPER FUNCTIONS (CURRIED MODEL FUNCTIONS) #
|
|
##############################################
|
|
|
|
def get_absolute_url(opts, func, self):
|
|
return settings.ABSOLUTE_URL_OVERRIDES.get('%s.%s' % (opts.app_label, opts.module_name), func)(self)
|