Browse Source

Remove Product.related_products

Oscar already has child products to model tightly coupled products, and
"recommended products" to model products that are related (which we
expect to mostly be used for upselling). Product.related_products was a
third option that sat somewhere inbetween, and which I think will often
not be needed, has a mediocre interface in the dashboard and beyond that
no special treatment in the codebase. Furthermore, I see it being
confusing when trying to model a catalogue, and it's easily added back
if necessary.
master
Maik Hoepfel 11 years ago
parent
commit
0e67e20ab3

+ 8
- 0
docs/source/releases/v0.8.rst View File

@@ -119,6 +119,14 @@ Backwards incompatible changes in 0.8
119 119
   just extend the ``Calculator`` class and adjust the weights.
120 120
 * ``Product.score`` was just duplicating ``ProductRecord.score`` and has been
121 121
   removed. Use ``Product.stats.score`` instead.
122
+* Oscar has child products to model tightly coupled products, and
123
+  ``Product.recommended_products`` to model products that are loosely related
124
+  (e.g. used for upselling). ``Product.related_products`` was a
125
+  third option that sat somewhere in between, and which was not well supported.
126
+  We fear it adds confusion, and in the spirit of keeping Oscare core lean,
127
+  has been removed. If you're using it, switch to
128
+  ``Product.recommended_products`` or just add the field back to your
129
+  custom Product instance and ``ProductForm`` when migrating.
122 130
 
123 131
 .. _rewritten: https://github.com/tangentlabs/django-oscar/commit/d8b4dbfed17be90846ea4bc47b5f7b39ad944c24
124 132
 

+ 6
- 9
oscar/apps/catalogue/abstract_models.py View File

@@ -241,16 +241,12 @@ class AbstractProduct(models.Model):
241 241
                     "something like a personalised message to be printed on "
242 242
                     "a T-shirt."))
243 243
 
244
-    related_products = models.ManyToManyField(
245
-        'catalogue.Product', related_name='relations', blank=True,
246
-        verbose_name=_("Related Products"),
247
-        help_text=_("Related items are things like different formats of the "
248
-                    "same book.  Grouping them together allows better linking "
249
-                    "between products on the site."))
250
-
251 244
     recommended_products = models.ManyToManyField(
252 245
         'catalogue.Product', through='ProductRecommendation', blank=True,
253
-        verbose_name=_("Recommended Products"))
246
+        verbose_name=_("Recommended Products"),
247
+        help_text=_("These products are products that are in some way related"
248
+                    "to the product. They can be a different version of the"
249
+                    "same product, or e.g. accessories for upselling."))
254 250
 
255 251
     # Denormalised product rating - used by reviews app.
256 252
     # Product has no ratings if rating is None
@@ -505,7 +501,7 @@ class AbstractProduct(models.Model):
505 501
             status=self.reviews.model.APPROVED).count()
506 502
 
507 503
 
508
-class ProductRecommendation(models.Model):
504
+class AbstractProductRecommendation(models.Model):
509 505
     """
510 506
     'Through' model for product recommendations
511 507
     """
@@ -520,6 +516,7 @@ class ProductRecommendation(models.Model):
520 516
                     ' value will appear before one with a lower ranking.'))
521 517
 
522 518
     class Meta:
519
+        abstract = True
523 520
         verbose_name = _('Product Recommendation')
524 521
         verbose_name_plural = _('Product Recomendations')
525 522
         ordering = ['primary', '-ranking']

+ 148
- 0
oscar/apps/catalogue/migrations/0023_auto.py View File

@@ -0,0 +1,148 @@
1
+# -*- coding: utf-8 -*-
2
+from south.utils import datetime_utils as datetime
3
+from south.db import db
4
+from south.v2 import SchemaMigration
5
+from django.db import models
6
+
7
+
8
+class Migration(SchemaMigration):
9
+
10
+    def forwards(self, orm):
11
+        # Removing M2M table for field related_products on 'Product'
12
+        db.delete_table(db.shorten_name(u'catalogue_product_related_products'))
13
+
14
+
15
+    def backwards(self, orm):
16
+        # Adding M2M table for field related_products on 'Product'
17
+        m2m_table_name = db.shorten_name(u'catalogue_product_related_products')
18
+        db.create_table(m2m_table_name, (
19
+            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
20
+            ('from_product', models.ForeignKey(orm[u'catalogue.product'], null=False)),
21
+            ('to_product', models.ForeignKey(orm[u'catalogue.product'], null=False))
22
+        ))
23
+        db.create_unique(m2m_table_name, ['from_product_id', 'to_product_id'])
24
+
25
+
26
+    models = {
27
+        u'catalogue.attributeentity': {
28
+            'Meta': {'object_name': 'AttributeEntity'},
29
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
30
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
31
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'blank': 'True'}),
32
+            'type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'entities'", 'to': u"orm['catalogue.AttributeEntityType']"})
33
+        },
34
+        u'catalogue.attributeentitytype': {
35
+            'Meta': {'object_name': 'AttributeEntityType'},
36
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
37
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
38
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'blank': 'True'})
39
+        },
40
+        u'catalogue.attributeoption': {
41
+            'Meta': {'object_name': 'AttributeOption'},
42
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'options'", 'to': u"orm['catalogue.AttributeOptionGroup']"}),
43
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
44
+            'option': ('django.db.models.fields.CharField', [], {'max_length': '255'})
45
+        },
46
+        u'catalogue.attributeoptiongroup': {
47
+            'Meta': {'object_name': 'AttributeOptionGroup'},
48
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
49
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
50
+        },
51
+        u'catalogue.category': {
52
+            'Meta': {'ordering': "['full_name']", 'object_name': 'Category'},
53
+            'depth': ('django.db.models.fields.PositiveIntegerField', [], {}),
54
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
55
+            'full_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
56
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
57
+            'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
58
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
59
+            'numchild': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
60
+            'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
61
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'})
62
+        },
63
+        u'catalogue.option': {
64
+            'Meta': {'object_name': 'Option'},
65
+            'code': ('oscar.models.fields.autoslugfield.AutoSlugField', [], {'allow_duplicates': 'False', 'max_length': '128', 'separator': "u'-'", 'blank': 'True', 'unique': 'True', 'populate_from': "'name'", 'overwrite': 'False'}),
66
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
67
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
68
+            'type': ('django.db.models.fields.CharField', [], {'default': "'Required'", 'max_length': '128'})
69
+        },
70
+        u'catalogue.product': {
71
+            'Meta': {'ordering': "['-date_created']", 'object_name': 'Product'},
72
+            'attributes': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['catalogue.ProductAttribute']", 'through': u"orm['catalogue.ProductAttributeValue']", 'symmetrical': 'False'}),
73
+            'categories': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['catalogue.Category']", 'through': u"orm['catalogue.ProductCategory']", 'symmetrical': 'False'}),
74
+            'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
75
+            'date_updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
76
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
77
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
78
+            'is_discountable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
79
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'variants'", 'null': 'True', 'to': u"orm['catalogue.Product']"}),
80
+            'product_class': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'products'", 'null': 'True', 'on_delete': 'models.PROTECT', 'to': u"orm['catalogue.ProductClass']"}),
81
+            'product_options': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['catalogue.Option']", 'symmetrical': 'False', 'blank': 'True'}),
82
+            'rating': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
83
+            'recommended_products': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['catalogue.Product']", 'symmetrical': 'False', 'through': u"orm['catalogue.ProductRecommendation']", 'blank': 'True'}),
84
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
85
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
86
+            'upc': ('oscar.models.fields.NullCharField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
87
+        },
88
+        u'catalogue.productattribute': {
89
+            'Meta': {'ordering': "['code']", 'object_name': 'ProductAttribute'},
90
+            'code': ('django.db.models.fields.SlugField', [], {'max_length': '128'}),
91
+            'entity_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['catalogue.AttributeEntityType']", 'null': 'True', 'blank': 'True'}),
92
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
93
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
94
+            'option_group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['catalogue.AttributeOptionGroup']", 'null': 'True', 'blank': 'True'}),
95
+            'product_class': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attributes'", 'null': 'True', 'to': u"orm['catalogue.ProductClass']"}),
96
+            'required': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
97
+            'type': ('django.db.models.fields.CharField', [], {'default': "'text'", 'max_length': '20'})
98
+        },
99
+        u'catalogue.productattributevalue': {
100
+            'Meta': {'unique_together': "(('attribute', 'product'),)", 'object_name': 'ProductAttributeValue'},
101
+            'attribute': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['catalogue.ProductAttribute']"}),
102
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
103
+            'product': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attribute_values'", 'to': u"orm['catalogue.Product']"}),
104
+            'value_boolean': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
105
+            'value_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
106
+            'value_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['catalogue.AttributeEntity']", 'null': 'True', 'blank': 'True'}),
107
+            'value_file': ('django.db.models.fields.files.FileField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
108
+            'value_float': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
109
+            'value_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
110
+            'value_integer': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
111
+            'value_option': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['catalogue.AttributeOption']", 'null': 'True', 'blank': 'True'}),
112
+            'value_richtext': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
113
+            'value_text': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
114
+        },
115
+        u'catalogue.productcategory': {
116
+            'Meta': {'ordering': "['product', 'category']", 'unique_together': "(('product', 'category'),)", 'object_name': 'ProductCategory'},
117
+            'category': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['catalogue.Category']"}),
118
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
119
+            'product': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['catalogue.Product']"})
120
+        },
121
+        u'catalogue.productclass': {
122
+            'Meta': {'ordering': "['name']", 'object_name': 'ProductClass'},
123
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
124
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
125
+            'options': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['catalogue.Option']", 'symmetrical': 'False', 'blank': 'True'}),
126
+            'requires_shipping': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
127
+            'slug': ('oscar.models.fields.autoslugfield.AutoSlugField', [], {'allow_duplicates': 'False', 'max_length': '128', 'separator': "u'-'", 'blank': 'True', 'unique': 'True', 'populate_from': "'name'", 'overwrite': 'False'}),
128
+            'track_stock': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
129
+        },
130
+        u'catalogue.productimage': {
131
+            'Meta': {'ordering': "['display_order']", 'unique_together': "(('product', 'display_order'),)", 'object_name': 'ProductImage'},
132
+            'caption': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
133
+            'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
134
+            'display_order': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
135
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
136
+            'original': ('django.db.models.fields.files.ImageField', [], {'max_length': '255'}),
137
+            'product': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'images'", 'to': u"orm['catalogue.Product']"})
138
+        },
139
+        u'catalogue.productrecommendation': {
140
+            'Meta': {'ordering': "['primary', '-ranking']", 'unique_together': "(('primary', 'recommendation'),)", 'object_name': 'ProductRecommendation'},
141
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
142
+            'primary': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'primary_recommendations'", 'to': u"orm['catalogue.Product']"}),
143
+            'ranking': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '0'}),
144
+            'recommendation': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['catalogue.Product']"})
145
+        }
146
+    }
147
+
148
+    complete_apps = ['catalogue']

+ 4
- 0
oscar/apps/catalogue/models.py View File

@@ -20,6 +20,10 @@ class Product(AbstractProduct):
20 20
     pass
21 21
 
22 22
 
23
+class ProductRecommendation(AbstractProductRecommendation):
24
+    pass
25
+
26
+
23 27
 class ProductAttribute(AbstractProductAttribute):
24 28
     pass
25 29
 

+ 0
- 8
oscar/apps/dashboard/catalogue/forms.py View File

@@ -13,7 +13,6 @@ ProductClass = get_model('catalogue', 'ProductClass')
13 13
 Category = get_model('catalogue', 'Category')
14 14
 StockRecord = get_model('partner', 'StockRecord')
15 15
 Partner = get_model('partner', 'Partner')
16
-ProductClass = get_model('catalogue', 'ProductClass')
17 16
 ProductAttributeValue = get_model('catalogue', 'ProductAttributeValue')
18 17
 ProductCategory = get_model('catalogue', 'ProductCategory')
19 18
 ProductImage = get_model('catalogue', 'ProductImage')
@@ -278,7 +277,6 @@ class ProductForm(forms.ModelForm):
278 277
                    'attributes', 'categories')
279 278
         widgets = {
280 279
             'parent': ProductSelect,
281
-            'related_products': ProductSelectMultiple,
282 280
         }
283 281
 
284 282
     def __init__(self, product_class, data=None, *args, **kwargs):
@@ -301,13 +299,10 @@ class ProductForm(forms.ModelForm):
301 299
         is_parent = data and data.get('is_group', '') == 'on'
302 300
         self.add_attribute_fields(is_parent)
303 301
 
304
-        related_products = self.fields.get('related_products', None)
305 302
         parent = self.fields.get('parent', None)
306 303
 
307 304
         if parent is not None:
308 305
             parent.queryset = self.get_parent_products_queryset()
309
-        if related_products is not None:
310
-            related_products.queryset = self.get_related_products_queryset()
311 306
         if 'title' in self.fields:
312 307
             self.fields['title'].widget = forms.TextInput(
313 308
                 attrs={'autocomplete': 'off'})
@@ -337,9 +332,6 @@ class ProductForm(forms.ModelForm):
337 332
     def get_attribute_field(self, attribute):
338 333
         return self.FIELD_FACTORIES[attribute.type](attribute)
339 334
 
340
-    def get_related_products_queryset(self):
341
-        return Product.browsable.order_by('title')
342
-
343 335
     def get_parent_products_queryset(self):
344 336
         """
345 337
         :return: Canonical products excluding this product

+ 0
- 15
oscar/templates/oscar/catalogue/detail.html View File

@@ -206,21 +206,6 @@
206 206
         {% endblock product_review %}
207 207
     {% endiffeature %}
208 208
 
209
-    {% with related_products=product.related_products.all|slice:":6" %}
210
-        {% if related_products %}
211
-            <div class="sub-header">
212
-                <h2>{% trans "Related items" %}</h2>
213
-            </div>
214
-            <ul class="row-fluid">
215
-                {% for product in related_products %}
216
-                <li class="span2 {% if forloop.counter0|divisibleby:"6" %}no-margin{% endif %}">
217
-                    {% render_product product %}
218
-                </li>
219
-                {% endfor %}
220
-            </ul>
221
-        {% endif %}
222
-    {% endwith %}
223
-
224 209
     {% with recommended_products=product.recommended_products.all|slice:":6" %}
225 210
         {% if recommended_products %}
226 211
             <div class="sub-header">

+ 0
- 13
sites/demo/templates/catalogue/detail.html View File

@@ -169,19 +169,6 @@
169 169
         </div><!-- /span6 -->
170 170
     </div><!-- /row-fluid -->
171 171
     
172
-    {% with products=product.related_products.all|slice:":6" %}
173
-        {% if products %}
174
-            <div class="sub-header">
175
-                <h2>{% trans "Related items" %}</h2>
176
-            </div>
177
-            <ol class="row-fluid">
178
-                {% for product in products %}
179
-                <li class="span2 {% if forloop.counter0|divisibleby:"6" %}no-margin{% endif %}">{% render_product product %}</li>
180
-                {% endfor %}
181
-            </ol>
182
-        {% endif %}
183
-    {% endwith %}
184
-
185 172
     {% with products=product.recommended_products.all|slice:":6" %}
186 173
         {% if products %}
187 174
             <div class="sub-header">

+ 0
- 4
sites/sandbox/fixtures/multi-stockrecord-product.json View File

@@ -41,9 +41,7 @@
41 41
         "parent": null,
42 42
         "title": "David's book",
43 43
         "date_updated": "2013-07-18T12:23:50.102Z",
44
-        "related_products": [],
45 44
         "upc": "",
46
-        "score": 0.0,
47 45
         "is_discountable": true,
48 46
         "date_created": "2013-07-18T12:23:29.804Z",
49 47
         "product_options": [],
@@ -60,9 +58,7 @@
60 58
         "parent": null,
61 59
         "title": "Other book",
62 60
         "date_updated": "2013-07-18T14:35:52.484Z",
63
-        "related_products": [],
64 61
         "upc": "ABC",
65
-        "score": 0.0,
66 62
         "is_discountable": true,
67 63
         "date_created": "2013-07-18T14:35:52.484Z",
68 64
         "product_options": [],

+ 0
- 22
sites/sandbox/fixtures/variants.json View File

@@ -57,9 +57,7 @@
57 57
         "parent": null, 
58 58
         "title": "Oscar T-shirt", 
59 59
         "date_updated": "2013-12-12T16:33:57.426Z", 
60
-        "related_products": [], 
61 60
         "upc": null, 
62
-        "score": 0.0, 
63 61
         "is_discountable": true, 
64 62
         "date_created": "2013-12-12T16:33:57.426Z", 
65 63
         "product_options": [], 
@@ -76,9 +74,7 @@
76 74
         "parent": 1, 
77 75
         "title": "", 
78 76
         "date_updated": "2013-12-12T16:34:14.023Z", 
79
-        "related_products": [], 
80 77
         "upc": null, 
81
-        "score": 0.0, 
82 78
         "is_discountable": true, 
83 79
         "date_created": "2013-12-12T16:34:14.023Z", 
84 80
         "product_options": [], 
@@ -95,9 +91,7 @@
95 91
         "parent": 1, 
96 92
         "title": "", 
97 93
         "date_updated": "2013-12-12T16:34:32.170Z", 
98
-        "related_products": [], 
99 94
         "upc": null, 
100
-        "score": 0.0, 
101 95
         "is_discountable": true, 
102 96
         "date_created": "2013-12-12T16:34:32.170Z", 
103 97
         "product_options": [], 
@@ -114,9 +108,7 @@
114 108
         "parent": 1, 
115 109
         "title": "", 
116 110
         "date_updated": "2013-12-12T17:32:15.016Z", 
117
-        "related_products": [], 
118 111
         "upc": null, 
119
-        "score": 0.0, 
120 112
         "is_discountable": true, 
121 113
         "date_created": "2013-12-12T17:32:15.016Z", 
122 114
         "product_options": [], 
@@ -133,9 +125,7 @@
133 125
         "parent": null, 
134 126
         "title": "commandlinefu T-shirt", 
135 127
         "date_updated": "2013-12-13T11:34:21.810Z", 
136
-        "related_products": [], 
137 128
         "upc": null, 
138
-        "score": 0.0, 
139 129
         "is_discountable": true, 
140 130
         "date_created": "2013-12-13T11:34:21.810Z", 
141 131
         "product_options": [], 
@@ -152,9 +142,7 @@
152 142
         "parent": 5, 
153 143
         "title": "", 
154 144
         "date_updated": "2013-12-13T11:37:08.138Z", 
155
-        "related_products": [], 
156 145
         "upc": null, 
157
-        "score": 0.0, 
158 146
         "is_discountable": true, 
159 147
         "date_created": "2013-12-13T11:37:08.138Z", 
160 148
         "product_options": [], 
@@ -171,9 +159,7 @@
171 159
         "parent": 5, 
172 160
         "title": "", 
173 161
         "date_updated": "2013-12-13T11:37:45.834Z", 
174
-        "related_products": [], 
175 162
         "upc": null, 
176
-        "score": 0.0, 
177 163
         "is_discountable": true, 
178 164
         "date_created": "2013-12-13T11:37:45.834Z", 
179 165
         "product_options": [], 
@@ -190,9 +176,7 @@
190 176
         "parent": null, 
191 177
         "title": "Tangent T-shirt", 
192 178
         "date_updated": "2013-12-13T11:38:15.107Z", 
193
-        "related_products": [], 
194 179
         "upc": null, 
195
-        "score": 0.0, 
196 180
         "is_discountable": true, 
197 181
         "date_created": "2013-12-13T11:38:15.107Z", 
198 182
         "product_options": [], 
@@ -209,9 +193,7 @@
209 193
         "parent": 8, 
210 194
         "title": "", 
211 195
         "date_updated": "2013-12-13T11:38:49.769Z", 
212
-        "related_products": [], 
213 196
         "upc": null, 
214
-        "score": 0.0, 
215 197
         "is_discountable": true, 
216 198
         "date_created": "2013-12-13T11:38:49.769Z", 
217 199
         "product_options": [], 
@@ -228,9 +210,7 @@
228 210
         "parent": 8, 
229 211
         "title": "", 
230 212
         "date_updated": "2013-12-13T11:39:12.859Z", 
231
-        "related_products": [], 
232 213
         "upc": null, 
233
-        "score": 0.0, 
234 214
         "is_discountable": true, 
235 215
         "date_created": "2013-12-13T11:39:12.859Z", 
236 216
         "product_options": [], 
@@ -247,9 +227,7 @@
247 227
         "parent": 8, 
248 228
         "title": "", 
249 229
         "date_updated": "2013-12-13T11:39:53.073Z", 
250
-        "related_products": [], 
251 230
         "upc": null, 
252
-        "score": 0.0, 
253 231
         "is_discountable": true, 
254 232
         "date_created": "2013-12-13T11:39:53.073Z", 
255 233
         "product_options": [], 

+ 0
- 5
tests/functional/dashboard/product_tests.py View File

@@ -144,14 +144,9 @@ class TestProductUpdate(ProductWebTest):
144 144
         product_form = page.form
145 145
         product_form['title'] = 'Nice T-Shirt'
146 146
 
147
-        # ProductSelectMultiple widget expects comma separated ids:
148
-        product_form['related_products'] = ','.join((str(a.id), str(b.id)))
149
-
150 147
         page = product_form.submit()
151 148
 
152 149
         product = Product.objects.get(id=self.product.id)
153 150
 
154 151
         self.assertEqual(page.context['product'], self.product)
155 152
         self.assertEqual(product.title, 'Nice T-Shirt')
156
-        self.assertEqual(list(product.related_products.all().order_by('title')),
157
-                         [a, b])

Loading…
Cancel
Save