Inside Django Oscar dashboard, there is a part for managing content which contains:
- Content blocks
- Content blocks by page
- Pages
- Email template
- Reviews
Within entry scope, we only touch the first 3 items. Page actually is Flatpage object, it contains basic fields like: title, url, and content. For more information about Flatpage, visit Django Flatpages. Content block, like its name, is single block of content inside given page. It could be Raw HTML, Linked Image, Image,...
The third item "Content blocks by page" plays role of linkage between Pages and Content Blocks. What block(s) will stay in what page(s), how they will be positioned.
Standard (or stock) Oscar only provides 6 types of content blocks: Raw HTML, Image, Single Product, Automatic Product List, Hand Picked Product List, Multi Image
Those, in most case, well afford all normal needs of page content creation. But if you need a new special one, with unique styling, you should create a new content block type.
Note:Inside Oscar, another name of content block is Promotion. And content blocks are objects of models inside oscar.apps.promotions.models.
Steps for making new Promotion/Content Block are:
1. Create new application for deriving oscar.apps.promotions modules:
__init__.py
admin.py
models.py
conf.py
2. Inside models.py, we have something like this:
from django.db import models
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from oscar.apps.promotions.models import AbstractPromotion
from oscar.models.fields import ExtendedURLField
class NewsPromotion(AbstractPromotion):
"""
Model for news, events, press, or competitions object.
"""
_type = 'News'
name = models.CharField(_("Name"), max_length=128)
description = models.TextField(_("Description"))
link_url = ExtendedURLField(
_('Link URL'), blank=True,
help_text=_('This is where this promotion links to'))
image = models.ImageField(
_('Image'), upload_to=settings.OSCAR_PROMOTION_FOLDER,
max_length=255)
date_created = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.name
class Meta:
verbose_name = _("News")
verbose_name_plural = _("News")
This looks just like the Image Promotion, but having new description field for additional information
3. admin.py is like other admin.py module, for registering NewsPromotion to be shown in admin section.
4. Inside conf.py, we override the PROMOTION_CLASSES, add new type(s) there:
from models import NewsPromotion
from oscar.apps.promotions.conf import *
def get_promotion_classes():
return (RawHTML, Image, NewsPromotion, SingleProduct,
AutomaticProductList, HandPickedProductList, MultiImage)
PROMOTION_CLASSES = get_promotion_classes()
5. Now, add the new app into project setting file, inside the INSTALLED_APPS definition. Make sure it stands above the Oscar apps registration:
INSTALLED_APPS = [
...
'new_promotions',
'new_dashboard',
...
]
from oscar import get_core_apps
INSTALLED_APPS = INSTALLED_APPS + get_core_apps()
Have you notified the "new_dashboard"? We will have to customize it in order to have new content block shown inside dashboard.
6. Create new application for deriving oscar.apps.dashboard, we name it 'new_dashboard' for example:
__init__.py
app.py
promotions/__init__.py
promotions/app.py
promotions/forms.py
promotions/views.py
7. Because we have new view for NewsPromotion, we must override the app.py module of oscar.apps.dashboard. Short, and simple:
from oscar.apps.dashboard.app import DashboardApplication as \
CoreDashboardApp
from promotions.app import application
class DashboardApplication(CoreDashboardApp):
promotions_app = application
application = DashboardApplication()
8. We also have to override the nested application "oscars.apps.dashboard.promotions". Its app.py is a bit longer for some altered configurations (here, it used the new PROMOTION_CLASSES overrided above)
from django.conf.urls import patterns, url
from oscar.apps.dashboard.promotions.app import PromotionsDashboardApplication \
as CorePromotionsDashboardApplication
from apps.dashboard.promotions import views
from apps.promotions.conf import PROMOTION_CLASSES
class PromotionsDashboardApplication(CorePromotionsDashboardApplication):
list_view = views.ListView
page_list = views.PageListView
page_detail = views.PageDetailView
create_redirect_view = views.CreateRedirectView
delete_page_promotion_view = views.DeletePagePromotionView
for klass in PROMOTION_CLASSES:
locals()['create_%s_view' % klass.classname()] \
= getattr(views, 'Create%sView' % klass.__name__)
locals()['update_%s_view' % klass.classname()] \
= getattr(views, 'Update%sView' % klass.__name__)
locals()['delete_%s_view' % klass.classname()] \
= getattr(views, 'Delete%sView' % klass.__name__)
def get_urls(self):
urls = [
url(r'^$', self.list_view.as_view(), name='promotion-list'),
url(r'^pages/$', self.page_list.as_view(),
name='promotion-list-by-page'),
url(r'^page/(?P<path>/([\w-]+(/[\w-]+)*/)?)$',
self.page_detail.as_view(), name='promotion-list-by-url'),
url(r'^create/$',
self.create_redirect_view.as_view(),
name='promotion-create-redirect'),
url(r'^page-promotion/(?P<pk>\d+)/$',
self.delete_page_promotion_view.as_view(),
name='pagepromotion-delete')]
for klass in PROMOTION_CLASSES:
code = klass.classname()
urls += [
url(r'create/%s/' % code,
getattr(self, 'create_%s_view' % code).as_view(),
name='promotion-create-%s' % code),
url(r'^update/(?P<ptype>%s)/(?P<pk>\d+)/$' % code,
getattr(self, 'update_%s_view' % code).as_view(),
name='promotion-update'),
url(r'^delete/(?P<ptype>%s)/(?P<pk>\d+)/$' % code,
getattr(self, 'delete_%s_view' % code).as_view(),
name='promotion-delete')]
return self.post_process_urls(patterns('', *urls))
application = PromotionsDashboardApplication()
9. Also the forms.py, where new PROMOTION_CLASSES is used:
from django.utils.translation import ugettext_lazy as _
from oscar.apps.dashboard.promotions.forms import *
from apps.promotions.conf import PROMOTION_CLASSES
class PromotionTypeSelectForm(forms.Form):
choices = []
for klass in PROMOTION_CLASSES:
choices.append((klass.classname(), klass._meta.verbose_name))
promotion_type = forms.ChoiceField(choices=tuple(choices),
label=_("Promotion type"))
10. Finally with promotions app, views.py with new views for NewsPromotions:
from oscar.apps.dashboard.promotions.views import *
from apps.promotions.conf import PROMOTION_CLASSES
from apps.promotions.models import NewsPromotion
from forms import PromotionTypeSelectForm as SelectForm
class ListView(generic.TemplateView):
template_name = 'dashboard/promotions/promotion_list.html'
def get_context_data(self):
# Need to load all promotions of all types and chain them together
# no pagination required for now.
data = []
num_promotions = 0
for klass in PROMOTION_CLASSES:
objects = klass.objects.all()
num_promotions += objects.count()
data.append(objects)
promotions = itertools.chain(*data)
ctx = {
'num_promotions': num_promotions,
'promotions': promotions,
'select_form': SelectForm(),
}
return ctx
class CreateRedirectView(generic.RedirectView):
permanent = True
def get_redirect_url(self, **kwargs):
code = self.request.GET.get('promotion_type', None)
urls = {}
for klass in PROMOTION_CLASSES:
urls[klass.classname()] = reverse('dashboard:promotion-create-%s' %
klass.classname())
return urls.get(code, None)
class CreateNewsPromotionView(CreateView):
model = NewsPromotion
class UpdateNewsPromotionView(UpdateView):
model = NewsPromotion
class DeleteNewsPromotionView(DeleteView):
model = NewsPromotion
11. Don't forget to add "new_dashboard" into INSTALLED_APPS inside settings file
12. Almost last, a new template with the same name, 'newspromotion.html', must be present in templates path (here is "promotions/newspromotion.html"):
{% load staticfiles %}
<div class="promotion-news">
{% if promotion.image %}
{% if promotion.link_url %}
<div class="promotion-block widget-image-link">
<a href="{{ promotion.link_url }}">
<img src="{% static promotion.image.url %}" alt="{{ promotion.name }}" />
{% comment %}
<h3>{{ promotion.name }} <i class="icon-arrow-right hidden-phone"></i></h3>
{% endcomment %}
</a>
</div>
{% else %}
<img src="{% static promotion.image.url %}" title="{{ promotion.name }}" />
{% endif %}
{% endif %}
<div class="news-info">
<h4>{{ promotion.name }}</h4>
<div class="promotion-date">{{ promotion.date_created|date:"F d, Y" }}</div>
<div class="promotion-description">{{ promotion.description|safe }}</div>
</div>
</div>
13. Do the sync or migrate for making new model active.