writing’a’django’ ecommerce’ - o'reilly mediaassets.en.oreilly.com/1/event/80/writing a...

60
Writing a Django ecommerce framework OSCON 2012 Friday, 20 July 12

Upload: leliem

Post on 03-Apr-2018

218 views

Category:

Documents


2 download

TRANSCRIPT

Writing  a  Django  e-­‐commerce  framework

OSCON  2012

Friday, 20 July 12

Me•David  Winterbottom  /  @codeinthehole

•Tangent  Labs,  London

•Head  of  E-­‐commerce  team

•Python  hacker

•commandlinefu.com,  django-­‐oscar

Friday, 20 July 12

Synopsis•Motivation

•Oscar  -­‐  what  problem  does  it  solve?

•Design  decisions  -­‐  customisation  techniques

•E-­‐commerce  tips/advice

Friday, 20 July 12

Motivation

Friday, 20 July 12

•5  years  of  e-­‐commerce  projects

•PHP  =>  Python  /  Django

•Lots  of  war  stories

Tangent  Labs

Friday, 20 July 12

Books

Friday, 20 July 12

Bookshop  domain•Millions  of  products

•ISBNs,  BIC/BISAC  categories,  authors,  publishers

•Automated  feed-­‐driven  processing

•Catalogue

•Stock

•Rich  data

Friday, 20 July 12

Two  tier  structure•Application  tier  -­‐  serves  HTTP  requests

•Processing  tier:

•Download  and  import  feeds

•Data  cleaning  and  processing

•Periodic  jobs  for  fulfilment

Friday, 20 July 12

Variations•New  fulfilment  partners

•Stock/availability  logic

•Multiple  payment  partners

•Split-­‐payment  sources

•Multibuy  offers,  vouchers

•eBooks!

Friday, 20 July 12

PHP  framework•Home-­‐rolled  web  framework

•Customisation  via  include  path  overrides

•Lots  of  duplication

•Hard  to  upgrade

Friday, 20 July 12

Nailvarnish

Friday, 20 July 12

T-­‐shirts

Friday, 20 July 12

Digital  music

Friday, 20 July 12

Bar  equipment

Friday, 20 July 12

New  requirements•Product  variations  -­‐  colours,  sizes  etc

•New  fulfilment  processes:  

•webservices  /  warehouses

•Per-­‐customer  prices

Friday, 20 July 12

B2B•Sales  reps,  customer  hierarchies

•Tax  rules

•Managed  budgets

•Integration  with  “enterprise”  partners

Friday, 20 July 12

•Original  assumptions  all  broken

•Domains  vary  wildly

•Lots  of  work-­‐arounds

Friday, 20 July 12

Oscar

Friday, 20 July 12

Requirements•Lean  -­‐  as  few  assumptions  as  possible

•Models  domain  

•without  duplication

•without  too  much  meta-­‐data

Friday, 20 July 12

Friday, 20 July 12

Domain  modelling•Implementation  deeply  connected  to  core  business  concepts

•“...where  powerful  new  features  unfold  as  corollaries  to  older  features.”

Friday, 20 July 12

Friday, 20 July 12

Lots  of  others•Satchmo  

•Lightning-­‐Fast-­‐Shop

•Satchless

•Django-­‐shop

•Plata,  Mamona,  Cartridge,  ...

•http://djangopackages.com/grids/g/ecommerce/

Friday, 20 July 12

Design  decisions

Friday, 20 July 12

Python

•Decimal  support

•**kwargs

•Mixins

Friday, 20 July 12

Friday, 20 July 12

Web  framework

•Well-­‐thought  out  structure

•Templating,  HTTP,  security,  caching,  ...

•Class-­‐based  views

Friday, 20 July 12

Models

•Models  drive  the  design

•Capture  domain  logic

•Foundation  for  rest  of  application

Friday, 20 July 12

Eco-­‐system•Haystack  (search)

•South  (database  migrations)

•Celery  (job  queue)

•Internal  libraries

Friday, 20 July 12

Customisation

Friday, 20 July 12

DEBUG = True

settings.py

Friday, 20 July 12

AUTH_PROFILE_MODULE = ‘customer.Profile’

settings.py

Friday, 20 July 12

Different  approach

•Key  idea:

•Having  the  same  name/identifier  as  your  parent  

•Subclass  and  override

Friday, 20 July 12

 Templates

Friday, 20 July 12

Friday, 20 July 12

TEMPLATE_LOADERS = ( 'django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader',)

TEMPLATE_DIRS = ( '/var/www/project/templates/',)

Replace

Friday, 20 July 12

TEMPLATE_LOADERS = ( 'django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader',)

TEMPLATE_DIRS = ( '/var/www/project/templates/' '/path/to/oscar/',)

Override

parent  of  Oscar’s  template  directory

Friday, 20 July 12

# base.html

{% extends 'templates/base.html' %}

{% block scripts %}<script src="new.js" type="text/javascript"></script>{% endblock %}

Override

Friday, 20 July 12

# base.html

{% extends 'templates/base.html' %}

{% block scripts %}<script src="new.js" type="text/javascript"></script>{{ block.super }}{% endblock %}

Extend

Friday, 20 July 12

Recap•Filename  is  the  ID

•Use  include-­‐path  trick  to  “subclass”  parent  

•Use  blocks  to  provide  hook  points

•...but,  don’t  go  too  far

Friday, 20 July 12

Apps

Friday, 20 July 12

from django.db.loading import get_model

Line = get_model('basket', 'Line')

Dynamic  loading  1

INSTALLED_APPS = ( 'oscar.apps.basket')

Friday, 20 July 12

from django.db.loading import get_model

Line = get_model('basket', 'Line')

Dynamic  loading  1

INSTALLED_APPS = ( 'oscar.apps.basket')

‘App  label’

Friday, 20 July 12

INSTALLED_APPS = ( 'oscar', 'oscar.apps.catalogue', 'oscar.apps.basket', 'oscar.apps.checkout', 'oscar.apps.order', ...)

Friday, 20 July 12

INSTALLED_APPS = ( 'oscar', 'oscar.apps.catalogue', 'myproject.basket', 'oscar.apps.checkout', 'oscar.apps.order', ...)

Same  app  label  as  parent  app

Friday, 20 July 12

# myproject/basket/models.py

from django.db import modelsfrom oscar.apps.basket.abstract_models import \ AbstractLine

class Line(AbstractLine): cost_centre = models.CharField(max_length=64)

from oscar.apps.basket.models import *

Friday, 20 July 12

INSTALLED_APPS = ( 'oscar', 'oscar.apps.catalogue', 'oscar.apps.basket', 'oscar.apps.checkout', 'oscar.apps.order', ...)

templatetagsmanagement  commands

Friday, 20 July 12

Recap

•App  label    is  the  ID

•Models  can  overridden  and  extended

Friday, 20 July 12

from oscar.core.loading import get_class

OrderCreator = get_class('order.utils', 'OrderCreator')

Dynamic  loading  2

Friday, 20 July 12

# order/utils.py

from oscar.apps.order.utils import OrderCreator as \ CoreOrderCreator

class OrderCreator(CoreOrderCreator):

def allocate_stock(self, line): # Override/extend method ...

Friday, 20 July 12

Recap

•(App  module,  class  name)    is  the  ID

•Any  class  can  be  overridden  or  extended

Friday, 20 July 12

Tips  and  advice

Friday, 20 July 12

Log  everything...except  sensitive  customer  details.

Friday, 20 July 12

Audit  models

Friday, 20 July 12

from django.db import models

class NielsenDataFile(models.Model): filepath = models.CharField(max_length=128) PENDING, FAILED, PROCESSED = range(3) status = models.IntegerField(default=PENDING)

num_valid_records = models.IntegerField() num_invalid_records = models.IntegerField() date_downloaded = models.DateTimeField(null=True) date_processed = models.DateTimeField(null=True)

Friday, 20 July 12

•High  and  low  thresholds

•It’s  always  your  fault  when  things  go  wrong

Monitor  everything

Friday, 20 July 12

Service  layers•Avoid  views  talking  to  your  models  directly

•See  ‘Facade’  design  pattern

•Anti-­‐corruption  layers

Friday, 20 July 12

Generic  vs  bespoke•“If  you’re  using  a  framework,  you  aren’t  doing  good  modelling”

•Capture  the  domain  correctly  

•It’s  ok  to  throw  away  the  framework

Friday, 20 July 12

Summary•Django  is  great  for  modelling  complex  domains

•Writing  customisable  django  apps  isn’t  easy

•Oscar’s  future:  

•NoSQL  -­‐  no  more  EAV

Friday, 20 July 12