On Tuesday 14 February 2006 05:17, Russell Keith-Magee wrote: > On 2/14/06, Adrian Holovaty <[EMAIL PROTECTED]> wrote: > > This change, at a glance, looks good. Go ahead and commit it. > > Ok. Will do.
After your commit, I updated my patch that removes '.all()' for related objects, to take account of these changes -- it's attached. Regards, Luke -- "Making it up? Why should I want to make anything up? Life's bad enough as it is without wanting to invent any more of it." (Marvin the paranoid android) Luke Plant || L.Plant.98 (at) cantab.net || http://lukeplant.me.uk/
Index: django/db/models/base.py =================================================================== --- django/db/models/base.py (revision 2308) +++ django/db/models/base.py (working copy) @@ -200,7 +200,7 @@ else: sub_obj._collect_sub_objects(seen_objs) else: - for sub_obj in getattr(self, rel_opts_name).all(): + for sub_obj in getattr(self, rel_opts_name): sub_obj._collect_sub_objects(seen_objs) def delete(self): Index: django/db/models/fields/related.py =================================================================== --- django/db/models/fields/related.py (revision 2308) +++ django/db/models/fields/related.py (working copy) @@ -7,6 +7,7 @@ from django.core import validators from django import forms from django.dispatch import dispatcher +from django.db.models.query import QuerySet, Q # For Python 2.3 if not hasattr(__builtins__, 'set'): @@ -94,7 +95,7 @@ setattr(instance, cache_name, rel_obj) return rel_obj -def _add_m2m_items(rel_manager_inst, managerclass, rel_model, join_table, this_col_name, +def _add_m2m_items(rel_manager, rel_model, join_table, this_col_name, rel_col_name, this_pk_val, *objs, **kwargs): # Utility function used by the ManyRelatedObjectsDescriptors # to do addition to a many-to-many field. @@ -112,7 +113,7 @@ # Create the related object. if kwargs: assert len(objs) == 0, "add() can't be passed both positional and keyword arguments" - objs = [managerclass.add(rel_manager_inst, **kwargs)] + objs = [rel_manager.add(**kwargs)] else: assert len(objs) > 0, "add() must be passed either positional or keyword arguments" for obj in objs: @@ -203,47 +204,53 @@ this_col_name = this_opts.object_name.lower() + '_id' rel_col_name = rel_opts.object_name.lower() + '_id' - # Dynamically create a class that subclasses the related - # model's default manager. - superclass = self.related.model._default_manager.__class__ + # Dynamically create a class that subclasses QuerySet + rel_manager = self.related.model._default_manager - class RelatedManager(superclass): - def get_query_set(self): - return superclass.get_query_set(self).filter(**(self.core_filters)) + class RelatedManager(QuerySet): if rel_type == "o2m": def add(self, **kwargs): kwargs.update({rel_field.name: instance}) - return superclass.add(self, **kwargs) + self._result_cache = None + return rel_manager.add(**kwargs) else: def add(self, *objs, **kwargs): - _add_m2m_items(self, superclass, rel_model, join_table, this_col_name, + self._result_cache = None + _add_m2m_items(rel_manager, rel_model, join_table, this_col_name, rel_col_name, instance._get_pk_val(), *objs, **kwargs) add.alters_data = True if rel_type == "o2m": def remove(self, *objs): + self._result_cache = None pass # TODO else: def remove(self, *objs): + self._result_cache = None _remove_m2m_items(rel_model, join_table, this_col_name, rel_col_name, instance._get_pk_val(), *objs) remove.alters_data = True if rel_type == "o2m": def clear(self): + self._result_cache = None pass # TODO else: def clear(self): + self._result_cache = None _clear_m2m_items(join_table, this_col_name, instance._get_pk_val()) clear.alters_data = True + if self.rel_type == 'o2m': + core_filters = {'%s__%s__exact' % (rel_field.name, rel_field.rel.to._meta.pk.name): getattr(instance, rel_field.rel.get_related_field().attname)} + else: + core_filters = {'%s__%s__exact' % (rel_field.name, instance_type._meta.pk.name): instance._get_pk_val()} + + def __init__(self, model=None): + QuerySet.__init__(self, model) + self._filters = Q(**self.core_filters) + manager = RelatedManager() - - if self.rel_type == 'o2m': - manager.core_filters = {'%s__%s__exact' % (rel_field.name, rel_field.rel.to._meta.pk.name): getattr(instance, rel_field.rel.get_related_field().attname)} - else: - manager.core_filters = {'%s__%s__exact' % (rel_field.name, instance_type._meta.pk.name): instance._get_pk_val()} - manager.model = self.related.model return manager @@ -273,29 +280,32 @@ # Dynamically create a class that subclasses the related # model's default manager. - superclass = self.rel_model._default_manager.__class__ + rel_manager = self.rel_model._default_manager - class RelatedManager(superclass): - def get_query_set(self): - return superclass.get_query_set(self).extra( - tables=(join_table,), - where=[ + class RelatedManager(QuerySet): + def __init__(self, model=None): + QuerySet.__init__(self, model) + self._tables = (join_table,) + self._where=[ '%s.%s = %s.%s' % (qn(rel_opts.db_table), qn(rel_opts.pk.column), join_table, rel_col_name), '%s.%s = %%s' % (join_table, this_col_name) - ], - params = [instance._get_pk_val()] - ) + ] + self._params = [instance._get_pk_val()] + def add(self, *objs, **kwargs): - _add_m2m_items(self, superclass, rel_model, join_table, this_col_name, + self._result_cache = None + _add_m2m_items(rel_manager, rel_model, join_table, this_col_name, rel_col_name, instance._get_pk_val(), *objs, **kwargs) add.alters_data = True def remove(self, *objs): + self._result_cache = None _remove_m2m_items(rel_model, join_table, this_col_name, rel_col_name, instance._get_pk_val(), *objs) remove.alters_data = True def clear(self): + self._result_cache = None _clear_m2m_items(join_table, this_col_name, instance._get_pk_val()) clear.alters_data = True @@ -489,7 +499,7 @@ def flatten_data(self, follow, obj = None): new_data = {} if obj: - instance_ids = [instance._get_pk_val() for instance in getattr(obj, self.name).all()] + instance_ids = [instance._get_pk_val() for instance in getattr(obj, self.name)] if self.rel.raw_id_admin: new_data[self.name] = ",".join([str(id) for id in instance_ids]) else: Index: django/contrib/auth/models.py =================================================================== --- django/contrib/auth/models.py (revision 2308) +++ django/contrib/auth/models.py (working copy) @@ -156,7 +156,7 @@ def get_all_permissions(self): if not hasattr(self, '_perm_cache'): import sets - self._perm_cache = sets.Set(["%s.%s" % (p.package_id, p.codename) for p in self.user_permissions.all()]) + self._perm_cache = sets.Set(["%s.%s" % (p.package_id, p.codename) for p in self.user_permissions]) self._perm_cache.update(self.get_group_permissions()) return self._perm_cache @@ -183,7 +183,7 @@ def get_and_delete_messages(self): messages = [] - for m in self.message_set.all(): + for m in self.message_set: messages.append(m.message) m.delete() return messages Index: django/contrib/admin/views/main.py =================================================================== --- django/contrib/admin/views/main.py (revision 2308) +++ django/contrib/admin/views/main.py (working copy) @@ -387,7 +387,7 @@ _get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, related.opts, current_depth+2) else: has_related_objs = False - for sub_obj in getattr(obj, rel_opts_name).all(): + for sub_obj in getattr(obj, rel_opts_name): has_related_objs = True if related.field.rel.edit_inline or not related.opts.admin: # Don't display link to edit, because it either has no @@ -410,7 +410,7 @@ opts_seen.append(related.opts) rel_opts_name = related.get_accessor_name() has_related_objs = False - for sub_obj in getattr(obj, rel_opts_name).all(): + for sub_obj in getattr(obj, rel_opts_name): has_related_objs = True if related.field.rel.edit_inline or not related.opts.admin: # Don't display link to edit, because it either has no Index: tests/modeltests/m2o_recursive/models.py =================================================================== --- tests/modeltests/m2o_recursive/models.py (revision 2308) +++ tests/modeltests/m2o_recursive/models.py (working copy) @@ -26,7 +26,7 @@ >>> c = Category(id=None, name='Child category', parent=r) >>> c.save() ->>> r.child_set.all() +>>> r.child_set [Child category] >>> r.child_set.get(name__startswith='Child') Child category @@ -35,7 +35,7 @@ ... DoesNotExist ->>> c.child_set.all() +>>> c.child_set [] >>> c.parent Root category Index: tests/modeltests/many_to_many/models.py =================================================================== --- tests/modeltests/many_to_many/models.py (revision 2308) +++ tests/modeltests/many_to_many/models.py (working copy) @@ -51,17 +51,17 @@ >>> a2.publications.add(title='Highlights for Children') # Article objects have access to their related Publication objects. ->>> a1.publications.all() +>>> a1.publications [The Python Journal] ->>> a2.publications.all() +>>> a2.publications [The Python Journal, Science News, Science Weekly, Highlights for Children] # Publication objects have access to their related Article objects. ->>> p2.article_set.all() +>>> p2.article_set [NASA uses Python] >>> p1.article_set.order_by('headline') [Django lets you build Web apps easily, NASA uses Python] ->>> Publication.objects.get(id=4).article_set.all() +>>> Publication.objects.get(id=4).article_set [NASA uses Python] # We can perform kwarg queries across m2m relationships @@ -97,7 +97,7 @@ >>> Publication.objects.all() [Science News, Science Weekly, Highlights for Children] >>> a1 = Article.objects.get(pk=1) ->>> a1.publications.all() +>>> a1.publications [] # If we delete an Article, its Publications won't be able to access it. @@ -111,31 +111,31 @@ >>> a4 = Article(headline='NASA finds intelligent life on Earth') >>> a4.save() >>> p2.article_set.add(a4) ->>> p2.article_set.all() +>>> p2.article_set [NASA finds intelligent life on Earth] ->>> a4.publications.all() +>>> a4.publications [Science News] # Adding via the other end using keywords >>> p2.article_set.add(headline='Oxygen-free diet works wonders') ->>> p2.article_set.all().order_by('headline') +>>> p2.article_set.order_by('headline') [NASA finds intelligent life on Earth, Oxygen-free diet works wonders] ->>> a5 = p2.article_set.all().order_by('headline')[1] ->>> a5.publications.all() +>>> a5 = p2.article_set.order_by('headline')[1] +>>> a5.publications [Science News] # Removing publication from an article: >>> a4.publications.remove(p2) ->>> p2.article_set.all().order_by('headline') +>>> p2.article_set.order_by('headline') [Oxygen-free diet works wonders] ->>> a4.publications.all() +>>> a4.publications [] # And from the other end >>> p2.article_set.remove(a5) >>> p2.article_set.order_by('headline') [] ->>> a5.publications.all() +>>> a5.publications [] # You can clear the whole lot: @@ -145,21 +145,21 @@ >>> a4.publications.order_by('title') [Science News, Science Weekly] >>> p2.article_set.clear() ->>> p2.article_set.all() +>>> p2.article_set [] ->>> a4.publications.all() +>>> a4.publications [Science Weekly] # And you can clear from the other end >>> p2.article_set.add(a4, a5) ->>> p2.article_set.all().order_by('headline') +>>> p2.article_set.order_by('headline') [NASA finds intelligent life on Earth, Oxygen-free diet works wonders] >>> a4.publications.order_by('title') [Science News, Science Weekly] >>> a4.publications.clear() ->>> a4.publications.all() +>>> a4.publications [] ->>> p2.article_set.all().order_by('headline') +>>> p2.article_set.order_by('headline') [Oxygen-free diet works wonders] # Recreate the article and Publication we just deleted. @@ -175,7 +175,7 @@ [Highlights for Children, The Python Journal] >>> Article.objects.all() [Django lets you build Web apps easily, NASA finds intelligent life on Earth, Oxygen-free diet works wonders, NASA uses Python] ->>> a2.publications.all() +>>> a2.publications [The Python Journal] # Bulk delete some articles - references to deleted objects should go @@ -187,7 +187,7 @@ # After the delete, the QuerySet cache needs to be cleared, and the referenced objects should be gone >>> print q [] ->>> p1.article_set.all() +>>> p1.article_set [NASA uses Python] Index: tests/modeltests/custom_pk/models.py =================================================================== --- tests/modeltests/custom_pk/models.py (revision 2308) +++ tests/modeltests/custom_pk/models.py (working copy) @@ -62,9 +62,9 @@ >>> b = Business(name='Sears') >>> b.save() >>> b.employees.add(dan, fran) ->>> b.employees.all() +>>> b.employees [Dan Jones, Fran Jones] ->>> fran.business_set.all() +>>> fran.business_set [Sears] >>> Business.objects.in_bulk(['Sears']) {'Sears': Sears} Index: tests/modeltests/m2o_recursive2/models.py =================================================================== --- tests/modeltests/m2o_recursive2/models.py (revision 2308) +++ tests/modeltests/m2o_recursive2/models.py (working copy) @@ -32,12 +32,12 @@ Jane Smith >>> kid.father John Smith Senior ->>> dad.fathers_child_set.all() +>>> dad.fathers_child_set [John Smith Junior] ->>> mom.mothers_child_set.all() +>>> mom.mothers_child_set [John Smith Junior] ->>> kid.mothers_child_set.all() +>>> kid.mothers_child_set [] ->>> kid.fathers_child_set.all() +>>> kid.fathers_child_set [] """ Index: tests/modeltests/m2m_multiple/models.py =================================================================== --- tests/modeltests/m2m_multiple/models.py (revision 2308) +++ tests/modeltests/m2m_multiple/models.py (working copy) @@ -50,30 +50,30 @@ >>> a2.primary_categories.add(c1, c2) >>> a2.secondary_categories.add(c4) ->>> a1.primary_categories.all() +>>> a1.primary_categories [Crime, News] ->>> a2.primary_categories.all() +>>> a2.primary_categories [News, Sports] ->>> a1.secondary_categories.all() +>>> a1.secondary_categories [Life] ->>> c1.primary_article_set.all() +>>> c1.primary_article_set [Area man runs] ->>> c1.secondary_article_set.all() +>>> c1.secondary_article_set [] ->>> c2.primary_article_set.all() +>>> c2.primary_article_set [Area man steals, Area man runs] ->>> c2.secondary_article_set.all() +>>> c2.secondary_article_set [] ->>> c3.primary_article_set.all() +>>> c3.primary_article_set [Area man steals] ->>> c3.secondary_article_set.all() +>>> c3.secondary_article_set [] ->>> c4.primary_article_set.all() +>>> c4.primary_article_set [] ->>> c4.secondary_article_set.all() +>>> c4.secondary_article_set [Area man steals, Area man runs] """ Index: tests/modeltests/m2m_intermediary/models.py =================================================================== --- tests/modeltests/m2m_intermediary/models.py (revision 2308) +++ tests/modeltests/m2m_intermediary/models.py (working copy) @@ -63,6 +63,6 @@ This is a test >>> w2.article This is a test ->>> r1.writer_set.all() +>>> r1.writer_set [John Smith (Main writer)] """