On Monday 13 February 2006 18:52, Luke Plant wrote: > And there isn't a technical reason for it either - it should be > relatively easy to make it go away.
The attached patch does so - the .all() call is now optional on related objects (it's still there due to inheritance, but I've fixed it so it does the right thing). The patch is probably pretty rough, but it passes all the tests that were passing before (i.e. all but 4), and I've removed all (or almost all I think) instances of .all() on related objects in the tests. I don't have any more time to work on this though! Luke -- Life is complex. It has both real and imaginary components. Luke Plant || L.Plant.98 (at) cantab.net || http://lukeplant.me.uk/
Index: django/db/models/fields/related.py =================================================================== --- django/db/models/fields/related.py (revision 2306) +++ 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'): @@ -176,6 +177,10 @@ [this_pk_val]) connection.commit() +class RelatedManagerQuerySet(QuerySet): + def all(self): + return self._clone() + class ManyRelatedObjectsDescriptor(object): # This class provides the functionality that makes the related-object # managers available as attributes on a model class, for fields that have @@ -204,46 +209,53 @@ rel_col_name = rel_opts.object_name.lower() + '_id' # Dynamically create a class that subclasses the related - # model's default manager. + # model's default manager and a QuerySet superclass = self.related.model._default_manager.__class__ - class RelatedManager(superclass): - def get_query_set(self): - return superclass.get_query_set(self).filter(**(self.core_filters)) + class RelatedManager(RelatedManagerQuerySet, superclass): if rel_type == "o2m": def add(self, **kwargs): kwargs.update({rel_field.name: instance}) + self._result_cache = None return superclass.add(self, **kwargs) else: def add(self, *objs, **kwargs): + self._result_cache = None _add_m2m_items(self, superclass, 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 @@ -275,27 +287,30 @@ # model's default manager. superclass = self.rel_model._default_manager.__class__ - class RelatedManager(superclass): - def get_query_set(self): - return superclass.get_query_set(self).extra( - tables=(join_table,), - where=[ + class RelatedManager(RelatedManagerQuerySet, superclass): + 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): + self._result_cache = None _add_m2m_items(self, superclass, 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 Index: tests/modeltests/m2o_recursive/models.py =================================================================== --- tests/modeltests/m2o_recursive/models.py (revision 2306) +++ 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 2306) +++ 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 @@ -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] Index: tests/modeltests/custom_pk/models.py =================================================================== --- tests/modeltests/custom_pk/models.py (revision 2306) +++ 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 2306) +++ 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 2306) +++ 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 2306) +++ 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)] """