Slide 1

Slide 1 text

Writing  a  Django   e-­‐commerce   framework OSCON  2012 Friday, 20 July 12

Slide 2

Slide 2 text

Me •David  Winterbottom  /  @codeinthehole •Tangent  Labs,  London •Head  of  E-­‐commerce  team •Python  hacker •commandlinefu.com,  django-­‐oscar Friday, 20 July 12

Slide 3

Slide 3 text

Synopsis •Motivation •Oscar  -­‐  what  problem  does  it  solve? •Design  decisions  -­‐  customisation  techniques •E-­‐commerce  tips/advice Friday, 20 July 12

Slide 4

Slide 4 text

Motivation Friday, 20 July 12

Slide 5

Slide 5 text

•5  years  of  e-­‐commerce  projects •PHP  =>  Python  /  Django •Lots  of  war  stories Tangent  Labs Friday, 20 July 12

Slide 6

Slide 6 text

Books Friday, 20 July 12

Slide 7

Slide 7 text

Bookshop  domain •Millions  of  products •ISBNs,  BIC/BISAC  categories,  authors,  publishers •Automated  feed-­‐driven  processing •Catalogue •Stock •Rich  data Friday, 20 July 12

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

Variations •New  fulfilment  partners •Stock/availability  logic •Multiple  payment  partners •Split-­‐payment  sources •Multibuy  offers,  vouchers •eBooks! Friday, 20 July 12

Slide 10

Slide 10 text

PHP  framework •Home-­‐rolled  web  framework •Customisation  via  include  path  overrides •Lots  of  duplication •Hard  to  upgrade Friday, 20 July 12

Slide 11

Slide 11 text

Nailvarnish Friday, 20 July 12

Slide 12

Slide 12 text

T-­‐shirts Friday, 20 July 12

Slide 13

Slide 13 text

Digital  music Friday, 20 July 12

Slide 14

Slide 14 text

Bar  equipment Friday, 20 July 12

Slide 15

Slide 15 text

New  requirements •Product  variations  -­‐  colours,  sizes  etc •New  fulfilment  processes:   •webservices  /  warehouses •Per-­‐customer  prices Friday, 20 July 12

Slide 16

Slide 16 text

B2B •Sales  reps,  customer  hierarchies •Tax  rules •Managed  budgets •Integration  with  “enterprise”  partners Friday, 20 July 12

Slide 17

Slide 17 text

•Original  assumptions  all  broken •Domains  vary  wildly •Lots  of  work-­‐arounds Friday, 20 July 12

Slide 18

Slide 18 text

Oscar Friday, 20 July 12

Slide 19

Slide 19 text

Requirements •Lean  -­‐  as  few  assumptions  as  possible •Models  domain   •without  duplication •without  too  much  meta-­‐data Friday, 20 July 12

Slide 20

Slide 20 text

Friday, 20 July 12

Slide 21

Slide 21 text

Domain  modelling •Implementation  deeply   connected  to  core  business   concepts •“...where  powerful  new   features  unfold  as  corollaries  to   older  features.” Friday, 20 July 12

Slide 22

Slide 22 text

Friday, 20 July 12

Slide 23

Slide 23 text

Lots  of  others •Satchmo   •Lightning-­‐Fast-­‐Shop •Satchless •Django-­‐shop •Plata,  Mamona,  Cartridge,  ... •http://djangopackages.com/grids/g/ecommerce/ Friday, 20 July 12

Slide 24

Slide 24 text

Design  decisions Friday, 20 July 12

Slide 25

Slide 25 text

Python •Decimal  support •**kwargs •Mixins Friday, 20 July 12

Slide 26

Slide 26 text

Friday, 20 July 12

Slide 27

Slide 27 text

Web  framework •Well-­‐thought  out  structure •Templating,  HTTP,  security,  caching,  ... •Class-­‐based  views Friday, 20 July 12

Slide 28

Slide 28 text

Models •Models  drive  the  design •Capture  domain  logic •Foundation  for  rest  of  application Friday, 20 July 12

Slide 29

Slide 29 text

Eco-­‐system •Haystack  (search) •South  (database  migrations) •Celery  (job  queue) •Internal  libraries Friday, 20 July 12

Slide 30

Slide 30 text

Customisation Friday, 20 July 12

Slide 31

Slide 31 text

DEBUG = True settings.py Friday, 20 July 12

Slide 32

Slide 32 text

AUTH_PROFILE_MODULE = ‘customer.Profile’ settings.py Friday, 20 July 12

Slide 33

Slide 33 text

Different  approach •Key  idea: •Having  the  same  name/identifier  as  your  parent   •Subclass  and  override Friday, 20 July 12

Slide 34

Slide 34 text

 Templates Friday, 20 July 12

Slide 35

Slide 35 text

Friday, 20 July 12

Slide 36

Slide 36 text

TEMPLATE_LOADERS = ( 'django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader', ) TEMPLATE_DIRS = ( '/var/www/project/templates/', ) Replace Friday, 20 July 12

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

# base.html {% extends 'templates/base.html' %} {% block scripts %} {% endblock %} Override Friday, 20 July 12

Slide 39

Slide 39 text

# base.html {% extends 'templates/base.html' %} {% block scripts %} {{ block.super }} {% endblock %} Extend Friday, 20 July 12

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

Apps Friday, 20 July 12

Slide 42

Slide 42 text

from django.db.loading import get_model Line = get_model('basket', 'Line') Dynamic  loading  1 INSTALLED_APPS = ( 'oscar.apps.basket' ) Friday, 20 July 12

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

INSTALLED_APPS = ( 'oscar', 'oscar.apps.catalogue', 'oscar.apps.basket', 'oscar.apps.checkout', 'oscar.apps.order', ... ) Friday, 20 July 12

Slide 45

Slide 45 text

INSTALLED_APPS = ( 'oscar', 'oscar.apps.catalogue', 'myproject.basket', 'oscar.apps.checkout', 'oscar.apps.order', ... ) Same  app  label  as   parent  app Friday, 20 July 12

Slide 46

Slide 46 text

# myproject/basket/models.py from django.db import models from 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

Slide 47

Slide 47 text

INSTALLED_APPS = ( 'oscar', 'oscar.apps.catalogue', 'oscar.apps.basket', 'oscar.apps.checkout', 'oscar.apps.order', ... ) templatetags management  commands Friday, 20 July 12

Slide 48

Slide 48 text

Recap •App  label    is  the  ID •Models  can  overridden  and  extended Friday, 20 July 12

Slide 49

Slide 49 text

from oscar.core.loading import get_class OrderCreator = get_class('order.utils', 'OrderCreator') Dynamic  loading  2 Friday, 20 July 12

Slide 50

Slide 50 text

# 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

Slide 51

Slide 51 text

Recap •(App  module,  class  name)    is  the  ID •Any  class  can  be  overridden  or  extended Friday, 20 July 12

Slide 52

Slide 52 text

Tips  and  advice Friday, 20 July 12

Slide 53

Slide 53 text

Log  everything ...except  sensitive  customer  details. Friday, 20 July 12

Slide 54

Slide 54 text

Audit  models Friday, 20 July 12

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

•High  and  low  thresholds •It’s  always  your  fault  when  things  go  wrong Monitor  everything Friday, 20 July 12

Slide 57

Slide 57 text

Service  layers •Avoid  views  talking  to  your  models  directly •See  ‘Facade’  design  pattern •Anti-­‐corruption  layers Friday, 20 July 12

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

Image  credits Books: http://www.flickr.com/photos/th3ph17/3091294342/ Nail varnish: http://www.flickr.com/photos/kqedquest/831547339/ T-shirts: http://www.flickr.com/photos/blazerman/177165473/ Friday, 20 July 12