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

DjangoCon Europe 2013 Keynote

DjangoCon Europe 2013 Keynote

67e05420d4dd3492097aeb77f44f7867?s=128

Daniel Greenfeld

May 17, 2013
Tweet

More Decks by Daniel Greenfeld

Other Decks in Technology

Transcript

  1. @pydanny pydanny@cartwheelweb.com Keynote DjangoCircus 2013

  2. @pydanny pydanny@cartwheelweb.com Daniel Greenfeld Hi everyone. My name is Daniel

    Greenfeld. I’m the guy with the unenviable task of being the keynote after Brandon Rhodes.
  3. @pydanny pydanny@cartwheelweb.com @pydanny This is me.

  4. @pydanny pydanny@cartwheelweb.com I work as a principal for Cartwheel Web,

    doing Python and Django. The company is named after cartwheels.
  5. @pydanny pydanny@cartwheelweb.com http://2scoops.org Danny: 128,546++ Audrey: 121,871++ Co-wrote a book.

    There is Audrey. She half of the book.
  6. @pydanny pydanny@cartwheelweb.com Django conferences have a tradition. Django has a

    tradition.
  7. @pydanny pydanny@cartwheelweb.com e Django Tradition of Invited Critical Talks This

    is where some luminary is invited to give a talk that takes Django to task in some way.
  8. @pydanny pydanny@cartwheelweb.com is Isn’t A Hate Django Talk. I’m not

    giving one of those talks. Why not?
  9. @pydanny pydanny@cartwheelweb.com Why Not?

  10. @pydanny pydanny@cartwheelweb.com Negative It’s Too Easy to be

  11. @pydanny pydanny@cartwheelweb.com Example: “Class Based Views Suck” Saying this is

    easy. I used to say it all the time. Twitter, IRC, forums
  12. @pydanny pydanny@cartwheelweb.com You Know What’s Hard?

  13. @pydanny pydanny@cartwheelweb.com Working Stuff Delivering

  14. @pydanny pydanny@cartwheelweb.com Rants are fun but...

  15. @pydanny pydanny@cartwheelweb.com No one would remember Zed Shaw if he

    hadn’t written Learn Python the Hard Way* *Just one of many of his efforts.
  16. @pydanny pydanny@cartwheelweb.com Contribute Get Your Hands Dirty and

  17. @pydanny pydanny@cartwheelweb.com Running Events Writing Books Pull Requests Jannis used

    to say this all the time to me. This event is awesome. Learn Python the Hard Way. Two Scoops of Django
  18. @pydanny pydanny@cartwheelweb.com is Isn’t A Hate Django Talk. So no,

    this isn’t a rant about Django. Instead...
  19. @pydanny pydanny@cartwheelweb.com Django is Awesome ...it’s a celebration about what

    makes Django awesome. Which was the result of a lot of people delivering working stuff. Their hard work has made so much possible.
  20. @pydanny pydanny@cartwheelweb.com Why is Django Awesome?

  21. @pydanny pydanny@cartwheelweb.com Reason #1 Django is Everywhere

  22. @pydanny pydanny@cartwheelweb.com Django Everywhere NASA Disqus Google Grove.io HP IBM

    Instagram Mozilla Sony Cashstar BitBucket Canonical PBS National Geographic Microsoft Humble Bundle Django Circus djangocon.com PBS Pinterest Rdio e Onion Library of Congress Causes Politifact OpenStack Rackspace Discovery Channel Newspapers Lexis Nexis 2scoops.org Lanyrd Gondor
  23. @pydanny pydanny@cartwheelweb.com Django Everywhere NASA Disqus Google Grove.io HP IBM

    Instagram Mozilla Sony Cashstar BitBucket Canonical PBS National Geographic Microsoft Humble Bundle Django Circus djangocon.com PBS Pinterest Rdio e Onion Library of Congress Causes Politifact OpenStack Rackspace Discovery Channel Newspapers Lexis Nexis 2scoops.org Lanyrd Gondor Django is used on many projects. NASA’s Nebula begat OpenStack. I started with Django while at NASA. One of NASA’s products is Nebula, the god-parent of OpenStack.
  24. @pydanny pydanny@cartwheelweb.com Reason #2 Django is Powered by Python

  25. @pydanny pydanny@cartwheelweb.com Python is Awesome

  26. @pydanny pydanny@cartwheelweb.com Python has a Style Guide PEP-0008

  27. @pydanny pydanny@cartwheelweb.com Python has Zen PEP-0020

  28. @pydanny pydanny@cartwheelweb.com Most importantly...

  29. @pydanny pydanny@cartwheelweb.com Python shares the same rst two letters as

    me. Python Pydanny
  30. @pydanny pydanny@cartwheelweb.com Reason #3 Django has Awesome APIs

  31. @pydanny pydanny@cartwheelweb.com API win: Understandable No funky names besides ‘django’

    models views templates forms settings admin messages caching logging sessions No funky names except for Django, which means we don’t have to spend much time memorizing colorful names for common objects.
  32. @pydanny pydanny@cartwheelweb.com Case Study: python-requests vs. django.test.client

  33. @pydanny pydanny@cartwheelweb.com python-requests vs. django.test.client >>> r = requests.get('http://127.0.0.1:8000') >>>

    r.status_code 200 >>> params = {'key1': 'value1', 'key2': 'value2'} >>> r = requests.get("http://127.0.0.1:8000", params) >>> r.status_code 200 >>> import requests >>> from django.test.client import Client >>> requests = Client() Can anyone recognize this code? It’s requests in action, right? Nope, it’s the django test client! Anyway, I think it’s says something when two popular toolsets have similar API designs.
  34. @pydanny pydanny@cartwheelweb.com API Win Conventions

  35. @pydanny pydanny@cartwheelweb.com forms.py models.py views.py templates/ Forms Models Views Templates

  36. @pydanny pydanny@cartwheelweb.com forms.py from  django  import  forms from  pies.models  import

     Pie     class  PieForm(forms.ModelForm):          class  Meta:                model  =  Pie                fields  =  ('filling',  ) No code names. No puns. No underscores. Intuitive.
  37. @pydanny pydanny@cartwheelweb.com from  django.db  import  models     class  Pie(models.Model):

             STATUS_UNBAKED,  STATUS_BAKED  =  0,  1        FILLING_APPLE,  FILLING_MEAT  =  0,  1        STATUS_CHOICES  =  (                (STATUS_UNBAKED,  "Unbaked"),                (STATUS_BAKED,  "Baked")        )        FILLING_CHOICES  =  (                (FILLING_APPLE,  "Apple"),                (FILLING_MEAT,  "Meat"),        )          status  =  models.IntegerField(choices=STATUS_CHOICES,                                                  default=STATUS_UNBAKED)        filling  =  models.IntegerField(choices=FILLING_CHOICES)          def  __unicode__(self):                return  "{status}  {filling}  pie".format(                        status=self.get_status_display(),                        filling=self.get_filling_display()                ) models.py The Pie inherits right from Models. How awesome is that? What about model field definitions? Obvious!
  38. @pydanny pydanny@cartwheelweb.com Pro-Tip

  39. @pydanny pydanny@cartwheelweb.com pro-tip While Fat Models are Great Fat models

    are when you move as much logic as possible from views to models, so that you can more readily re-use the same code in more than one view. Very commonly between HTML and JSON versions of views.
  40. @pydanny pydanny@cartwheelweb.com pro-tip Overly Large Models Are Not

  41. @pydanny pydanny@cartwheelweb.com Really. class  Package(BaseModel):        title  =

     models.CharField(_("Title"),  max_length="100")        slug  =  models.SlugField(_("Slug"),  help_text="Enter  a  valid  'slug'  consisting  of  letters,  numbers,  underscores  or   hyphens.<br  />Values  will  be  converted  to  lowercase.",  unique=True)        category  =  models.ForeignKey(Category,  verbose_name="Installation")        repo_description  =  models.TextField(_("Repo  Description"),  blank=True)        repo_url  =  models.URLField(_("repo  URL"),  help_text=repo_url_help_text,  blank=True,  unique=True,  verify_exists=True)        repo_watchers  =  models.IntegerField(_("repo  watchers"),  default=0)        repo_forks  =  models.IntegerField(_("repo  forks"),  default=0)        repo_commits  =  models.IntegerField(_("repo  commits"),  default=0)        pypi_url  =  models.URLField(_("PyPI  slug"),  help_text=pypi_url_help_text,  blank=True,  default='',  verify_exists=True)        pypi_downloads  =  models.IntegerField(_("Pypi  downloads"),  default=0)        participants  =  models.TextField(_("Participants"),                                                help_text="List  of  collaborats/participants  on  the  project",  blank=True)        usage  =  models.ManyToManyField(User,  blank=True)        created_by  =  models.ForeignKey(User,  blank=True,  null=True,  related_name="creator",  on_delete=models.SET_NULL)        last_modified_by  =  models.ForeignKey(User,  blank=True,  null=True,  related_name="modifier",  on_delete=models.SET_NULL)        @property        def  pypi_version(self):                string_ver_list  =  self.version_set.values_list('number',  flat=True)                if  string_ver_list:                        vers_list  =  [versioner(v)  for  v  in  string_ver_list]                        latest  =  sorted(vers_list)[-­1]                        return  str(latest)                return  ''        @property        def  pypi_name(self):                """  return  the  pypi  name  of  a  package"""                if  not  self.pypi_url.strip():                        return  ""                name  =  self.pypi_url.replace("http://pypi.python.org/pypi/",  "")                if  "/"  in  name:                        return  name[:name.index("/")]                return  name        @property        def  last_updated(self):                try:                        last_commit  =  self.commit_set.latest('commit_date')                        if  last_commit:                                return  last_commit.commit_date                except  ObjectDoesNotExist:                        pass                return  None        @property        def  repo(self):                return  get_repo_for_repo_url(self.repo_url)        @property        def  active_examples(self):                return  self.packageexample_set.filter(active=True)        @property        def  license_latest(self):                try:                        return  self.version_set.latest().license                except  Version.DoesNotExist:                        return  "UNKNOWN"        def  grids(self):                return  (x.grid  for  x  in  self.gridpackage_set.all())        def  repo_name(self):                return  re.sub(self.repo.url_regex,  '',  self.repo_url)        def  participant_list(self):                return  self.participants.split(',')        def  get_usage_count(self):                return  self.usage.count()        def  commits_over_52(self):                now  =  datetime.now()                commits  =  Commit.objects.filter(                        package=self,                        commit_date__gt=now  -­  timedelta(weeks=52),                ).values_list('commit_date',  flat=True)                weeks  =  [0]  *  52                for  cdate  in  commits:                        age_weeks  =  (now  -­  cdate).days  //  7                        if  age_weeks  <  52:                                weeks[age_weeks]  +=  1                return  ','.join(map(str,  reversed(weeks)))        def  fetch_metadata(self,  *args,  **kwargs):                #  Get  the  downloads  from  pypi                if  self.pypi_url.strip()  and  self.pypi_url  !=  "http://pypi.python.org/pypi/":                        total_downloads  =  0                        for  release  in  fetch_releases(self.pypi_name):                                version,  created  =  Version.objects.get_or_create(                                        package=self,                                        number=release.version                                )                                #  add  to  total  downloads                                total_downloads  +=  release.downloads                                #  add  to  versions                                version.downloads  =  release.downloads                                if  hasattr(release,  "upload_time"):                                        version.upload_time  =  release.upload_time                                version.license  =  release.license                                version.hidden  =  release._pypi_hidden                                version.save()                        self.pypi_downloads  =  total_downloads                self.repo.fetch_metadata(self)                signal_fetch_latest_metadata.send(sender=self)                self.save()        def  save(self,  *args,  **kwargs):                if  not  self.repo_description:                        self.repo_description  =  ""                super(Package,  self).save(*args,  **kwargs)        def  fetch_commits(self):                self.repo.fetch_commits(self)        @property        def  last_released(self):                versions  =  self.version_set.exclude(upload_time=None)                if  versions:                        return  versions.latest()                return  None        @property        def  pypi_ancient(self):                release  =  self.last_released                if  release:                        return  release.upload_time  <  datetime.now()  -­  timedelta(365)                return  None        @property        def  no_development(self):                commit_date  =  self.last_updated                if  commit_date  is  not  None:                        return  commit_date  <  datetime.now()  -­  timedelta(365)                return  None        class  Meta:                ordering  =  ['title']                get_latest_by  =  'id'        def  __unicode__(self):                return  self.title        @models.permalink        def  get_absolute_url(self):                return  ("package",  [self.slug])
  42. @pydanny pydanny@cartwheelweb.com pro-tip Use Helper Functions

  43. @pydanny pydanny@cartwheelweb.com Using Helper Functions from  django.db  import  models  

    from  pies  import  utils  as  pie_utils     class  Pie(models.Model):          #  snip  all  the  attributes          def  is_savory(self):                return  pie_utils.is_savory(self.filling)          @property        def  cooking_time                return  pie_utils.cooking_time(self.filling)
  44. @pydanny pydanny@cartwheelweb.com Django’s API Conventions Encourage clean design

  45. @pydanny pydanny@cartwheelweb.com Content separation Presentation

  46. @pydanny pydanny@cartwheelweb.com Means We Don’t Fight So Much over Architecture

  47. @pydanny pydanny@cartwheelweb.com Get ings Done

  48. Reason #4 Django has Awesome Views

  49. @pydanny pydanny@cartwheelweb.com Django Views are Functions HTTP Request HTTP Response

    Django View Function HttpResponse  =  view(HttpRequest()) Django is part of a group of Python frameworks that emerged around 2005 that embodied the readily-understood concept that HTTP requests could be handled via functions.
  50. @pydanny pydanny@cartwheelweb.com @login_required def  update_package(request,  slug):        

     package  =  get_object_or_404(Package,  slug=slug)        package.fetch_metadata()        package.fetch_commits()        msg  =  "Package  updated  successfully"        messages.add_message(request,  messages.INFO,  msg)          return  HttpResponseRedirect(        reverse("package",  kwargs={"slug":  package.slug}) ) Django Views are Functions #  Do  whatever  needs  to  be  done! #  Do  whatever  needs  to  be  done! #  Do  whatever  needs  to  be  done!
  51. @pydanny pydanny@cartwheelweb.com Django Views are Functions HTTP Request HTTP Response

    Django View Function HttpResponse  =  view(HttpRequest())
  52. @pydanny pydanny@cartwheelweb.com What about Class-Based Views?

  53. @pydanny pydanny@cartwheelweb.com View.as_view() What the heck does this mean?

  54. @pydanny pydanny@cartwheelweb.com class  View(object):        @classonlymethod    

       def  as_view(cls,  **initkwargs):                """                Main  entry  point  for  a  request-­response  process.                """                #  sanitize  keyword  arguments                for  key  in  initkwargs:                        if  key  in  cls.http_method_names:                                raise  TypeError("You  tried  to  pass  in  the  %s  method  name  as  a  "                                                                "keyword  argument  to  %s().  Don't  do  that."                                                                %  (key,  cls.__name__))                        if  not  hasattr(cls,  key):                                raise  TypeError("%s()  received  an  invalid  keyword  %r.  as_view  "                                                                "only  accepts  arguments  that  are  already  "                                                                "attributes  of  the  class."  %  (cls.__name__,  key))                def  view(request,  *args,  **kwargs):                        self  =  cls(**initkwargs)                        if  hasattr(self,  'get')  and  not  hasattr(self,  'head'):                                self.head  =  self.get                        self.request  =  request                        self.args  =  args                        self.kwargs  =  kwargs                        return  self.dispatch(request,  *args,  **kwargs)                #  take  name  and  docstring  from  class                update_wrapper(view,  cls,  updated=())                #  and  possible  attributes  set  by  decorators                #  like  csrf_exempt  from  dispatch                update_wrapper(view,  cls.dispatch,  assigned=())                return  view Returns a callable instance of the view Keyword control code For Python’s introspection tools
  55. @pydanny pydanny@cartwheelweb.com HttpResponse  =   View.as_view(HttpRequest())

  56. @pydanny pydanny@cartwheelweb.com Django Views are Functions HTTP Request HTTP Response

    Django View Class HttpResponse  =   View.as_view(HttpRequest())
  57. Reason #5 Django has Awesome Longevity

  58. @pydanny pydanny@cartwheelweb.com Slow Deprecation Stable APIs make frameworks much nicer.

    They don’t just add longevity, they keep users happy.
  59. @pydanny pydanny@cartwheelweb.com from  django.db  import  models     class  Pie(models.Model):

             STATUS_UNBAKED,  STATUS_BAKED  =  0,  1        FILLING_APPLE,  FILLING_MEAT  =  0,  1        STATUS_CHOICES  =  (                (STATUS_UNBAKED,  "Unbaked"),                (STATUS_BAKED,  "Baked")        )        FILLING_CHOICES  =  (                (FILLING_APPLE,  "Apple"),                (FILLING_MEAT,  "Meat"),        )          status  =  models.IntegerField(choices=STATUS_CHOICES,                                                  default=STATUS_UNBAKED)        filling  =  models.IntegerField(choices=FILLING_CHOICES)          def  __unicode__(self):                return  "{status}  {filling}  pie".format(                        status=self.get_status_display(),                        filling=self.get_filling_display()                ) Django 1.5 Django 1.4 Django 1.3 Django 1.2 Django 1.1 Django 1.0 Much of the same code works across years of Django. Backporting Django is easy, it’s Python that’s changed!
  60. Reason #6 Django has Awesome Features

  61. @pydanny pydanny@cartwheelweb.com Django Features Admin Unicode XSS protection GeoDjango File

    Storage Prefetch related Aggregates Proxy Models Deferred Fields URL Namespaces New User Model Python 3 Multi-DB Model validation CSRF protection Object-level perms Email backends Natural Keys Template Caching Class Based Views Logging Static les Save Subsets Streaming Response Tests Con gurable on_delete TemplateResponse RequestFactory assertNumQueries Time zones Select for Update Password Hashing Signed Cookies WSGI Documented Caching
  62. @pydanny pydanny@cartwheelweb.com Admin Unicode XSS protection GeoDjango File Storage Prefetch

    related Aggregates Proxy Models Deferred Fields URL Namespaces New User Model Python 3 Multi-DB Model validation CSRF protection Object-level perms Email backends Natural Keys Template Caching Class Based Views Logging Static les Save Subsets Streaming Response Tests Con gurable on_delete TemplateResponse RequestFactory assertNumQueries Time zones Select for Update Password Hashing Signed Cookies WSGI Documented Caching Django Features
  63. @pydanny pydanny@cartwheelweb.com Django’s Admin is Awesome

  64. @pydanny pydanny@cartwheelweb.com django.contrib.admin a.k.a. How to sell Django 101

  65. @pydanny pydanny@cartwheelweb.com admin.py from  django.contrib  import  admin   from  pies.models

     import  Pie   admin.site.register(Pie) Shortest Admin module possible. Remember it. Sweet, elegant, API awesomeness. What’s even better is there is a growing list of Django admin skins and themes being developed. If you are working on this sort of thing, keep it up please!
  66. @pydanny pydanny@cartwheelweb.com Pro-Tip django.contrib.admin + NoSQL

  67. @pydanny pydanny@cartwheelweb.com Don’t Extend django.contrib.admin

  68. @pydanny pydanny@cartwheelweb.com Write it from scratch. Easier Stable

  69. @pydanny pydanny@cartwheelweb.com MongoDB- avored #  myapp/mongoadmin.py   #  Import  the

     MongoAdmin  base  class from  mongonaut.sites  import  MongoAdmin   #  Import  your  custom  models from  blog.models  import  Post   #  Instantiate  the  MongoAdmin  class #  Then  attach  the  mongoadmin  to  your  model Post.mongoadmin  =  MongoAdmin() https://github.com/pydanny/django-mongonaut
  70. @pydanny pydanny@cartwheelweb.com github.com/pydanny/django-mongonaut

  71. @pydanny pydanny@cartwheelweb.com admin2 #  myapp/admin2.py   #  Import  the  Admin2

     base  class from  admin2.sites  import  Admin2   #  Import  your  custom  models from  blog.models  import  Post   #  Instantiate  the  Admin2  class #  Then  attach  the  admin2  to  your  model Post.admin2  =  Admin2() https://github.com/pydanny/django-mongonaut
  72. @pydanny pydanny@cartwheelweb.com github.com/pydanny/django-admin2

  73. @pydanny pydanny@cartwheelweb.com Admin Unicode XSS protection GeoDjango File Storage Prefetch

    related Aggregates Proxy Models Deferred Fields URL Namespaces New User Model Python 3 Multi-DB Model validation CSRF protection Object-level perms Email backends Natural Keys Template Caching Class Based Views Logging Static les Save Subsets Streaming Response Tests Con gurable on_delete TemplateResponse RequestFactory assertNumQueries Time zones Select for Update Password Hashing Signed Cookies WSGI Documented Caching Django Features Okay then, back to features.
  74. @pydanny pydanny@cartwheelweb.com Admin Unicode XSS protection GeoDjango File Storage Prefetch

    related Aggregates Proxy Models Deferred Fields URL Namespaces New User Model Python 3 Multi-DB Model validation CSRF protection Object-level perms Email backends Natural Keys Template Caching Class Based Views Logging Static les Save Subsets Streaming Response Tests Con gurable on_delete TemplateResponse RequestFactory assertNumQueries Time zones Select for Update Password Hashing Signed Cookies WSGI Documented Caching Django Features There’s a lot. So many that we just barely touch them in our book.
  75. @pydanny pydanny@cartwheelweb.com Next reason...

  76. @pydanny pydanny@cartwheelweb.com Reason #7 Django’s Full Stack is Awesome a.k.a.

    The section where the micro-framework fans go nuts. Seriously, hear me out.
  77. @pydanny pydanny@cartwheelweb.com Dominating Hackathons * according to pydanny Not having

    to worry about wifi. Documentation in one place.
  78. @pydanny pydanny@cartwheelweb.com Building Companies * according to pydanny

  79. @pydanny pydanny@cartwheelweb.com Today in Django Real projects being done in

    unextended Django & Python Standard Python/Django Libraries No concept of the larger Python/Django World
  80. @pydanny pydanny@cartwheelweb.com Really.

  81. @pydanny pydanny@cartwheelweb.com Late 2012 Los Angeles based eco-service company US$5,000,000

    funding
  82. @pydanny pydanny@cartwheelweb.com No ird-Party Packages

  83. @pydanny pydanny@cartwheelweb.com Lots of Pain

  84. @pydanny pydanny@cartwheelweb.com Quite Pro table

  85. @pydanny pydanny@cartwheelweb.com oughts People building stuff with new tools under

    tight deadlines...
  86. @pydanny pydanny@cartwheelweb.com oughts Don’t always have time to investigate the

    community.
  87. @pydanny pydanny@cartwheelweb.com Django’s “ Full Stack” is a Win

  88. @pydanny pydanny@cartwheelweb.com On the ip side... They did a lot

    of work they didn’t need to do. What’s the solution?
  89. @pydanny pydanny@cartwheelweb.com Reason #8 Django has ousands of Awesome ird-Party

    Packages
  90. @pydanny pydanny@cartwheelweb.com How many Django speci c packages are there?

  91. @pydanny pydanny@cartwheelweb.com >>>  import  requests >>>  from  bs4  import  BeautifulSoup

    >>>  r  =  requests.get('https://pypi.python.org/pypi? %3Aaction=search&term=django&submit=search') >>>  soup  =  BeautifulSoup(r.content) >>>  t  =  soup.find("table",  class_="list") >>>  len(t.find_all('tr')) 4581 How many Django speci c packages are there?
  92. @pydanny pydanny@cartwheelweb.com Django is also Python! PyPI has over 30,000

    Packages! If even 20% are generic enough to be imported into Python projects that’s awesome.
  93. @pydanny pydanny@cartwheelweb.com Reason #9 Django has Awesome Documentation

  94. @pydanny pydanny@cartwheelweb.com Culture of Documentation

  95. @pydanny pydanny@cartwheelweb.com Setting the Bar

  96. @pydanny pydanny@cartwheelweb.com Transcendental Django is Documentation

  97. @pydanny pydanny@cartwheelweb.com Reason #10 Django is Awesomely Humble

  98. @pydanny pydanny@cartwheelweb.com e Django Tradition of Invited Critical Talks •

    2008 - Cal Henderson • 2010 - Eric Florenzano • 2011 - Glyph Lefkowitz, Steve Holden • 2012 - Kenneth Reitz, Justin Holmes At least this many talks, possibly more. Some were incredible, some were not.
  99. @pydanny pydanny@cartwheelweb.com Shaped the Code Base

  100. @pydanny pydanny@cartwheelweb.com Shaped the Core Team

  101. @pydanny pydanny@cartwheelweb.com Shaped the Community

  102. @pydanny pydanny@cartwheelweb.com Shaped Me I learned that inviting criticism of

    your work makes a difference. TSD is awesome because we ask people to be honest.
  103. @pydanny pydanny@cartwheelweb.com Reason #11 Django has an Awesome Community

  104. @pydanny pydanny@cartwheelweb.com For starters... “Django has brought more joy to

    my life than I thought was possible with a web framework.”
  105. @pydanny pydanny@cartwheelweb.com e Awesome Generosity of Django’s Community

  106. @pydanny pydanny@cartwheelweb.com Django’s Unwritten Rule of Generosity “ e more

    you help people in the Django/Python community, the more the community helps you.”
  107. @pydanny pydanny@cartwheelweb.com Case Study

  108. @pydanny pydanny@cartwheelweb.com http://2scoops.org

  109. @pydanny pydanny@cartwheelweb.com We didn’t do it alone. •9 General Technical

    Reviewers •3 Chapter Reviewers •116 Reader Contributors •2 Typesetting Experts While we stand on the shoulders of giants, over 125 people contributed directly to this book.
  110. @pydanny pydanny@cartwheelweb.com Gave the PDF of the book to developers

    in need who emailed us. This was especially helpful for developers in nations outside the normal monetary exchange.
  111. @pydanny pydanny@cartwheelweb.com In return we asked:

  112. @pydanny pydanny@cartwheelweb.com 1. Buy the book when possible.

  113. @pydanny pydanny@cartwheelweb.com 2. Give to Charity.

  114. @pydanny pydanny@cartwheelweb.com 3. Do something nice for someone.

  115. @pydanny pydanny@cartwheelweb.com What happened?

  116. “As music is my main skill I have mentored a

    young student for free every Friday for an hour.” Ethan Lowman Instruction was a pattern... starting with outside skills
  117. “I added Python and Django to the technical curriculum where

    I teach.” Herman Gonzales, Christopher Ayode, Pratyush Mittal Education was also a theme...
  118. “I contributed to open source.” Jay, Nadia, Sam, Alex, Agustin,

    Guillaume, Paulo, Pedro, Roberto, Isaac, Nima, Eric, Hamid, Joe, Glan, Marc, Mark, Jared, Karl, Mohammed, Akshay, Ethan, Robert, Trung, Ivan, Cesar, Piotr, Andrii, Jin, June, Lucas, Samantha, Dan, Kenneth, Chris, Katherine, omas, Yoko, Gabriel, Keith, Renee, Edgar, Jamal, Christine, Alexandra, Eric, Steve, Carl, Mario, Francis, Antonio, Kapoor, Indra, David, Clay, Earnest, Pocholo, Matt, Daniela, Charles, Ananta, Vadim, Rashid, Jing, Tony, Neil, Mathieu, Cecilia, Aubrey, John, Cora,Fabio, Michael, Michelle, Alec, Sara, Sean, Ian, Bruce, Herman, Arne, Adam, Jeff, Allison, Ruth, Katy A lot of people contributed to open source efforts.
  119. “I was able to donate $15 to Python 3 support

    in PyPy.” Juraj Bubniak Some people donated money.
  120. “I bought a homeless man dinner.” Alex Wochna Others supported

    causes like feeding the hungry.
  121. “I built a project for my church/mosque/temple/school.” Radic, Jaden, Luc,

    Wolfgang, Tobias, Richard, Ruth Lars, Jun, Bartek, Alex, Tyler, Pierre, Andreas, Julian ...working on worthy projects...
  122. “I helped develop evidenceupload.org, which was used to send 2

    GB of data to the FBI after the Boston Bombings.” Keith Donaldson ... and participating in current events.
  123. @pydanny pydanny@cartwheelweb.com People Took Our eme and Did Good Deeds.

  124. @pydanny pydanny@cartwheelweb.com ey were Awesome.

  125. @pydanny pydanny@cartwheelweb.com A Call to Action

  126. @pydanny pydanny@cartwheelweb.com Be Awesome. Change the world. That means make

    code, run events, write books. Do things that work, and make the world better.
  127. @pydanny pydanny@cartwheelweb.com One More ing... Cartwheel here

  128. @pydanny pydanny@cartwheelweb.com Finis