Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #8134 -- Corrected serialization of m2m fields with intermediat…

…e models. Thanks to Rock Howard for the report, and kire for the test case.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@8321 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 63ea57642dd980ff07627964efd445986bc9fe58 1 parent dd970bf
Russell Keith-Magee authored August 12, 2008
5  django/core/serializers/python.py
@@ -49,8 +49,9 @@ def handle_fk_field(self, obj, field):
49 49
         self._current[field.name] = smart_unicode(related, strings_only=True)
50 50
 
51 51
     def handle_m2m_field(self, obj, field):
52  
-        self._current[field.name] = [smart_unicode(related._get_pk_val(), strings_only=True)
53  
-                           for related in getattr(obj, field.name).iterator()]
  52
+        if field.creates_table:
  53
+            self._current[field.name] = [smart_unicode(related._get_pk_val(), strings_only=True)
  54
+                               for related in getattr(obj, field.name).iterator()]
54 55
 
55 56
     def getvalue(self):
56 57
         return self.objects
9  django/core/serializers/xml_serializer.py
@@ -100,10 +100,11 @@ def handle_m2m_field(self, obj, field):
100 100
         serialized as references to the object's PK (i.e. the related *data*
101 101
         is not dumped, just the relation).
102 102
         """
103  
-        self._start_relational_field(field)
104  
-        for relobj in getattr(obj, field.name).iterator():
105  
-            self.xml.addQuickElement("object", attrs={"pk" : smart_unicode(relobj._get_pk_val())})
106  
-        self.xml.endElement("field")
  103
+        if field.creates_table:
  104
+            self._start_relational_field(field)
  105
+            for relobj in getattr(obj, field.name).iterator():
  106
+                self.xml.addQuickElement("object", attrs={"pk" : smart_unicode(relobj._get_pk_val())})
  107
+            self.xml.endElement("field")
107 108
 
108 109
     def _start_relational_field(self, field):
109 110
         """
144  tests/regressiontests/m2m_through_regress/models.py
... ...
@@ -1,12 +1,13 @@
1  
-from django.db import models
2 1
 from datetime import datetime
3 2
 from django.contrib.auth.models import User
  3
+from django.core import management
  4
+from django.db import models
4 5
 
5 6
 # Forward declared intermediate model
6 7
 class Membership(models.Model):
7 8
     person = models.ForeignKey('Person')
8 9
     group = models.ForeignKey('Group')
9  
-    date_joined = models.DateTimeField(default=datetime.now)
  10
+    price = models.IntegerField(default=100)
10 11
     
11 12
     def __unicode__(self):
12 13
         return "%s is a member of %s" % (self.person.name, self.group.name)
@@ -14,7 +15,7 @@ def __unicode__(self):
14 15
 class UserMembership(models.Model):
15 16
     user = models.ForeignKey(User)
16 17
     group = models.ForeignKey('Group')
17  
-    date_joined = models.DateTimeField(default=datetime.now)
  18
+    price = models.IntegerField(default=100)
18 19
     
19 20
     def __unicode__(self):
20 21
         return "%s is a user and member of %s" % (self.user.username, self.group.name)
@@ -99,4 +100,141 @@ def __unicode__(self):
99 100
 >>> roll.user_members.all()
100 101
 [<User: frank>]
101 102
 
  103
+# Regression test for #8134 -- 
  104
+# m2m-through models shouldn't be serialized as m2m fields on the model.
  105
+# Dump the current contents of the database as a JSON fixture
  106
+>>> management.call_command('dumpdata', 'm2m_through_regress', format='json', indent=2)
  107
+[
  108
+  {
  109
+    "pk": 1, 
  110
+    "model": "m2m_through_regress.membership", 
  111
+    "fields": {
  112
+      "person": 1, 
  113
+      "price": 100, 
  114
+      "group": 1
  115
+    }
  116
+  }, 
  117
+  {
  118
+    "pk": 2, 
  119
+    "model": "m2m_through_regress.membership", 
  120
+    "fields": {
  121
+      "person": 1, 
  122
+      "price": 100, 
  123
+      "group": 2
  124
+    }
  125
+  }, 
  126
+  {
  127
+    "pk": 3, 
  128
+    "model": "m2m_through_regress.membership", 
  129
+    "fields": {
  130
+      "person": 2, 
  131
+      "price": 100, 
  132
+      "group": 1
  133
+    }
  134
+  }, 
  135
+  {
  136
+    "pk": 1, 
  137
+    "model": "m2m_through_regress.usermembership", 
  138
+    "fields": {
  139
+      "price": 100, 
  140
+      "group": 1, 
  141
+      "user": 1
  142
+    }
  143
+  }, 
  144
+  {
  145
+    "pk": 2, 
  146
+    "model": "m2m_through_regress.usermembership", 
  147
+    "fields": {
  148
+      "price": 100, 
  149
+      "group": 2, 
  150
+      "user": 1
  151
+    }
  152
+  }, 
  153
+  {
  154
+    "pk": 3, 
  155
+    "model": "m2m_through_regress.usermembership", 
  156
+    "fields": {
  157
+      "price": 100, 
  158
+      "group": 1, 
  159
+      "user": 2
  160
+    }
  161
+  }, 
  162
+  {
  163
+    "pk": 1, 
  164
+    "model": "m2m_through_regress.person", 
  165
+    "fields": {
  166
+      "name": "Bob"
  167
+    }
  168
+  }, 
  169
+  {
  170
+    "pk": 2, 
  171
+    "model": "m2m_through_regress.person", 
  172
+    "fields": {
  173
+      "name": "Jim"
  174
+    }
  175
+  }, 
  176
+  {
  177
+    "pk": 1, 
  178
+    "model": "m2m_through_regress.group", 
  179
+    "fields": {
  180
+      "name": "Rock"
  181
+    }
  182
+  }, 
  183
+  {
  184
+    "pk": 2, 
  185
+    "model": "m2m_through_regress.group", 
  186
+    "fields": {
  187
+      "name": "Roll"
  188
+    }
  189
+  }
  190
+]
  191
+
  192
+# Check the XML serializer too, since it doesn't use the common implementation
  193
+>>> management.call_command('dumpdata', 'm2m_through_regress', format='xml', indent=2)
  194
+<?xml version="1.0" encoding="utf-8"?>
  195
+<django-objects version="1.0">
  196
+  <object pk="1" model="m2m_through_regress.membership">
  197
+    <field to="m2m_through_regress.person" name="person" rel="ManyToOneRel">1</field>
  198
+    <field to="m2m_through_regress.group" name="group" rel="ManyToOneRel">1</field>
  199
+    <field type="IntegerField" name="price">100</field>
  200
+  </object>
  201
+  <object pk="2" model="m2m_through_regress.membership">
  202
+    <field to="m2m_through_regress.person" name="person" rel="ManyToOneRel">1</field>
  203
+    <field to="m2m_through_regress.group" name="group" rel="ManyToOneRel">2</field>
  204
+    <field type="IntegerField" name="price">100</field>
  205
+  </object>
  206
+  <object pk="3" model="m2m_through_regress.membership">
  207
+    <field to="m2m_through_regress.person" name="person" rel="ManyToOneRel">2</field>
  208
+    <field to="m2m_through_regress.group" name="group" rel="ManyToOneRel">1</field>
  209
+    <field type="IntegerField" name="price">100</field>
  210
+  </object>
  211
+  <object pk="1" model="m2m_through_regress.usermembership">
  212
+    <field to="auth.user" name="user" rel="ManyToOneRel">1</field>
  213
+    <field to="m2m_through_regress.group" name="group" rel="ManyToOneRel">1</field>
  214
+    <field type="IntegerField" name="price">100</field>
  215
+  </object>
  216
+  <object pk="2" model="m2m_through_regress.usermembership">
  217
+    <field to="auth.user" name="user" rel="ManyToOneRel">1</field>
  218
+    <field to="m2m_through_regress.group" name="group" rel="ManyToOneRel">2</field>
  219
+    <field type="IntegerField" name="price">100</field>
  220
+  </object>
  221
+  <object pk="3" model="m2m_through_regress.usermembership">
  222
+    <field to="auth.user" name="user" rel="ManyToOneRel">2</field>
  223
+    <field to="m2m_through_regress.group" name="group" rel="ManyToOneRel">1</field>
  224
+    <field type="IntegerField" name="price">100</field>
  225
+  </object>
  226
+  <object pk="1" model="m2m_through_regress.person">
  227
+    <field type="CharField" name="name">Bob</field>
  228
+  </object>
  229
+  <object pk="2" model="m2m_through_regress.person">
  230
+    <field type="CharField" name="name">Jim</field>
  231
+  </object>
  232
+  <object pk="1" model="m2m_through_regress.group">
  233
+    <field type="CharField" name="name">Rock</field>
  234
+  </object>
  235
+  <object pk="2" model="m2m_through_regress.group">
  236
+    <field type="CharField" name="name">Roll</field>
  237
+  </object>
  238
+</django-objects>
  239
+
102 240
 """}
9  tests/regressiontests/serializers_regress/models.py
@@ -132,6 +132,14 @@ class FKDataToField(models.Model):
132 132
 class FKDataToO2O(models.Model):
133 133
     data = models.ForeignKey(O2OData, null=True)
134 134
 
  135
+class M2MIntermediateData(models.Model):
  136
+    data = models.ManyToManyField(Anchor, null=True, through='Intermediate')
  137
+    
  138
+class Intermediate(models.Model):
  139
+    left = models.ForeignKey(M2MIntermediateData)
  140
+    right = models.ForeignKey(Anchor)
  141
+    extra = models.CharField(max_length=30, blank=True, default="doesn't matter")
  142
+
135 143
 # The following test classes are for validating the
136 144
 # deserialization of objects that use a user-defined
137 145
 # field as the primary key.
@@ -243,3 +251,4 @@ class InheritBaseModel(BaseModel):
243 251
 class ExplicitInheritBaseModel(BaseModel):
244 252
     parent = models.OneToOneField(BaseModel)
245 253
     child_data = models.IntegerField()
  254
+    
43  tests/regressiontests/serializers_regress/tests.py
@@ -54,6 +54,20 @@ def m2m_create(pk, klass, data):
54 54
     instance.data = data
55 55
     return [instance]
56 56
 
  57
+def im2m_create(pk, klass, data):
  58
+    instance = klass(id=pk)
  59
+    models.Model.save_base(instance, raw=True)
  60
+    return [instance]
  61
+
  62
+def im_create(pk, klass, data):
  63
+    instance = klass(id=pk)
  64
+    setattr(instance, 'right_id', data['right'])
  65
+    setattr(instance, 'left_id', data['left'])
  66
+    if 'extra' in data:
  67
+        setattr(instance, 'extra', data['extra'])
  68
+    models.Model.save_base(instance, raw=True)
  69
+    return [instance]
  70
+
57 71
 def o2o_create(pk, klass, data):
58 72
     instance = klass()
59 73
     instance.data_id = data
@@ -99,6 +113,19 @@ def m2m_compare(testcase, pk, klass, data):
99 113
     instance = klass.objects.get(id=pk)
100 114
     testcase.assertEqual(data, [obj.id for obj in instance.data.all()])
101 115
 
  116
+def im2m_compare(testcase, pk, klass, data):
  117
+    instance = klass.objects.get(id=pk)
  118
+    #actually nothing else to check, the instance just should exist
  119
+
  120
+def im_compare(testcase, pk, klass, data):
  121
+    instance = klass.objects.get(id=pk)
  122
+    testcase.assertEqual(data['left'], instance.left_id)
  123
+    testcase.assertEqual(data['right'], instance.right_id)
  124
+    if 'extra' in data:
  125
+        testcase.assertEqual(data['extra'], instance.extra)
  126
+    else:
  127
+        testcase.assertEqual("doesn't matter", instance.extra)
  128
+
102 129
 def o2o_compare(testcase, pk, klass, data):
103 130
     instance = klass.objects.get(data=data)
104 131
     testcase.assertEqual(data, instance.data_id)
@@ -119,6 +146,8 @@ def inherited_compare(testcase, pk, klass, data):
119 146
 generic_obj = (generic_create, generic_compare)
120 147
 fk_obj = (fk_create, fk_compare)
121 148
 m2m_obj = (m2m_create, m2m_compare)
  149
+im2m_obj = (im2m_create, im2m_compare)
  150
+im_obj = (im_create, im_compare)
122 151
 o2o_obj = (o2o_create, o2o_compare)
123 152
 pk_obj = (pk_create, pk_compare)
124 153
 inherited_obj = (inherited_create, inherited_compare)
@@ -231,6 +260,20 @@ def inherited_compare(testcase, pk, klass, data):
231 260
     (fk_obj, 452, FKDataToField, None),
232 261
 
233 262
     (fk_obj, 460, FKDataToO2O, 300),
  263
+    
  264
+    (im2m_obj, 470, M2MIntermediateData, None),
  265
+    
  266
+    #testing post- and prereferences and extra fields
  267
+    (im_obj, 480, Intermediate, {'right': 300, 'left': 470}),
  268
+    (im_obj, 481, Intermediate, {'right': 300, 'left': 490}), 
  269
+    (im_obj, 482, Intermediate, {'right': 500, 'left': 470}), 
  270
+    (im_obj, 483, Intermediate, {'right': 500, 'left': 490}), 
  271
+    (im_obj, 484, Intermediate, {'right': 300, 'left': 470, 'extra': "extra"}), 
  272
+    (im_obj, 485, Intermediate, {'right': 300, 'left': 490, 'extra': "extra"}), 
  273
+    (im_obj, 486, Intermediate, {'right': 500, 'left': 470, 'extra': "extra"}), 
  274
+    (im_obj, 487, Intermediate, {'right': 500, 'left': 490, 'extra': "extra"}), 
  275
+    
  276
+    (im2m_obj, 490, M2MIntermediateData, []),
234 277
 
235 278
     (data_obj, 500, Anchor, "Anchor 3"),
236 279
     (data_obj, 501, Anchor, "Anchor 4"),

0 notes on commit 63ea576

Please sign in to comment.
Something went wrong with that request. Please try again.