From 585b7acaa359fc1df07269c1a4b4756bdb6703f7 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 3 Nov 2009 14:02:49 +0000 Subject: Fixed #10109 -- Removed the use of raw SQL in many-to-many fields by introducing an autogenerated through model. This is the first part of Alex Gaynor's GSoC project to add Multi-db support to Django. git-svn-id: http://code.djangoproject.com/svn/django/trunk@11710 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- tests/modeltests/invalid_models/models.py | 1 + tests/modeltests/m2m_through/models.py | 10 +-- .../regressiontests/m2m_through_regress/models.py | 8 +- .../model_inheritance_regress/models.py | 66 ++++++++++++++++ .../regressiontests/serializers_regress/models.py | 8 +- tests/regressiontests/signals_regress/__init__.py | 0 tests/regressiontests/signals_regress/models.py | 89 ++++++++++++++++++++++ 7 files changed, 170 insertions(+), 12 deletions(-) create mode 100644 tests/regressiontests/signals_regress/__init__.py create mode 100644 tests/regressiontests/signals_regress/models.py (limited to 'tests') diff --git a/tests/modeltests/invalid_models/models.py b/tests/modeltests/invalid_models/models.py index c033d31237..af199635e6 100644 --- a/tests/modeltests/invalid_models/models.py +++ b/tests/modeltests/invalid_models/models.py @@ -182,6 +182,7 @@ class UniqueM2M(models.Model): """ Model to test for unique ManyToManyFields, which are invalid. """ unique_people = models.ManyToManyField( Person, unique=True ) + model_errors = """invalid_models.fielderrors: "charfield": CharFields require a "max_length" attribute. invalid_models.fielderrors: "decimalfield": DecimalFields require a "decimal_places" attribute. invalid_models.fielderrors: "decimalfield": DecimalFields require a "max_digits" attribute. diff --git a/tests/modeltests/m2m_through/models.py b/tests/modeltests/m2m_through/models.py index 10aa163343..16f303d02e 100644 --- a/tests/modeltests/m2m_through/models.py +++ b/tests/modeltests/m2m_through/models.py @@ -133,7 +133,7 @@ AttributeError: 'ManyRelatedManager' object has no attribute 'add' >>> rock.members.create(name='Anne') Traceback (most recent call last): ... -AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use Membership's Manager instead. +AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use m2m_through.Membership's Manager instead. # Remove has similar complications, and is not provided either. >>> rock.members.remove(jim) @@ -160,7 +160,7 @@ AttributeError: 'ManyRelatedManager' object has no attribute 'remove' >>> rock.members = backup Traceback (most recent call last): ... -AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model. Use Membership's Manager instead. +AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model. Use m2m_through.Membership's Manager instead. # Let's re-save those instances that we've cleared. >>> m1.save() @@ -184,7 +184,7 @@ AttributeError: 'ManyRelatedManager' object has no attribute 'add' >>> bob.group_set.create(name='Funk') Traceback (most recent call last): ... -AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use Membership's Manager instead. +AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use m2m_through.Membership's Manager instead. # Remove has similar complications, and is not provided either. >>> jim.group_set.remove(rock) @@ -209,7 +209,7 @@ AttributeError: 'ManyRelatedManager' object has no attribute 'remove' >>> jim.group_set = backup Traceback (most recent call last): ... -AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model. Use Membership's Manager instead. +AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model. Use m2m_through.Membership's Manager instead. # Let's re-save those instances that we've cleared. >>> m1.save() @@ -334,4 +334,4 @@ AttributeError: Cannot set values on a ManyToManyField which specifies an interm # QuerySet's distinct() method can correct this problem. >>> Person.objects.filter(membership__date_joined__gt=datetime(2004, 1, 1)).distinct() [, ] -"""} \ No newline at end of file +"""} diff --git a/tests/regressiontests/m2m_through_regress/models.py b/tests/regressiontests/m2m_through_regress/models.py index dcf5f8115b..56aecd6975 100644 --- a/tests/regressiontests/m2m_through_regress/models.py +++ b/tests/regressiontests/m2m_through_regress/models.py @@ -84,22 +84,22 @@ __test__ = {'API_TESTS':""" >>> bob.group_set = [] Traceback (most recent call last): ... -AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model. Use Membership's Manager instead. +AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model. Use m2m_through_regress.Membership's Manager instead. >>> roll.members = [] Traceback (most recent call last): ... -AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model. Use Membership's Manager instead. +AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model. Use m2m_through_regress.Membership's Manager instead. >>> rock.members.create(name='Anne') Traceback (most recent call last): ... -AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use Membership's Manager instead. +AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use m2m_through_regress.Membership's Manager instead. >>> bob.group_set.create(name='Funk') Traceback (most recent call last): ... -AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use Membership's Manager instead. +AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use m2m_through_regress.Membership's Manager instead. # Now test that the intermediate with a relationship outside # the current app (i.e., UserMembership) workds diff --git a/tests/regressiontests/model_inheritance_regress/models.py b/tests/regressiontests/model_inheritance_regress/models.py index a1ee6a2d86..6a804a97c1 100644 --- a/tests/regressiontests/model_inheritance_regress/models.py +++ b/tests/regressiontests/model_inheritance_regress/models.py @@ -110,6 +110,36 @@ class DerivedM(BaseM): return "PK = %d, base_name = %s, derived_name = %s" \ % (self.customPK, self.base_name, self.derived_name) +# Check that abstract classes don't get m2m tables autocreated. +class Person(models.Model): + name = models.CharField(max_length=100) + + class Meta: + ordering = ('name',) + + def __unicode__(self): + return self.name + +class AbstractEvent(models.Model): + name = models.CharField(max_length=100) + attendees = models.ManyToManyField(Person, related_name="%(class)s_set") + + class Meta: + abstract = True + ordering = ('name',) + + def __unicode__(self): + return self.name + +class BirthdayParty(AbstractEvent): + pass + +class BachelorParty(AbstractEvent): + pass + +class MessyBachelorParty(BachelorParty): + pass + __test__ = {'API_TESTS':""" # Regression for #7350, #7202 # Check that when you create a Parent object with a specific reference to an @@ -318,5 +348,41 @@ True >>> ParkingLot3._meta.get_ancestor_link(Place).name # the child->parent link "parent" +# Check that many-to-many relations defined on an abstract base class +# are correctly inherited (and created) on the child class. +>>> p1 = Person.objects.create(name='Alice') +>>> p2 = Person.objects.create(name='Bob') +>>> p3 = Person.objects.create(name='Carol') +>>> p4 = Person.objects.create(name='Dave') + +>>> birthday = BirthdayParty.objects.create(name='Birthday party for Alice') +>>> birthday.attendees = [p1, p3] + +>>> bachelor = BachelorParty.objects.create(name='Bachelor party for Bob') +>>> bachelor.attendees = [p2, p4] + +>>> print p1.birthdayparty_set.all() +[] + +>>> print p1.bachelorparty_set.all() +[] + +>>> print p2.bachelorparty_set.all() +[] + +# Check that a subclass of a subclass of an abstract model +# doesn't get it's own accessor. +>>> p2.messybachelorparty_set.all() +Traceback (most recent call last): +... +AttributeError: 'Person' object has no attribute 'messybachelorparty_set' + +# ... but it does inherit the m2m from it's parent +>>> messy = MessyBachelorParty.objects.create(name='Bachelor party for Dave') +>>> messy.attendees = [p4] + +>>> p4.bachelorparty_set.all() +[, ] + """} diff --git a/tests/regressiontests/serializers_regress/models.py b/tests/regressiontests/serializers_regress/models.py index 95119d4b05..313ed8fc3a 100644 --- a/tests/regressiontests/serializers_regress/models.py +++ b/tests/regressiontests/serializers_regress/models.py @@ -105,6 +105,9 @@ class Anchor(models.Model): data = models.CharField(max_length=30) + class Meta: + ordering = ('id',) + class UniqueAnchor(models.Model): """This is a model that can be used as something for other models to point at""" @@ -135,7 +138,7 @@ class FKDataToO2O(models.Model): class M2MIntermediateData(models.Model): data = models.ManyToManyField(Anchor, null=True, through='Intermediate') - + class Intermediate(models.Model): left = models.ForeignKey(M2MIntermediateData) right = models.ForeignKey(Anchor) @@ -242,7 +245,7 @@ class AbstractBaseModel(models.Model): class InheritAbstractModel(AbstractBaseModel): child_data = models.IntegerField() - + class BaseModel(models.Model): parent_data = models.IntegerField() @@ -252,4 +255,3 @@ class InheritBaseModel(BaseModel): class ExplicitInheritBaseModel(BaseModel): parent = models.OneToOneField(BaseModel) child_data = models.IntegerField() - \ No newline at end of file diff --git a/tests/regressiontests/signals_regress/__init__.py b/tests/regressiontests/signals_regress/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regressiontests/signals_regress/models.py b/tests/regressiontests/signals_regress/models.py new file mode 100644 index 0000000000..2b40ebc21a --- /dev/null +++ b/tests/regressiontests/signals_regress/models.py @@ -0,0 +1,89 @@ +""" +Testing signals before/after saving and deleting. +""" + +from django.db import models + +class Author(models.Model): + name = models.CharField(max_length=20) + + def __unicode__(self): + return self.name + +class Book(models.Model): + name = models.CharField(max_length=20) + authors = models.ManyToManyField(Author) + + def __unicode__(self): + return self.name + +def pre_save_test(signal, sender, instance, **kwargs): + print 'pre_save signal,', instance + if kwargs.get('raw'): + print 'Is raw' + +def post_save_test(signal, sender, instance, **kwargs): + print 'post_save signal,', instance + if 'created' in kwargs: + if kwargs['created']: + print 'Is created' + else: + print 'Is updated' + if kwargs.get('raw'): + print 'Is raw' + +def pre_delete_test(signal, sender, instance, **kwargs): + print 'pre_delete signal,', instance + print 'instance.id is not None: %s' % (instance.id != None) + +def post_delete_test(signal, sender, instance, **kwargs): + print 'post_delete signal,', instance + print 'instance.id is not None: %s' % (instance.id != None) + +__test__ = {'API_TESTS':""" + +# Save up the number of connected signals so that we can check at the end +# that all the signals we register get properly unregistered (#9989) +>>> pre_signals = (len(models.signals.pre_save.receivers), +... len(models.signals.post_save.receivers), +... len(models.signals.pre_delete.receivers), +... len(models.signals.post_delete.receivers)) + +>>> models.signals.pre_save.connect(pre_save_test) +>>> models.signals.post_save.connect(post_save_test) +>>> models.signals.pre_delete.connect(pre_delete_test) +>>> models.signals.post_delete.connect(post_delete_test) + +>>> a1 = Author(name='Neal Stephenson') +>>> a1.save() +pre_save signal, Neal Stephenson +post_save signal, Neal Stephenson +Is created + +>>> b1 = Book(name='Snow Crash') +>>> b1.save() +pre_save signal, Snow Crash +post_save signal, Snow Crash +Is created + +# Assigning to m2m shouldn't generate an m2m signal +>>> b1.authors = [a1] + +# Removing an author from an m2m shouldn't generate an m2m signal +>>> b1.authors = [] + +>>> models.signals.post_delete.disconnect(post_delete_test) +>>> models.signals.pre_delete.disconnect(pre_delete_test) +>>> models.signals.post_save.disconnect(post_save_test) +>>> models.signals.pre_save.disconnect(pre_save_test) + +# Check that all our signals got disconnected properly. +>>> post_signals = (len(models.signals.pre_save.receivers), +... len(models.signals.post_save.receivers), +... len(models.signals.pre_delete.receivers), +... len(models.signals.post_delete.receivers)) + +>>> pre_signals == post_signals +True + +"""} -- cgit v1.3