Ticket #14394: validate_bad_data_assignment_to_m2m_strictly.patch

File validate_bad_data_assignment_to_m2m_strictly.patch, 6.3 KB (added by igors, 5 years ago)
  • django/db/models/fields/related.py

     
    727727        if not self.field.rel.through._meta.auto_created:
    728728            opts = self.field.rel.through._meta
    729729            raise AttributeError("Cannot set values on a ManyToManyField which specifies an intermediary model.  Use %s.%s's Manager instead." % (opts.app_label, opts.object_name))
     730       
     731        manager = self.__get__(instance)
    730732
    731         manager = self.__get__(instance)
     733        if not hasattr(value, '__iter__'):
     734            raise TypeError(u"Cannot set non iterables on a ManyToManyField.")
     735       
     736        model_instances = []
     737        for obj in value:
     738            if isinstance(obj, manager.model):
     739                if not obj.pk:
     740                    raise TypeError(u"Cannot set %s to a ManyToManyField because it's not saved. Make sure you pass saved models or valid primary keys." % unicode(obj))
     741                model_instances.append(obj)
     742            else:
     743                try:
     744                    model_instances.append(manager.model.objects.get(pk=obj))
     745                except (manager.model.DoesNotExist, ValueError):
     746                    raise TypeError(u"Cannot set value to this ManyToManyField. Make sure you pass valid models or primary keys.")
     747
    732748        manager.clear()
    733         manager.add(*value)
     749        manager.add(*model_instances)
    734750
     751
    735752class ManyToOneRel(object):
    736753    def __init__(self, to, field_name, related_name=None,
    737754            limit_choices_to=None, lookup_overrides=None, parent_link=False):
  • tests/regressiontests/m2m_regress/tests.py

     
    22from django.test import TestCase
    33
    44from models import (SelfRefer, Tag, TagCollection, Entry, SelfReferChild,
    5     SelfReferChildSibling, Worksheet)
     5    SelfReferChildSibling, Worksheet, Line)
    66
    77
    88class M2MRegressionTests(TestCase):
     
    7373
    7474        self.assertQuerysetEqual(c1.tags.all(), ["<Tag: t1>", "<Tag: t2>"])
    7575        self.assertQuerysetEqual(t1.tag_collections.all(), ["<TagCollection: c1>"])
     76
     77    def test_assigning_any_iterable_with_valid_models_to_m2m_works(self):
     78        t1 = Tag.objects.create(name='t1')
     79        t2 = Tag.objects.create(name='t2')
     80       
     81        c1 = TagCollection.objects.create(name='c1')
     82        c1.tags = [t1,t2]
     83
     84        c1 = TagCollection.objects.get(pk=c1.pk)
     85        self.assertQuerysetEqual(c1.tags.all(), ["<Tag: t1>", "<Tag: t2>"])
     86       
     87        c1.tags = set([t1, t2])
     88
     89        c1 = TagCollection.objects.get(pk=c1.pk)
     90        self.assertQuerysetEqual(c1.tags.order_by('name'), ["<Tag: t1>", "<Tag: t2>"])
     91
     92        c1.tags = Tag.objects.filter(name__in=('t1', 't2'))
     93
     94        c1 = TagCollection.objects.get(pk=c1.pk)
     95        self.assertQuerysetEqual(c1.tags.all(), ["<Tag: t1>", "<Tag: t2>"])
     96       
     97        c1.tags = (t1, t2)
     98
     99        c1 = TagCollection.objects.get(pk=c1.pk)
     100        self.assertQuerysetEqual(c1.tags.all(), ["<Tag: t1>", "<Tag: t2>"])
     101   
     102    def test_assigning_any_iterable_with_valid_primary_keys_to_m2m_works(self):
     103        t1 = Tag.objects.create(name='t1')
     104        t2 = Tag.objects.create(name='t2')
     105       
     106        c1 = TagCollection.objects.create(name='c1')
     107        c1.tags = [t1.pk, t2.pk]
     108       
     109        c1 = TagCollection.objects.get(pk=c1.pk)
     110        self.assertQuerysetEqual(c1.tags.all(), ["<Tag: t1>", "<Tag: t2>"])
     111
     112    def test_assigning_non_iterable_to_m2m_doesnt_clear_existing_relations(self):
     113        t1 = Tag.objects.create(name='t1')
     114        t2 = Tag.objects.create(name='t2')
     115       
     116        c1 = TagCollection.objects.create(name='c1')
     117        c1.tags = [t1,t2]
     118       
     119        def invalid_assigment():
     120            c1.tags = 7
     121
     122        self.assertRaises(TypeError, invalid_assigment)
     123
     124        c1 = TagCollection.objects.get(pk=c1.pk)
     125
     126        self.assertQuerysetEqual(c1.tags.all(), ["<Tag: t1>", "<Tag: t2>"])
     127   
     128    def test_assigning_invalid_iterable_to_m2m_doesnt_clear_existing_relations(self):
     129        t1 = Tag.objects.create(name='t1')
     130        t2 = Tag.objects.create(name='t2')
     131       
     132        c1 = TagCollection.objects.create(name='c1')
     133        c1.tags = [t1,t2]
     134
     135        def invalid_assigment():
     136            c1.tags = ["foo"]
     137
     138        self.assertRaises(TypeError, invalid_assigment)
     139
     140        c1 = TagCollection.objects.get(pk=c1.pk)
     141        self.assertQuerysetEqual(c1.tags.all(), ["<Tag: t1>", "<Tag: t2>"])
     142
     143    def test_assigning_iterable_with_invalid_pks_to_m2m_doesnt_clear_existing_relations(self):
     144        t1 = Tag.objects.create(name='t1')
     145        t2 = Tag.objects.create(name='t2')
     146       
     147        c1 = TagCollection.objects.create(name='c1')
     148        c1.tags = [t1,t2]
     149
     150        def invalid_assigment():
     151            c1.tags = [1234]
     152
     153        self.assertRaises(TypeError, invalid_assigment)
     154
     155        c1 = TagCollection.objects.get(pk=c1.pk)
     156        self.assertQuerysetEqual(c1.tags.all(), ["<Tag: t1>", "<Tag: t2>"])
     157
     158    def test_assigning_iterable_with_unsaved_models_to_m2m_doesnt_clear_existing_relations(self):
     159        t1 = Tag.objects.create(name='t1')
     160        t2 = Tag.objects.create(name='t2')
     161        t3 = Tag(name='t3')
     162       
     163        c1 = TagCollection.objects.create(name='c1')
     164        c1.tags = [t1,t2]
     165
     166        def invalid_assigment():
     167            c1.tags = [t3]
     168
     169        self.assertRaises(TypeError, invalid_assigment)
     170
     171        c1 = TagCollection.objects.get(pk=c1.pk)
     172        self.assertQuerysetEqual(c1.tags.all(), ["<Tag: t1>", "<Tag: t2>"])
     173
     174    def test_assigning_iterable_with_wrong_models_to_m2m_doesnt_clear_existing_relations(self):
     175        t1 = Tag.objects.create(name='t1')
     176        t2 = Tag.objects.create(name='t2')
     177        line = Line.objects.create(name="not a tag")
     178
     179        c1 = TagCollection.objects.create(name='c1')
     180        c1.tags = [t1,t2]
     181
     182        def invalid_assigment():
     183            c1.tags = [line]
     184
     185        self.assertRaises(TypeError, invalid_assigment)
     186
     187        c1 = TagCollection.objects.get(pk=c1.pk)
     188        self.assertQuerysetEqual(c1.tags.all(), ["<Tag: t1>", "<Tag: t2>"])
Back to Top