-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
790 lines (790 loc) · 32.7 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Deploying Django Projects as Python Packages</title>
<meta name="description" content="Python has a powerful, versatile and fast packaging toolchain consisting of setuptools, pip and wheel. Why not use this toolchain to package and deploy Django projects instead of pushing complete Git repositories to servers?">
<meta name="author" content="Markus Zapke-Gründemann">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui">
<link rel="stylesheet" href="css/reveal.css">
<link rel="stylesheet" href="css/theme/black.css" id="theme">
<link rel="stylesheet" href="css/font-awesome.min.css">
<link rel="stylesheet" href="css/custom.css">
<!-- Code syntax highlighting -->
<link rel="stylesheet" href="lib/css/zenburn.css">
<!-- Printing and PDF exports -->
<script>
var link = document.createElement( 'link' );
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = window.location.search.match( /print-pdf/gi ) ? 'css/print/pdf.css' : 'css/print/paper.css';
document.getElementsByTagName( 'head' )[0].appendChild( link );
</script>
<!--[if lt IE 9]>
<script src="lib/js/html5shiv.js"></script>
<![endif]-->
</head>
<body>
<div class="reveal">
<!-- Any section element inside of this container is displayed as a slide -->
<div class="slides">
<section>
<h2>Deploying Django Projects<br>as Python Packages</h2>
<p>Leipzig Python User Group 🐍</p>
<p>Markus Zapke-Gründemann</p>
</section>
<section>
<h2>About me 👋</h2>
<ul>
<p class="fragment">Started creating websites at the of end the 90's</p>
<p class="fragment">Open Source Software Developer</p>
<p class="fragment">Board member of the <a hef="http://django-de.org/">German Django Association</a></p>
<p class="fragment">Active <a href="http://djangogirls.org/">Django Girls</a> supporter</p>
<p class="fragment">Lab Lead of the <a href"http://codefor.de/leipzig/">Open Knowledge Lab in Leipzig</a></p>
<p class="fragment">Owner of <a href="http://www.transcode.de/">transcode</a></p></p>
</ul>
<aside data-markdown class="notes">
<script type="text/template">
<small>
* I started creating websites because I'm fascinated by the new communication possibilities the internet provides.
* I used Perl, PHP and Python (in that order).
* Because I never had the opportunity to visit a university I learned everything I know from Open Source.
* This is one of the main reasons why I am participating in Open Source: To give something back and grow the community.
* The German Django Association was founded to organize the first DjangoCon Europe community edition in Berlin 2010.
* The Open Knowledge Lab is a part of Code for Germany, which is inspired by Code for America.
* transcode offers Python, Django and Ember.js software development as well as professional Django training.
</small>
</script>
</aside>
</section>
<section>
<h2>Schedule 🕝</h2>
<ol>
<li>Analysing the main parts of a Django project 🔍</li>
<li>Reasons to create a Django project distribution 💯</li>
<li>How to create a Django project distribution 📦</li>
<li>How to deploy on Heroku 🚢</li>
<li>Time for discussions 💬</li>
</ol>
<aside data-markdown class="notes">
<script type="text/template">
<small>
* Questions to ask the audience:
* How many of you have created a Python package?
* How many of you have published a Python package?
* How many of you did this with a Django project?
* How many of you deployed this package?
</small>
</script>
</aside>
</section>
<section>
<section>
<h2>Analysing the main parts<br>of a Django project 🔍</h2>
</section>
<section>
<h2>Definitions</h2>
<dl>
<dt><code>models.py</code></dt>
<dd>Python module (a single file)</dd>
<dt><code>myapp/__init__.py</code></dt>
<dd>Python package (a file in a directory)</dd>
<dt><code>myproject-1.0.0-py2.py3-none-any.whl</code></dt>
<dt><code>myproject-1.0.0.tar.gz</code></dt>
<dd>Python distributions</dd>
</dl>
<aside data-markdown class="notes">
<script type="text/template">
<small>
* Before you can start I have to define a few terms.
* Of course a Python module can be part of a Python package.
* And Python modules and packages are part of a Python distribution.
* Even if the title of the workshop says "Deploying Django Projects as Python Packages" you are actually deploying a Python distribution.
</small>
</script>
</aside>
</section>
<section>
<pre>
<code data-trim data-noescape class="tall bash">
myproject # project directory
├── .git
├── manage.py # sets DJANGO_SETTINGS_MODULE to myproject.settings
| # runs django.core.management.execute_from_command_line()
├── myapp
│ ├── __init__.py # myapp is a Python package
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ │ └── __init__.py # myapp.migrations are a Python package, too
│ ├── models.py
│ ├── tests.py
│ └── views.py
└── myproject
├── __init__.py # myproject is a Python package, too
├── settings.py # has myapp.apps.MyappConfig in INSTALLED_APPS
├── urls.py
└── wsgi.py # sets DJANGO_SETTINGS_MODULE to myproject.settings
# runs django.core.wsgi.get_wsgi_application
</code>
</pre>
<aside data-markdown class="notes">
<script type="text/template">
<small>
* This is the default structure you get after you executed the <code>startproject</code> and <code>startapp</code> commands.
* Because you need to keep the code in a repository <code>git init</code> has also been executed.
* You will recognize that you already have two Python packages at the top level.
* Furthermore you can see that <code>manage.py</code>, <code>settings.py</code> and <code>wsgi.py</code> refer to apps and settings as Python paths.
* These are all important observations that will help you to build a Python distribution for your Django project.
</small>
</script>
</aside>
</section>
</section>
<section>
<section>
<h2>Reasons to create a<br>Django project distribution 💯</h2>
<p class="fragment"><code>pip</code> and <code>setuptools</code> have evolved quite a lot</p>
<p class="fragment"><code>wheels</code> makes installations faster and easier</p>
<p class="fragment"><code>MANIFEST.in</code> controls which files are distributed</p>
<p class="fragment">Assets or GNU gettext <code>.mo</code> files can be compiled locally</p>
<p class="fragment">Distribution is easy</p>
<p class="fragment">Dependencies can be defined in <code>setup.py</code></p>
<p class="fragment">It's also possible to define optional dependencies</p>
<p class="fragment"><code>tox</code> can be used to test if your distribution installs correctly</p>
<aside data-markdown class="notes">
<script type="text/template">
<small>
* Distribution and installation
* Because <code>pip</code> automatically builds and caches wheels installations are very fast.
* Even if you do not install the Python distribution using <code>pip</code> you can still use it to ship code as an archive.
</small>
</script>
</aside>
</section>
</section>
<section>
<section>
<h2>How to create a<br>Django project distribution 📦</h2>
<h4>And pitfalls to avoid <sub>🕳</sub>🚶</h4>
</section>
<section>
<h3>Rename the inner <code>myproject</code> directory</h3>
<pre>
<code data-trim data-noescape class="tall bash">
myproject
├── .git
├── manage.py # 2. Change myproject.settings to config.settings
├── myapp
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
└── myproject # 1. Rename to config
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py # 3. Change myproject.settings to config.settings
</code>
</pre>
<aside data-markdown class="notes">
<script type="text/template">
<small>
* So let's start with transforming your Django project into a Python distribution.
* The first thing you have to do is to rename the <code>myproject</code> Python package to <code>config</code>.
* After that change <code>myproject.settings</code> to <code>config.settings</code> in <code>manage.py</code> and <code>wsgi.py</code>.
</small>
</script>
</aside>
</section>
<section>
<h3>Create a <code>setup.py</code> file</h3>
<pre>
<code data-trim data-noescape class="python tall">
from setuptools import find_packages, setup
setup(
name='myproject',
version='1.0.0',
description='Lorem ipsum dolor sit amet.',
long_description='Consectetur adipisicing elit, sed do tempor.',
author='Ada Lovelace',
author_email='[email protected]',
include_package_data=True,
install_requires=[],
license='Other/Proprietary License',
packages=find_packages(exclude=['tests*']),
zip_safe=False,
classifiers=[
'Framework :: Django',
'Intended Audience :: Developers',
'License :: Other/Proprietary License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
],
)
</code>
</pre>
<aside data-markdown class="notes">
<script type="text/template">
<small>
* Create a file named <code>setup.py</code> in your repository root.
* In this file you have to call the <code>setup()</code> function from the <code>setuptools</code> package.
* <code>include_package_data=True</code> tells setuptools to automatically include any data files that are specified by your <code>MANIFEST.in</code> file.
* You can leave <code>install_requires</code> empty for now.
* Set <code>license</code> to whatever license fits your needs - but if you use an Open Source license stick to ones that are OSI approved.
* <code>find_packages()</code> will search for all packages to include in the distribution and exclude any tests.
* A full list of trove classifiers is available on PyPI.
</small>
</script>
</aside>
</section>
<section>
<h3>Current project structure</h3>
<pre>
<code data-trim data-noescape class="tall bash">
myproject
├── .git
├── config # Was renamed from myproject to config
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── manage.py
├── myapp
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
└── setup.py # Was added in the previous step
</code>
</pre>
<aside data-markdown class="notes">
<script type="text/template">
<small>
* This is the project structure you have after adding <code>setup.py</code>.
* But all code for a Python distribution must be in a single directory.
* At the moment you have two Python packages in your repository root.
* So let's reorganize the project structure.
</small>
</script>
</aside>
</section>
<section>
<h3>New project structure</h3>
<pre>
<code data-trim data-noescape class="tall bash">
myproject
├── .git
├── manage.py # 1. Modify DJANGO_SETTINGS_MODULE
| # Example: myproject.config.settings
├── myproject # 2. Create a new directory
│ ├── __init__.py # 3. Create a new empty file
│ ├── apps # 4. Create a new directory
│ │ ├── __init__.py # 5. Create a new empty file
│ │ ├── myapp # 6. Move myapp into myproject/apps
│ │ │ ├── __init__.py
│ │ │ ├── apps.py
│ │ │ └── ...
│ │ └── myotherapp # Just another example app ;)
│ │ ├── __init__.py
│ │ └── ...
│ └── config # 7. Move config into myproject
│ ├── __init__.py
│ ├── settings.py # 8. Modify INSTALLED_APPS - example:
│ │ # myproject.apps.myapp.apps.MyappConfig
│ │ # Modify ROOT_URLCONF, WSGI_APPLICATION
│ ├── urls.py # 9. Modify include() calls
│ └── wsgi.py # 10. Modify DJANGO_SETTINGS_MODULE
└── setup.py
</code>
</pre>
<aside data-markdown class="notes">
<script type="text/template">
<small>
* This is the new project structure that organizes all code in a single Python package.
* This package will be collected by the <code>find_packages()</code> function in <code>setup.py</code>.
* Make sure you replace all imports pointing at your apps files.
* Also exchange the Python paths in <code>manage.py</code>, <code>settings.py</code>, <code>urls.py</code> and <code>wsgi.py</code> with the new paths.
</small>
</script>
</aside>
</section>
<section>
<h3>Define the dependencies<br>in <code>setup.py</code></h3>
<pre>
<code data-trim data-noescape class="tall bash">
requires = [
'Django==1.8.8',
'django-braces==1.8.1',
'django-crispy-forms==1.5.2',
'psycopg2==2.6.1',
'pytz==2015.7',
]
setup(
name='myproject',
version='1.0.0',
description='Lorem ipsum dolor sit amet.',
long_description='Consectetur adipisicing elit, sed do tempor.',
author='Ada Lovelace',
author_email='[email protected]',
include_package_data=True,
install_requires=requires, # Replaced [] with requires variable
...
)
</code>
</pre>
<aside data-markdown class="notes">
<script type="text/template">
<small>
* Replace the list for the <code>install_requires</code> argument with the new <code>requires</code> variable.
* You can pin every dependency to a specific version, like in <code>requirements.txt</code>.
* You can also add the dependencies of your dependencies, if you really want to. But I'll later show a different solution for this.
</small>
</script>
</aside>
</section>
<section>
<h3>Define the optional dependencies<br>in <code>setup.py</code></h3>
<pre>
<code data-trim data-noescape class="tall bash">
extras_require = {
'django_storages': [
'boto==2.39.0',
'django-storages-redux==1.3.2',
],
'gunicorn': [
'gunicorn==19.4.5',
],
'whitenoise': [
'whitenoise==2.0.6',
],
}
setup(
name='myproject',
version='1.0.0',
...
include_package_data=True,
install_requires=requires,
extras_require=extras_require, # This line is new
...
)
</code>
</pre>
<aside data-markdown class="notes">
<script type="text/template">
<small>
* You can define optional dependencies in a way similar to how you defined your dependencies before.
* But optional dependencies can be defined in groups.
* Be aware that you can't use a hyphen in the key, that is why <code>django_storages</code> is used.
</small>
</script>
</aside>
</section>
<section>
<h3>How to install optional dependencies?</h3>
<pre>
<code data-trim data-noescape class="tall bash">
$ pip install myproject[gunicorn,whitenoise]==1.0.0
</code>
</pre>
<aside data-markdown class="notes">
<script type="text/template">
<small>
* I think this way is superior to having to deal with multiple requirements files for deployment.
* But we still use requirements files to install development and testing packages.
</small>
</script>
</aside>
</section>
<section>
<h3>Create directories for<br>media, static files and templates</h3>
<pre>
<code data-trim data-noescape class="tall bash">
myproject
├── .git
├── manage.py
├── myproject
│ ├── __init__.py
│ ├── apps
│ ├── config
│ ├── media # Use this path for your MEDIA_ROOT setting
│ │ └── .gitkeep
│ ├── static # Add this path to your STATICFILES_DIRS setting
│ │ └── .gitkeep
│ ├── static_root # Use this path for your STATIC_ROOT setting
│ │ └── .gitkeep
│ └── templates # Add this path to your DIRS setting in TEMPLATES
│ ├── 404.html
│ └── base.html
└── setup.py
</code>
</pre>
<aside data-markdown class="notes">
<script type="text/template">
<small>
* So of course you want directories for media, static files and template.
* Create them like inside the <code>myproject</code> directory so that they can become a part of the distribution.
</small>
</script>
</aside>
</section>
<section>
<h3>What about <code>manage.py</code>?</h3>
<pre>
<code data-trim data-noescape class="tall bash">
entry_points = {
'console_scripts': [
'site-admin = configurations.management:execute_from_command_line',
]
}
setup(
name='myproject',
version='1.0.0',
...
include_package_data=True,
install_requires=requires,
extras_require=extras_require,
entry_points=entry_points, # This line is new
...
)
</code>
</pre>
<aside data-markdown class="notes">
<script type="text/template">
<small>
* As explained at the beginning <code>manage.py</code> just sets <code>DJANGO_SETTINGS_MODULE</code> and runs <code>django.core.management.execute_from_command_line()</code>.
* This means we do not really need it, it's just there for convenience.
* So you can either simply call <code>django-admin</code> or use <code>entry_points</code> instead. This is important if you use a different <code>execute_from_command_line()</code> function, like the one from django-configurations.
* So you simply call <code>site-admin</code> instead of <code>django-admin</code> in this case.
</small>
</script>
</aside>
</section>
<section>
<h3>Create a <code>MANIFEST.in</code> file</h3>
<pre>
<code data-trim data-noescape class="tall bash">
include *.rst # For files like CONTRIBUTING.rst or README.rst
include LICENSE
include manage.py
include MANIFEST.in
include package.json
include tox.ini
recursive-include requirements *.pip
graft heroku # Contains files for a Heroku deployment
graft myproject
prune myproject/media # Don't include any media files
include myproject/media/.gitkeep # But just add the empty directory
prune myproject/static_root
graft tests
exclude .editorconfig
global-exclude *.py[co]
global-exclude __pycache__
</code>
</pre>
<aside data-markdown class="notes">
<script type="text/template">
<small>
* You need the <code>MANIFEST.in</code> file to configure which files are included in the distribution.
* Create it in the same directory as <code>setup.py</code>.
</small>
</script>
</aside>
</section>
<section>
<h3>Configure the wheel to be universal<br>in <code>setup.cfg</code></h3>
<pre>
<code data-trim data-noescape class="tall bash">
[wheel]
universal = 1
</code>
</pre>
<aside data-markdown class="notes">
<script type="text/template">
<small>
* Create the file in the same directory as <code>setup.py</code>.
* If you do not configure the wheel to be universal you will have to create different ones for each Python version.
</small>
</script>
</aside>
</section>
<section>
<h3>Current project structure</h3>
<pre>
<code data-trim data-noescape class="tall bash">
myproject
├── .git
├── manage.py
├── myproject
│ ├── __init__.py
│ ├── apps
│ ├── config
│ ├── media
│ ├── static
│ ├── static_root
│ └── templates
├── MANIFEST.in
├── setup.cfg
└── setup.py
</code>
</pre>
<aside data-markdown class="notes">
<script type="text/template">
<small>
* This is how your main project structure should look like now.
</small>
</script>
</aside>
</section>
<section>
<h3>How to set up tests?</h3>
<pre>
<code data-trim data-noescape class="tall bash">
myproject
├── .git
├── manage.py
├── myproject
│ ├── __init__.py
│ ├── apps
│ ├── config
│ ├── media
│ ├── static
│ ├── static_root
│ └── templates
├── tests
│ ├── __init__.py
│ └── myapp
│ ├── __init__.py
│ └── test_models.py # from myproject.apps.myapp import models
├── MANIFEST.in
├── setup.cfg
└── setup.py
</code>
</pre>
<aside data-markdown class="notes">
<script type="text/template">
<small>
* Create a new directory for your tests called <code>tests</code> in the repository root and put a <code>__init__.py</code> in it.
* Then put the tests into subdirectories grouping them by app or topic.
* This is the same way Django itself organizes it test suite.
* Make sure you always import code from your project with the full Python path. Both Python packages are completely separate!
</small>
</script>
</aside>
</section>
<section>
<h3>Where to put additional code?</h3>
<pre>
<code data-trim data-noescape class="tall bash">
myproject
├── .git
├── manage.py
├── myproject
│ ├── __init__.py
│ ├── context_processors.py
| | # Usage: from myproject.context_processors import func
│ ├── apps
│ ├── config
│ ├── media
│ ├── static
│ ├── static_root
│ └── templates
├── tests
├── MANIFEST.in
├── setup.cfg
└── setup.py
</code>
</pre>
<aside data-markdown class="notes">
<script type="text/template">
<small>
* Because you have now a project structure that is completely namespaced it should be easier to figure our where to put additional code.
</small>
</script>
</aside>
</section>
</section>
<section>
<section>
<h2>How to deploy on Heroku 🚢</h2>
</section>
<section>
<h3>Install heroku-slugify</h3>
<pre>
<code data-trim data-noescape class="tall bash">
$ npm install heroku-slugify --global
</code>
</pre>
</section>
<section>
<h3>Build your distribution</h3>
<pre>
<code data-trim data-noescape class="tall bash">
$ python setup.py sdist bdist_wheel
$ ls -1 dist
myproject-1.0.0-py2.py3-none-any.whl
myproject-1.0.0.tar.gz
</code>
</pre>
</section>
<section>
<h3>Upload your distribution</h3>
<p>
<a href="https://packagecloud.io/">packagecloud.io</a><br>
<small>Hosted repos for Deb, RPM, Python and RubyGem Packages</small>
</p>
<aside data-markdown class="notes">
<script type="text/template">
<small>
* If you have private projects or don't want to upload to PyPI you can use packagecloud.
* They got a nice Ruby Gem you can use to upload your distribution.
</small>
</script>
</aside>
</section>
<section>
<h3>Create the files required for Heroku</h3>
<pre>
<code data-trim data-noescape class="tall bash">
myproject
├── manage.py
└── heroku
├── Procfile # The application server to use
| # web: gunicorn myproject.config.wsgi
├── manage.py # A copy of myproject/manage.py
├── requirements.in # Contains something like
| # myproject[gunicorn,whitenoise]==1.0.0
├── requirements.txt # Compiled by pip-tools
├── runtime.txt # Python version to be used
└── static_root
└── .gitkeep
</code>
</pre>
<aside data-markdown class="notes">
<script type="text/template">
<small>
* If you are using packagecloud or another private server you need to add an <code>--extra-index-url</code> option to <code>requirements.in</code>.
* You need the <code>manage.py</code> file and the empty <code>static_root</code> directory, otherwise <code>collectstatic</code> won't work on Heroku.
</small>
</script>
</aside>
</section>
<section>
<h3>How to configure the Django settings?</h3>
<p><a href="http://12factor.net/">The Twelve-Factor App</a></p>
<p>django-configurations</p>
<p>envdir</p>
<aside data-markdown class="notes">
<script type="text/template">
<small>
* Accoring to the The Twelve-Factor App methodology the configuration should be kept in the environment.
* You can use django-configurations to load settings from the environment.
* <code>envdir</code> is tool that helps you with that in your local development environment.
</small>
</script>
</aside>
</section>
<section>
<h3>Deploy</h3>
<pre>
<code data-trim data-noescape class="tall bash">
$ heroku-slugify --app YOUR_HEROKU_APP_ID_HERE --version 1.0.0
</code>
</pre>
</section>
</section>
<section>
<h2>Time for discussions 💬</h2>
</section>
<section>
<h2>Resources 📌</h2>
<small>
<p>
<a href="https://github.com/keimlink/django-package-deployment-workshop">
<i class="fa fa-github"></i>keimlink/django-package-deployment-workshop
</a>
</p>
<p>
<a href="https://github.com/transcode-de/cookiecutter-django-project">
<i class="fa fa-github"></i>transcode-de/cookiecutter-django-project
</a>
</p>
<p>
<a href="https://github.com/keimlink/heroku-slugify">
<i class="fa fa-github"></i>keimlink/heroku-slugify
</a>
</p>
<p>
<a href="https://github.com/nvie/pip-tools">
<i class="fa fa-github"></i>nvie/pip-tools
</a>
</p>
<p>
<a href="https://github.com/jazzband/django-configurations">
<i class="fa fa-github"></i>jazzband/django-configurations
</a>
</p>
<p>
<a href="https://github.com/jezdez/envdir">
<i class="fa fa-github"></i>jezdez/envdir
</a>
</p>
<p>
<a href="https://github.com/mgedmin/check-manifest">
<i class="fa fa-github"></i>mgedmin/check-manifest
</a>
</p>
<p>
<a href="https://github.com/peritus/bumpversion">
<i class="fa fa-github"></i>peritus/bumpversion
</a>
</p>
<p>
<a href="https://twitter.com/keimlink"><i class="fa fa-twitter"></i>@keimlink</a>
</p>
</small>
<p class="creativecommons">
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/">
<img alt="Creative Commons License" src="img/cc-by-88x31.png" />
</a><br>
<span>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.</span>
</p>
</section>
</div>
</div>
<script src="lib/js/head.min.js"></script>
<script src="js/reveal.js"></script>
<script>
// Full list of configuration options available at:
// https://github.com/hakimel/reveal.js#configuration
Reveal.initialize({
controls: true,
progress: true,
history: true,
center: true,
transition: 'slide', // none/fade/slide/convex/concave/zoom
// Optional reveal.js plugins
dependencies: [
{ src: 'lib/js/classList.js', condition: function() { return !document.body.classList; } },
{ src: 'plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
{ src: 'plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
{ src: 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
{ src: 'plugin/zoom-js/zoom.js', async: true },
{ src: 'plugin/notes/notes.js', async: true }
]
});
</script>
</body>
</html>