Browse Source

Move RangeProductFileUpload model from dashboard to offers app

It is the only model in any of the Oscar dashboard apps. It causes an
issue with the get_model() call, because the dashboard app labels had
to be renamed for Django 1.7. So in Django < 1.7
get_model('ranges', 'RangeProductFileUpload') would work, wheras in
Django 1.7 it had to be get_model('ranges_dashboard',
'RangeProductFileUpload'). There is other ways of solving the problem,
but given that it is a rarely used model of temporary nature, enforcing
that dashboard apps are models-free seems like the cleanest solution.
This only has to be enforced till support for Django 1.6 is dropped, and
I doubt we'll see the need for a model in a dashboard app till then.
master
Maik Hoepfel 11 years ago
parent
commit
aa5b1a144a

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

@@ -450,6 +450,14 @@ Migrations
450 450
     Please double-check it's outcome and make sure to do something similar
451 451
     if you have forked the catalogue app.
452 452
 
453
+.. warning::
454
+
455
+    The ``RangeProductFileUpload`` model has been moved from the ranges
456
+    dashboard app to the offers app. The migrations that have been naively
457
+    drop and re-create the model; any data is lost! This is probably not an
458
+    issue, as the model is only used while an range upload is in progress. If
459
+    you need to keep the data, ensure you migrate it across.
460
+
453 461
 .. note::
454 462
 
455 463
     Be sure to read the detailed instructions for
@@ -470,6 +478,10 @@ Migrations
470 478
       and do entity attribute changes and model deletions.
471 479
     - ``0025`` & ``0026`` - Schema & data migration to determine and save Product structure.
472 480
 
481
+* Offer:
482
+
483
+    - ``0033`` - Add moved ``RangedProductFileUpload`` model.
484
+
473 485
 * Order:
474 486
 
475 487
     - ``0029`` - Add ``unique_together`` to ``PaymentEventQuantity`` and ``ShippingEventQuantity``
@@ -484,6 +496,11 @@ Migrations
484 496
 
485 497
     - ``0006`` - Add ``unique_together`` to ``OrderedProduct``
486 498
 
499
+* Ranges dashboard:
500
+
501
+    - ``0003`` - Drop ``RangeProductFileUpload`` from ``ranges`` app. This is
502
+                 a destructive change!
503
+
487 504
 * Shipping:
488 505
 
489 506
     - ``0007`` - Change ``WeightBand.upper_limit`` from ``FloatField`` to ``DecimalField``

+ 0
- 107
oscar/apps/dashboard/ranges/models.py View File

@@ -1,107 +0,0 @@
1
-import os
2
-import re
3
-from django.utils.translation import ugettext_lazy as _
4
-from django.db import models
5
-from django.utils.timezone import now
6
-from oscar.core.compat import AUTH_USER_MODEL
7
-from six.moves import filter
8
-
9
-
10
-class RangeProductFileUpload(models.Model):
11
-    range = models.ForeignKey('offer.Range', related_name='file_uploads',
12
-                              verbose_name=_("Range"))
13
-    filepath = models.CharField(_("File Path"), max_length=255)
14
-    size = models.PositiveIntegerField(_("Size"))
15
-    uploaded_by = models.ForeignKey(AUTH_USER_MODEL,
16
-                                    verbose_name=_("Uploaded By"))
17
-    date_uploaded = models.DateTimeField(_("Date Uploaded"), auto_now_add=True)
18
-
19
-    PENDING, FAILED, PROCESSED = 'Pending', 'Failed', 'Processed'
20
-    choices = (
21
-        (PENDING, PENDING),
22
-        (FAILED, FAILED),
23
-        (PROCESSED, PROCESSED),
24
-    )
25
-    status = models.CharField(_("Status"), max_length=32, choices=choices,
26
-                              default=PENDING)
27
-    error_message = models.CharField(_("Error Message"), max_length=255,
28
-                                     blank=True)
29
-
30
-    # Post-processing audit fields
31
-    date_processed = models.DateTimeField(_("Date Processed"), null=True)
32
-    num_new_skus = models.PositiveIntegerField(_("Number of New SKUs"),
33
-                                               null=True)
34
-    num_unknown_skus = models.PositiveIntegerField(_("Number of Unknown SKUs"),
35
-                                                   null=True)
36
-    num_duplicate_skus = models.PositiveIntegerField(
37
-        _("Number of Duplicate SKUs"), null=True)
38
-
39
-    class Meta:
40
-        ordering = ('-date_uploaded',)
41
-        verbose_name = _("Range Product Uploaded File")
42
-        verbose_name_plural = _("Range Product Uploaded Files")
43
-
44
-    @property
45
-    def filename(self):
46
-        return os.path.basename(self.filepath)
47
-
48
-    def mark_as_failed(self, message=None):
49
-        self.date_processed = now()
50
-        self.error_message = message
51
-        self.status = self.FAILED
52
-        self.save()
53
-
54
-    def mark_as_processed(self, num_new, num_unknown, num_duplicate):
55
-        self.status = self.PROCESSED
56
-        self.date_processed = now()
57
-        self.num_new_skus = num_new
58
-        self.num_unknown_skus = num_unknown
59
-        self.num_duplicate_skus = num_duplicate
60
-        self.save()
61
-
62
-    def was_processing_successful(self):
63
-        return self.status == self.PROCESSED
64
-
65
-    def process(self):
66
-        """
67
-        Process the file upload and add products to the range
68
-        """
69
-        all_ids = set(self.extract_ids())
70
-        products = self.range.included_products.all()
71
-        existing_skus = products.values_list('stockrecord__partner_sku',
72
-                                             flat=True)
73
-        existing_skus = set(filter(bool, existing_skus))
74
-        existing_upcs = products.values_list('upc', flat=True)
75
-        existing_upcs = set(filter(bool, existing_upcs))
76
-        existing_ids = existing_skus.union(existing_upcs)
77
-        new_ids = all_ids - existing_ids
78
-
79
-        Product = models.get_model('catalogue', 'Product')
80
-        products = Product._default_manager.filter(
81
-            models.Q(stockrecord__partner_sku__in=new_ids) |
82
-            models.Q(upc__in=new_ids))
83
-        for product in products:
84
-            self.range.add_product(product)
85
-
86
-        # Processing stats
87
-        found_skus = products.values_list('stockrecord__partner_sku',
88
-                                          flat=True)
89
-        found_skus = set(filter(bool, found_skus))
90
-        found_upcs = set(filter(bool, products.values_list('upc', flat=True)))
91
-        found_ids = found_skus.union(found_upcs)
92
-        missing_ids = new_ids - found_ids
93
-        dupes = set(all_ids).intersection(existing_ids)
94
-
95
-        self.mark_as_processed(products.count(), len(missing_ids), len(dupes))
96
-
97
-    def extract_ids(self):
98
-        """
99
-        Extract all SKU- or UPC-like strings from the file
100
-        """
101
-        for line in open(self.filepath, 'r'):
102
-            for id in re.split('[^\w:\.-]', line):
103
-                if id:
104
-                    yield id
105
-
106
-    def delete_file(self):
107
-        os.unlink(self.filepath)

+ 40
- 0
oscar/apps/dashboard/ranges/south_migrations/0003_auto__del_rangeproductfileupload.py View File

@@ -0,0 +1,40 @@
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
+from oscar.core.compat import AUTH_USER_MODEL, AUTH_USER_MODEL_NAME
8
+
9
+
10
+class Migration(SchemaMigration):
11
+
12
+    def forwards(self, orm):
13
+        # Deleting model 'RangeProductFileUpload'
14
+        db.delete_table(u'ranges_rangeproductfileupload')
15
+
16
+
17
+    def backwards(self, orm):
18
+        # Adding model 'RangeProductFileUpload'
19
+        db.create_table(u'ranges_rangeproductfileupload', (
20
+            ('status', self.gf('django.db.models.fields.CharField')(default='Pending', max_length=32)),
21
+            ('date_processed', self.gf('django.db.models.fields.DateTimeField')(null=True)),
22
+            ('uploaded_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm[AUTH_USER_MODEL])),
23
+            ('num_new_skus', self.gf('django.db.models.fields.PositiveIntegerField')(null=True)),
24
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
25
+            ('size', self.gf('django.db.models.fields.PositiveIntegerField')()),
26
+            ('filepath', self.gf('django.db.models.fields.CharField')(max_length=255)),
27
+            ('error_message', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
28
+            ('num_unknown_skus', self.gf('django.db.models.fields.PositiveIntegerField')(null=True)),
29
+            ('range', self.gf('django.db.models.fields.related.ForeignKey')(related_name='file_uploads', to=orm['offer.Range'])),
30
+            ('num_duplicate_skus', self.gf('django.db.models.fields.PositiveIntegerField')(null=True)),
31
+            ('date_uploaded', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
32
+        ))
33
+        db.send_create_signal('ranges', ['RangeProductFileUpload'])
34
+
35
+
36
+    models = {
37
+        
38
+    }
39
+
40
+    complete_apps = ['ranges']

+ 2
- 3
oscar/apps/dashboard/ranges/views.py View File

@@ -4,7 +4,6 @@ from django.conf import settings
4 4
 from django.contrib import messages
5 5
 from django.core import exceptions
6 6
 from django.core.urlresolvers import reverse
7
-from oscar.core.loading import get_model
8 7
 from django.http import HttpResponseRedirect
9 8
 from django.shortcuts import get_object_or_404, HttpResponse
10 9
 from django.template.loader import render_to_string
@@ -14,14 +13,14 @@ from django.views.generic import (
14 13
 
15 14
 
16 15
 from oscar.views.generic import BulkEditMixin
17
-from oscar.core.loading import get_classes
16
+from oscar.core.loading import get_classes, get_model
18 17
 
19 18
 Range = get_model('offer', 'Range')
20 19
 RangeProduct = get_model('offer', 'RangeProduct')
20
+RangeProductFileUpload = get_model('offer', 'RangeProductFileUpload')
21 21
 Product = get_model('catalogue', 'Product')
22 22
 RangeForm, RangeProductForm = get_classes('dashboard.ranges.forms',
23 23
                                           ['RangeForm', 'RangeProductForm'])
24
-RangeProductFileUpload = get_model('ranges', 'RangeProductFileUpload')
25 24
 
26 25
 
27 26
 class RangeListView(ListView):

+ 103
- 0
oscar/apps/offer/models.py View File

@@ -1,3 +1,5 @@
1
+import os
2
+import re
1 3
 import six
2 4
 import operator
3 5
 from decimal import Decimal as D, ROUND_DOWN, ROUND_UP
@@ -12,6 +14,7 @@ from django.core.exceptions import ValidationError
12 14
 from django.core.urlresolvers import reverse
13 15
 from django.conf import settings
14 16
 
17
+from oscar.core.compat import AUTH_USER_MODEL
15 18
 from oscar.core.utils import slugify
16 19
 from oscar.core.loading import get_class, get_model
17 20
 from oscar.apps.offer.managers import ActiveOfferManager
@@ -1583,3 +1586,103 @@ class ShippingPercentageDiscountBenefit(ShippingBenefit):
1583 1586
     def shipping_discount(self, charge):
1584 1587
         discount = charge * self.value / D('100.0')
1585 1588
         return discount.quantize(D('0.01'))
1589
+
1590
+
1591
+class RangeProductFileUpload(models.Model):
1592
+    range = models.ForeignKey('offer.Range', related_name='file_uploads',
1593
+                              verbose_name=_("Range"))
1594
+    filepath = models.CharField(_("File Path"), max_length=255)
1595
+    size = models.PositiveIntegerField(_("Size"))
1596
+    uploaded_by = models.ForeignKey(AUTH_USER_MODEL,
1597
+                                    verbose_name=_("Uploaded By"))
1598
+    date_uploaded = models.DateTimeField(_("Date Uploaded"), auto_now_add=True)
1599
+
1600
+    PENDING, FAILED, PROCESSED = 'Pending', 'Failed', 'Processed'
1601
+    choices = (
1602
+        (PENDING, PENDING),
1603
+        (FAILED, FAILED),
1604
+        (PROCESSED, PROCESSED),
1605
+    )
1606
+    status = models.CharField(_("Status"), max_length=32, choices=choices,
1607
+                              default=PENDING)
1608
+    error_message = models.CharField(_("Error Message"), max_length=255,
1609
+                                     blank=True)
1610
+
1611
+    # Post-processing audit fields
1612
+    date_processed = models.DateTimeField(_("Date Processed"), null=True)
1613
+    num_new_skus = models.PositiveIntegerField(_("Number of New SKUs"),
1614
+                                               null=True)
1615
+    num_unknown_skus = models.PositiveIntegerField(_("Number of Unknown SKUs"),
1616
+                                                   null=True)
1617
+    num_duplicate_skus = models.PositiveIntegerField(
1618
+        _("Number of Duplicate SKUs"), null=True)
1619
+
1620
+    class Meta:
1621
+        ordering = ('-date_uploaded',)
1622
+        verbose_name = _("Range Product Uploaded File")
1623
+        verbose_name_plural = _("Range Product Uploaded Files")
1624
+
1625
+    @property
1626
+    def filename(self):
1627
+        return os.path.basename(self.filepath)
1628
+
1629
+    def mark_as_failed(self, message=None):
1630
+        self.date_processed = now()
1631
+        self.error_message = message
1632
+        self.status = self.FAILED
1633
+        self.save()
1634
+
1635
+    def mark_as_processed(self, num_new, num_unknown, num_duplicate):
1636
+        self.status = self.PROCESSED
1637
+        self.date_processed = now()
1638
+        self.num_new_skus = num_new
1639
+        self.num_unknown_skus = num_unknown
1640
+        self.num_duplicate_skus = num_duplicate
1641
+        self.save()
1642
+
1643
+    def was_processing_successful(self):
1644
+        return self.status == self.PROCESSED
1645
+
1646
+    def process(self):
1647
+        """
1648
+        Process the file upload and add products to the range
1649
+        """
1650
+        all_ids = set(self.extract_ids())
1651
+        products = self.range.included_products.all()
1652
+        existing_skus = products.values_list('stockrecord__partner_sku',
1653
+                                             flat=True)
1654
+        existing_skus = set(filter(bool, existing_skus))
1655
+        existing_upcs = products.values_list('upc', flat=True)
1656
+        existing_upcs = set(filter(bool, existing_upcs))
1657
+        existing_ids = existing_skus.union(existing_upcs)
1658
+        new_ids = all_ids - existing_ids
1659
+
1660
+        Product = models.get_model('catalogue', 'Product')
1661
+        products = Product._default_manager.filter(
1662
+            models.Q(stockrecord__partner_sku__in=new_ids) |
1663
+            models.Q(upc__in=new_ids))
1664
+        for product in products:
1665
+            self.range.add_product(product)
1666
+
1667
+        # Processing stats
1668
+        found_skus = products.values_list('stockrecord__partner_sku',
1669
+                                          flat=True)
1670
+        found_skus = set(filter(bool, found_skus))
1671
+        found_upcs = set(filter(bool, products.values_list('upc', flat=True)))
1672
+        found_ids = found_skus.union(found_upcs)
1673
+        missing_ids = new_ids - found_ids
1674
+        dupes = set(all_ids).intersection(existing_ids)
1675
+
1676
+        self.mark_as_processed(products.count(), len(missing_ids), len(dupes))
1677
+
1678
+    def extract_ids(self):
1679
+        """
1680
+        Extract all SKU- or UPC-like strings from the file
1681
+        """
1682
+        for line in open(self.filepath, 'r'):
1683
+            for id in re.split('[^\w:\.-]', line):
1684
+                if id:
1685
+                    yield id
1686
+
1687
+    def delete_file(self):
1688
+        os.unlink(self.filepath)

+ 250
- 0
oscar/apps/offer/south_migrations/0033_auto__add_rangeproductfileupload.py View File

@@ -0,0 +1,250 @@
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
+from oscar.core.compat import AUTH_USER_MODEL, AUTH_USER_MODEL_NAME
8
+
9
+
10
+class Migration(SchemaMigration):
11
+
12
+    def forwards(self, orm):
13
+        # Adding model 'RangeProductFileUpload'
14
+        db.create_table(u'offer_rangeproductfileupload', (
15
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
16
+            ('range', self.gf('django.db.models.fields.related.ForeignKey')(related_name='file_uploads', to=orm['offer.Range'])),
17
+            ('filepath', self.gf('django.db.models.fields.CharField')(max_length=255)),
18
+            ('size', self.gf('django.db.models.fields.PositiveIntegerField')()),
19
+            ('uploaded_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm[AUTH_USER_MODEL])),
20
+            ('date_uploaded', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
21
+            ('status', self.gf('django.db.models.fields.CharField')(default='Pending', max_length=32)),
22
+            ('error_message', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
23
+            ('date_processed', self.gf('django.db.models.fields.DateTimeField')(null=True)),
24
+            ('num_new_skus', self.gf('django.db.models.fields.PositiveIntegerField')(null=True)),
25
+            ('num_unknown_skus', self.gf('django.db.models.fields.PositiveIntegerField')(null=True)),
26
+            ('num_duplicate_skus', self.gf('django.db.models.fields.PositiveIntegerField')(null=True)),
27
+        ))
28
+        db.send_create_signal(u'offer', ['RangeProductFileUpload'])
29
+
30
+
31
+    def backwards(self, orm):
32
+        # Deleting model 'RangeProductFileUpload'
33
+        db.delete_table(u'offer_rangeproductfileupload')
34
+
35
+
36
+    models = {
37
+        u'auth.group': {
38
+            'Meta': {'object_name': 'Group'},
39
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
40
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
41
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
42
+        },
43
+        u'auth.permission': {
44
+            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
45
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
46
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
47
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
48
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
49
+        },
50
+        AUTH_USER_MODEL: {
51
+            'Meta': {'object_name': AUTH_USER_MODEL_NAME},
52
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
53
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
54
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
55
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
56
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
57
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
58
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
59
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
60
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
61
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
62
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
63
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
64
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
65
+        },
66
+        'catalogue.attributeoption': {
67
+            'Meta': {'object_name': 'AttributeOption'},
68
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'options'", 'to': "orm['catalogue.AttributeOptionGroup']"}),
69
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
70
+            'option': ('django.db.models.fields.CharField', [], {'max_length': '255'})
71
+        },
72
+        'catalogue.attributeoptiongroup': {
73
+            'Meta': {'object_name': 'AttributeOptionGroup'},
74
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
75
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
76
+        },
77
+        'catalogue.category': {
78
+            'Meta': {'ordering': "['full_name']", 'object_name': 'Category'},
79
+            'depth': ('django.db.models.fields.PositiveIntegerField', [], {}),
80
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
81
+            'full_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
82
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
83
+            'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
84
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
85
+            'numchild': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
86
+            'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
87
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'})
88
+        },
89
+        'catalogue.option': {
90
+            'Meta': {'object_name': 'Option'},
91
+            'code': ('oscar.models.fields.autoslugfield.AutoSlugField', [], {'allow_duplicates': 'False', 'max_length': '128', 'separator': "u'-'", 'blank': 'True', 'unique': 'True', 'populate_from': "'name'", 'overwrite': 'False'}),
92
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
93
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
94
+            'type': ('django.db.models.fields.CharField', [], {'default': "'Required'", 'max_length': '128'})
95
+        },
96
+        'catalogue.product': {
97
+            'Meta': {'ordering': "['-date_created']", 'object_name': 'Product'},
98
+            'attributes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['catalogue.ProductAttribute']", 'through': "orm['catalogue.ProductAttributeValue']", 'symmetrical': 'False'}),
99
+            'categories': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['catalogue.Category']", 'through': "orm['catalogue.ProductCategory']", 'symmetrical': 'False'}),
100
+            'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
101
+            'date_updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
102
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
103
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
104
+            'is_discountable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
105
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['catalogue.Product']"}),
106
+            'product_class': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'products'", 'null': 'True', 'on_delete': 'models.PROTECT', 'to': "orm['catalogue.ProductClass']"}),
107
+            'product_options': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['catalogue.Option']", 'symmetrical': 'False', 'blank': 'True'}),
108
+            'rating': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
109
+            'recommended_products': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['catalogue.Product']", 'symmetrical': 'False', 'through': "orm['catalogue.ProductRecommendation']", 'blank': 'True'}),
110
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
111
+            'structure': ('django.db.models.fields.CharField', [], {'default': "'standalone'", 'max_length': '10'}),
112
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
113
+            'upc': ('oscar.models.fields.NullCharField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
114
+        },
115
+        'catalogue.productattribute': {
116
+            'Meta': {'ordering': "['code']", 'object_name': 'ProductAttribute'},
117
+            'code': ('django.db.models.fields.SlugField', [], {'max_length': '128'}),
118
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
119
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
120
+            'option_group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.AttributeOptionGroup']", 'null': 'True', 'blank': 'True'}),
121
+            'product_class': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attributes'", 'null': 'True', 'to': "orm['catalogue.ProductClass']"}),
122
+            'required': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
123
+            'type': ('django.db.models.fields.CharField', [], {'default': "'text'", 'max_length': '20'})
124
+        },
125
+        'catalogue.productattributevalue': {
126
+            'Meta': {'unique_together': "(('attribute', 'product'),)", 'object_name': 'ProductAttributeValue'},
127
+            'attribute': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ProductAttribute']"}),
128
+            'entity_content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}),
129
+            'entity_object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
130
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
131
+            'product': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attribute_values'", 'to': "orm['catalogue.Product']"}),
132
+            'value_boolean': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
133
+            'value_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
134
+            'value_file': ('django.db.models.fields.files.FileField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
135
+            'value_float': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
136
+            'value_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
137
+            'value_integer': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
138
+            'value_option': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.AttributeOption']", 'null': 'True', 'blank': 'True'}),
139
+            'value_richtext': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
140
+            'value_text': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
141
+        },
142
+        'catalogue.productcategory': {
143
+            'Meta': {'ordering': "['product', 'category']", 'unique_together': "(('product', 'category'),)", 'object_name': 'ProductCategory'},
144
+            'category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Category']"}),
145
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
146
+            'product': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Product']"})
147
+        },
148
+        'catalogue.productclass': {
149
+            'Meta': {'ordering': "['name']", 'object_name': 'ProductClass'},
150
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
151
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
152
+            'options': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['catalogue.Option']", 'symmetrical': 'False', 'blank': 'True'}),
153
+            'requires_shipping': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
154
+            'slug': ('oscar.models.fields.autoslugfield.AutoSlugField', [], {'allow_duplicates': 'False', 'max_length': '128', 'separator': "u'-'", 'blank': 'True', 'unique': 'True', 'populate_from': "'name'", 'overwrite': 'False'}),
155
+            'track_stock': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
156
+        },
157
+        'catalogue.productrecommendation': {
158
+            'Meta': {'ordering': "['primary', '-ranking']", 'unique_together': "(('primary', 'recommendation'),)", 'object_name': 'ProductRecommendation'},
159
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
160
+            'primary': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'primary_recommendations'", 'to': "orm['catalogue.Product']"}),
161
+            'ranking': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '0'}),
162
+            'recommendation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Product']"})
163
+        },
164
+        u'contenttypes.contenttype': {
165
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
166
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
167
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
168
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
169
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
170
+        },
171
+        'offer.benefit': {
172
+            'Meta': {'object_name': 'Benefit'},
173
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
174
+            'max_affected_items': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
175
+            'proxy_class': ('oscar.models.fields.NullCharField', [], {'default': 'None', 'max_length': '255', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
176
+            'range': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['offer.Range']", 'null': 'True', 'blank': 'True'}),
177
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}),
178
+            'value': ('oscar.models.fields.PositiveDecimalField', [], {'null': 'True', 'max_digits': '12', 'decimal_places': '2', 'blank': 'True'})
179
+        },
180
+        'offer.condition': {
181
+            'Meta': {'object_name': 'Condition'},
182
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
183
+            'proxy_class': ('oscar.models.fields.NullCharField', [], {'default': 'None', 'max_length': '255', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
184
+            'range': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['offer.Range']", 'null': 'True', 'blank': 'True'}),
185
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}),
186
+            'value': ('oscar.models.fields.PositiveDecimalField', [], {'null': 'True', 'max_digits': '12', 'decimal_places': '2', 'blank': 'True'})
187
+        },
188
+        'offer.conditionaloffer': {
189
+            'Meta': {'ordering': "['-priority']", 'object_name': 'ConditionalOffer'},
190
+            'benefit': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['offer.Benefit']"}),
191
+            'condition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['offer.Condition']"}),
192
+            'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
193
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
194
+            'end_datetime': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
195
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
196
+            'max_basket_applications': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
197
+            'max_discount': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '12', 'decimal_places': '2', 'blank': 'True'}),
198
+            'max_global_applications': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
199
+            'max_user_applications': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
200
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
201
+            'num_applications': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
202
+            'num_orders': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
203
+            'offer_type': ('django.db.models.fields.CharField', [], {'default': "'Site'", 'max_length': '128'}),
204
+            'priority': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
205
+            'redirect_url': ('oscar.models.fields.ExtendedURLField', [], {'max_length': '200', 'blank': 'True'}),
206
+            'slug': ('oscar.models.fields.autoslugfield.AutoSlugField', [], {'allow_duplicates': 'False', 'max_length': '128', 'separator': "u'-'", 'blank': 'True', 'unique': 'True', 'populate_from': "'name'", 'overwrite': 'False'}),
207
+            'start_datetime': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
208
+            'status': ('django.db.models.fields.CharField', [], {'default': "'Open'", 'max_length': '64'}),
209
+            'total_discount': ('django.db.models.fields.DecimalField', [], {'default': "'0.00'", 'max_digits': '12', 'decimal_places': '2'})
210
+        },
211
+        'offer.range': {
212
+            'Meta': {'object_name': 'Range'},
213
+            'classes': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'classes'", 'blank': 'True', 'to': "orm['catalogue.ProductClass']"}),
214
+            'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
215
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
216
+            'excluded_products': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'excludes'", 'blank': 'True', 'to': "orm['catalogue.Product']"}),
217
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
218
+            'included_categories': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'includes'", 'blank': 'True', 'to': "orm['catalogue.Category']"}),
219
+            'included_products': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'includes'", 'blank': 'True', 'through': "orm['offer.RangeProduct']", 'to': "orm['catalogue.Product']"}),
220
+            'includes_all_products': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
221
+            'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
222
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
223
+            'proxy_class': ('oscar.models.fields.NullCharField', [], {'default': 'None', 'max_length': '255', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
224
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '128', 'unique': 'True', 'null': 'True'})
225
+        },
226
+        'offer.rangeproduct': {
227
+            'Meta': {'unique_together': "(('range', 'product'),)", 'object_name': 'RangeProduct'},
228
+            'display_order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
229
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
230
+            'product': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Product']"}),
231
+            'range': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['offer.Range']"})
232
+        },
233
+        u'offer.rangeproductfileupload': {
234
+            'Meta': {'ordering': "('-date_uploaded',)", 'object_name': 'RangeProductFileUpload'},
235
+            'date_processed': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
236
+            'date_uploaded': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
237
+            'error_message': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
238
+            'filepath': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
239
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
240
+            'num_duplicate_skus': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
241
+            'num_new_skus': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
242
+            'num_unknown_skus': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
243
+            'range': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'file_uploads'", 'to': "orm['offer.Range']"}),
244
+            'size': ('django.db.models.fields.PositiveIntegerField', [], {}),
245
+            'status': ('django.db.models.fields.CharField', [], {'default': "'Pending'", 'max_length': '32'}),
246
+            'uploaded_by': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['{0}']".format(AUTH_USER_MODEL)})
247
+        }
248
+    }
249
+
250
+    complete_apps = ['offer']

Loading…
Cancel
Save