Permalink
Browse files

Added slug to the offer model and improved the general plumbing of of…

…fers into other areas
  • Loading branch information...
1 parent e54fc4e commit 1ccbec259f8eb80a9504d8e1895e463f22371d2c @codeinthehole codeinthehole committed May 2, 2012
View
@@ -10,8 +10,9 @@ class OfferApplication(Application):
def get_urls(self):
urlpatterns = patterns('',
- url(r'^(?P<pk>\d+)/$', self.detail_view.as_view(), name='detail'),
+ url(r'^(?P<slug>[\w-]+)/$', self.detail_view.as_view(), name='detail'),
)
return self.post_process_urls(urlpatterns)
+
application = OfferApplication()
@@ -0,0 +1,174 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding field 'ConditionalOffer.slug'
+ db.add_column('offer_conditionaloffer', 'slug', self.gf('django.db.models.fields.SlugField')(max_length=128, unique=True, null=True, db_index=True), keep_default=False)
+
+
+ def backwards(self, orm):
+
+ # Deleting field 'ConditionalOffer.slug'
+ db.delete_column('offer_conditionaloffer', 'slug')
+
+
+ models = {
+ 'catalogue.attributeentity': {
+ 'Meta': {'object_name': 'AttributeEntity'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
+ 'type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'entities'", 'to': "orm['catalogue.AttributeEntityType']"})
+ },
+ 'catalogue.attributeentitytype': {
+ 'Meta': {'object_name': 'AttributeEntityType'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'})
+ },
+ 'catalogue.attributeoption': {
+ 'Meta': {'object_name': 'AttributeOption'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'options'", 'to': "orm['catalogue.AttributeOptionGroup']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'option': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'catalogue.attributeoptiongroup': {
+ 'Meta': {'object_name': 'AttributeOptionGroup'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'catalogue.category': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Category'},
+ 'depth': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'full_name': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'numchild': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '1024', 'db_index': 'True'})
+ },
+ 'catalogue.option': {
+ 'Meta': {'object_name': 'Option'},
+ 'code': ('django.db.models.fields.SlugField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'type': ('django.db.models.fields.CharField', [], {'default': "'Required'", 'max_length': '128'})
+ },
+ 'catalogue.product': {
+ 'Meta': {'ordering': "['-date_created']", 'object_name': 'Product'},
+ 'attributes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['catalogue.ProductAttribute']", 'through': "orm['catalogue.ProductAttributeValue']", 'symmetrical': 'False'}),
+ 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['catalogue.Category']", 'through': "orm['catalogue.ProductCategory']", 'symmetrical': 'False'}),
+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'date_updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'variants'", 'null': 'True', 'to': "orm['catalogue.Product']"}),
+ 'product_class': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ProductClass']", 'null': 'True'}),
+ 'product_options': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['catalogue.Option']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'recommended_products': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['catalogue.Product']", 'symmetrical': 'False', 'through': "orm['catalogue.ProductRecommendation']", 'blank': 'True'}),
+ 'related_products': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'relations'", 'blank': 'True', 'to': "orm['catalogue.Product']"}),
+ 'score': ('django.db.models.fields.FloatField', [], {'default': '0.0', 'db_index': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'status': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'upc': ('django.db.models.fields.CharField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
+ },
+ 'catalogue.productattribute': {
+ 'Meta': {'ordering': "['code']", 'object_name': 'ProductAttribute'},
+ 'code': ('django.db.models.fields.SlugField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'entity_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.AttributeEntityType']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'option_group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.AttributeOptionGroup']", 'null': 'True', 'blank': 'True'}),
+ 'product_class': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attributes'", 'null': 'True', 'to': "orm['catalogue.ProductClass']"}),
+ 'required': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'type': ('django.db.models.fields.CharField', [], {'default': "'text'", 'max_length': '20'})
+ },
+ 'catalogue.productattributevalue': {
+ 'Meta': {'object_name': 'ProductAttributeValue'},
+ 'attribute': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ProductAttribute']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'product': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attribute_values'", 'to': "orm['catalogue.Product']"}),
+ 'value_boolean': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'value_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'value_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.AttributeEntity']", 'null': 'True', 'blank': 'True'}),
+ 'value_float': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
+ 'value_integer': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'value_option': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.AttributeOption']", 'null': 'True', 'blank': 'True'}),
+ 'value_richtext': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'value_text': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'catalogue.productcategory': {
+ 'Meta': {'ordering': "['-is_canonical']", 'object_name': 'ProductCategory'},
+ 'category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Category']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_canonical': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'product': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Product']"})
+ },
+ 'catalogue.productclass': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'ProductClass'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'options': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['catalogue.Option']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'})
+ },
+ 'catalogue.productrecommendation': {
+ 'Meta': {'object_name': 'ProductRecommendation'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'primary': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'primary_recommendations'", 'to': "orm['catalogue.Product']"}),
+ 'ranking': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '0'}),
+ 'recommendation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Product']"})
+ },
+ 'offer.benefit': {
+ 'Meta': {'object_name': 'Benefit'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'max_affected_items': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'range': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['offer.Range']", 'null': 'True', 'blank': 'True'}),
+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'value': ('oscar.models.fields.PositiveDecimalField', [], {'null': 'True', 'max_digits': '12', 'decimal_places': '2', 'blank': 'True'})
+ },
+ 'offer.condition': {
+ 'Meta': {'object_name': 'Condition'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'range': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['offer.Range']"}),
+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'value': ('oscar.models.fields.PositiveDecimalField', [], {'max_digits': '12', 'decimal_places': '2'})
+ },
+ 'offer.conditionaloffer': {
+ 'Meta': {'ordering': "['-priority']", 'object_name': 'ConditionalOffer'},
+ 'benefit': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['offer.Benefit']"}),
+ 'condition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['offer.Condition']"}),
+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'end_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
+ 'num_orders': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'offer_type': ('django.db.models.fields.CharField', [], {'default': "'Site'", 'max_length': '128'}),
+ 'priority': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'redirect_url': ('oscar.models.fields.ExtendedURLField', [], {'max_length': '200', 'blank': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '128', 'unique': 'True', 'null': 'True', 'db_index': 'True'}),
+ 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'total_discount': ('django.db.models.fields.DecimalField', [], {'default': "'0.00'", 'max_digits': '12', 'decimal_places': '2'})
+ },
+ 'offer.range': {
+ 'Meta': {'object_name': 'Range'},
+ 'classes': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'classes'", 'blank': 'True', 'to': "orm['catalogue.ProductClass']"}),
+ 'excluded_products': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'excludes'", 'blank': 'True', 'to': "orm['catalogue.Product']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'included_categories': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'includes'", 'blank': 'True', 'to': "orm['catalogue.Category']"}),
+ 'included_products': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'includes'", 'blank': 'True', 'to': "orm['catalogue.Product']"}),
+ 'includes_all_products': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'})
+ }
+ }
+
+ complete_apps = ['offer']
View
@@ -3,9 +3,11 @@
import datetime
from django.core import exceptions
+from django.template.defaultfilters import slugify
from django.db import models
from django.utils.translation import ugettext as _
from django.core.exceptions import ValidationError
+from django.core.urlresolvers import reverse
from django.conf import settings
from oscar.apps.offer.managers import ActiveOfferManager
@@ -18,7 +20,10 @@ class ConditionalOffer(models.Model):
"""
A conditional offer (eg buy 1, get 10% off)
"""
- name = models.CharField(max_length=128, unique=True)
+ name = models.CharField(max_length=128, unique=True,
+ help_text="""This is displayed within the customer's
+ basket""")
+ slug = models.SlugField(max_length=128, unique=True, null=True)
description = models.TextField(blank=True, null=True)
# Offers come in a few different types:
@@ -68,6 +73,14 @@ class ConditionalOffer(models.Model):
class Meta:
ordering = ['-priority']
+
+ def save(self, *args, **kwargs):
+ if not self.slug:
+ self.slug = slugify(self.name)
+ return super(ConditionalOffer, self).save(*args, **kwargs)
+
+ def get_absolute_url(self):
+ return reverse('offer:detail', kwargs={'slug': self.slug})
def __unicode__(self):
return self.name
@@ -9,11 +9,10 @@
class OfferDetailView(ListView):
context_object_name = 'products'
template_name = 'offer/detail.html'
- template_name_expired = 'offer/detail.html'
paginate_by = 20
def get(self, request, *args, **kwargs):
- self.offer = get_object_or_404(ConditionalOffer, id=self.kwargs['pk'])
+ self.offer = get_object_or_404(ConditionalOffer, slug=self.kwargs['slug'])
return super(OfferDetailView, self).get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
@@ -17,7 +17,6 @@
</div>
{% endblock header %}
-
{% block content %}
{% if basket_warnings %}
@@ -30,7 +29,7 @@
{% if upsell_messages %}
<h2>You could be missing out on offers!</h2>
{% for upsell in upsell_messages %}
- <div class="warning">{{ upsell.message }} to quality for the '{{ upsell.offer.name }}' special offer</div>
+ <div class="warning">{{ upsell.message }} to quality for the <a href="{{ upsell.offer.get_absolute_url }}">{{ upsell.offer.name }}</a> special offer</div>
{% endfor %}
{% endif %}
@@ -53,7 +53,7 @@
</tr>
{% for offer in offers %}
<tr>
- <td>{{ offer.name }}</td>
+ <td><a href="{{ offer.get_absolute_url }}">{{ offer.name }}</a></td>
<td>{{ offer.start_date }}</td>
<td>{{ offer.end_date }}</td>
<td>{{ offer.condition.description }}</td>
@@ -28,6 +28,13 @@
<div class="sub-header">
<h1>{{ offer.name }}</h1>
</div>
+
+ {% if not offer.is_active %}
+ <div class="alert alert-error">
+ This offer has expired
+ </div>
+ {% endif %}
+
{% if offer.description %}
<h3>Description</h3>
<p>{{ offer.description }}</p>

0 comments on commit 1ccbec2

Please sign in to comment.