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.