Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Writing a Django e-commerce framework

Writing a Django e-commerce framework

From OSCON 2012. This presentation discusses the reasons for writing an e-commerce framework and details a number of techniques for customising Django apps.

David Winterbottom

July 18, 2012
Tweet

More Decks by David Winterbottom

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

  4. Motivation
    Friday, 20 July 12

    View Slide

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

    View Slide

  6. Books
    Friday, 20 July 12

    View Slide

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

    View Slide

  8. 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

    View Slide

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

    View Slide

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

    View Slide

  11. Nailvarnish
    Friday, 20 July 12

    View Slide

  12. T-­‐shirts
    Friday, 20 July 12

    View Slide

  13. Digital  music
    Friday, 20 July 12

    View Slide

  14. Bar  equipment
    Friday, 20 July 12

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  18. Oscar
    Friday, 20 July 12

    View Slide

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

    View Slide

  20. Friday, 20 July 12

    View Slide

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

    View Slide

  22. Friday, 20 July 12

    View Slide

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

    View Slide

  24. Design  decisions
    Friday, 20 July 12

    View Slide

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

    View Slide

  26. Friday, 20 July 12

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  30. Customisation
    Friday, 20 July 12

    View Slide

  31. DEBUG = True
    settings.py
    Friday, 20 July 12

    View Slide

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

    View Slide

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

    View Slide

  34.  Templates
    Friday, 20 July 12

    View Slide

  35. Friday, 20 July 12

    View Slide

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

    View Slide

  37. 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

    View Slide

  38. # base.html
    {% extends 'templates/base.html' %}
    {% block scripts %}

    {% endblock %}
    Override
    Friday, 20 July 12

    View Slide

  39. # base.html
    {% extends 'templates/base.html' %}
    {% block scripts %}

    {{ block.super }}
    {% endblock %}
    Extend
    Friday, 20 July 12

    View Slide

  40. 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

    View Slide

  41. Apps
    Friday, 20 July 12

    View Slide

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

    View Slide

  43. 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

    View Slide

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

    View Slide

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

    View Slide

  46. # 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  50. # 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

    View Slide

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

    View Slide

  52. Tips  and  advice
    Friday, 20 July 12

    View Slide

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

    View Slide

  54. Audit  models
    Friday, 20 July 12

    View Slide

  55. 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

    View Slide

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

    View Slide

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

    View Slide

  58. 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

    View Slide

  59. 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

    View Slide

  60. 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

    View Slide