I am... Igor Davydenko Python & React.js developer From Kyiv, Ukraine Works on Ezhome Inc. Primarly designs & develops backend API Personal Site @ GitHub @ Twitter
My Path Everything started from Microsoft FrontPage, early 2002 Met PHP4, HTML4, early 2003 First school projects, sites, 2003-2005 First work, 2006 Met PHP5, late 2006 Own web design studio, 2007 Switch to Python & Django, late 2007
JavaScript was bad My motto was: Let someone else make frontend Tasks Backbone? Okay, but not in big teams Angular? No, no, no. Just no Vanilla JS? Cool, but not in big teams
JavaScript problems No standart modules < s c r i p t > -hell in templates Hard to maintain across distributed team One developer -> one code style, X developers -> X code styles Hard to maintain between projects Same v e n d o r / directories, no dependencies management
But then... node.js happens npm installs dependencies CommonJS allows reuse your code browserify builds bundles from your code jshint/jslint/jsrc lints your code Even some people starts write backends in node.js
Before In template.html, < s c r i p t s r c = " / p a t h / t o / u n d e r s c o r e . j s " t y p e = " t e x t / j a v a s c r i p t " > < / s c r i p t > < s c r i p t t y p e = " t e x t / j a v a s c r i p t " > _ . e a c h ( d o c u m e n t . g e t E l e m e n t s B y T a g N a m e ( " a " ) , f u n c t i o n ( i t e m ) { i f ( i t e m . h r e f . i n d e x O f ( " h t t p : / / " ) | | i t e m . h r e f . i n d e x O f ( " h t t p s : / / " ) ) { i t e m . h r e f = " / g o ? " + i t e m . h r e f ; i t e m . o n c l i c k = f u n c t i o n ( ) { i t e m . c l a s s L i s t . a d d ( " v i s i t e d - l i n k " ) ; r e t u r n F a l s e ; } } } ) ; . . . < / s c r i p t >
Now In template.html, < s c r i p t s r c = " / p a t h / t o / b u n d l e . j s " t y p e = " t e x t / j a v a s c r i p t " > < / s c r i p t > < s c r i p t t y p e = " t e x t / j a v a s c r i p t " > p r o c e s s E x t e r n a l L i n k s ( d o c u m e n t . g e t E l e m e n t s B y T a g N a m e ( " a " ) ) ; < / s c r i p t >
Now In js/external-links.js, i m p o r t { e a c h } f r o m " u n d e r s c o r e " ; f u n c t i o n h a n d l e E x t e r n a l L i n k C l i c k ( e v t ) { e v t . p r e v e n t D e f a u l t ( ) ; e v t . t a r g e t . c l a s s L i s t . a l l ( " v i s i t e d - l i n k " ) ; } e x p o r t f u n c t i o n p r o c e s s E x t e r n a l L i n k s ( e l e m e n t s ) { e a c h ( e l e m e n t s , i t e m = > { i f ( / ^ h t t p s ? \ : \ / \ / / . t e s t ( i t e m . h r e f ) ) { i t e m . h r e f = " / g o ? " + i t e m . h r e f ; i t e m . a d d E v e n t L i s t e n e r ( " c l i c k " , h a n d l e E x t e r a n l L i n k C l i c k ) ; } } ) ; } e x p o r t d e f a u l t { p r o c e s s E x t e r n a l L i n k s : p r o c e s s E x t e r n a l L i n k s } ;
ES2015. Modules ./lib/myLibrary.js e x p o r t f u n c t i o n m y F u n c t i o n ( ) { . . . } e x p o r t d e f a u l t { m y F u n c t i o n : m y F u n c t i o n } ; ./app.js i m p o r t m y L i b r a r y f r o m " . / l i b / m y L i b r a r y " ; i m p o r t { m y F u n c t i o n } f r o m " . / l i b / m y L i b r a r y " ;
ES2015. Let + Const c o n s t u n c h a n g a b l e = t r u e ; l e t c h a n g e a b l e ; c h a n g e a b l e = t r u e ; u n c h a n g e a b l e = f a l s e ; / / E r r o r
ES2015. Arrows c o n s t e p i s o d e s = [ . . . ] ; c o n s t a N e w H o p e = e p i s o d e s . f i l t e r ( i t e m = > i t e m . e p i s o d e = = = " I V " ) ; c o n s t e p i s o d e T i t l e s = e p i s o d e s . m a p ( i t e m = > { r e t u r n i t e m . t i t l e . t o U p p e r C a s e ( ) ; } )
ES2015. Classes c l a s s A N e w H o p e e x t e n d s E p i s o d e { i d : " I V " , n a m e : " A N e w H o p e " , c o n s t r u c t o r ( ) { s u p e r ( ) ; t h i s . d i r e c t e d B y = " G e o r g e L u c a s " ; } r e n d e r ( ) { . . . } } c o n s t a N e w H o p e = n e w A N e w H o p e ( ) ;
ES2015. Template Strings c o n s t e p i s o d e = ' I V ' ; c o n s t t i t l e = ' A N e w H o p e ' ; ` A l o n g t i m e a g o i n a g a l a x y f a r , f a r a w a y . . . S T A R W A R S E p i s o d e $ { e p i s o d e } $ { t i t l e . t o U p p e r C a s e ( ) } I t i s a p e r i o d o f c i v i l w a r . R e b e l s p a c e s h i p s , s t r i k i n g f r o m a h i d d e n b a s e , h a v e w o n t h e i r f i r s t v i c t o r y a g a i n s t t h e e v i l G a l a c t i c E m p i r e . `
ES2015. Destructing c o n s t [ f i r s t , , t h i r d ] = [ 1 , 2 , 3 , 4 , 5 , 6 ] ; c o n s t { e p i s o d e , t i t l e } = { e p i s o d e : " I V " , t i t l e : " A N e w H o p e " , o p e n i n g : " . . . " } ; c o n s t { e p i s o d e : i d , t i t l e : n a m e } = { . . . } ;
ES2015. Map + Set v a r m a p p i n g = n e w M a p ( ) ; m a p . s e t ( " I V " , " A N e w H o p e " ) ; m a p . g e t ( " I V " ) = = = " A N e w H o p e " ; v a r e p i s o d e s = n e w S e t ( ) ; e p i s o d e s . a d d ( " A N e w H o p e " ) . a d d ( " T h e E m p i r e S t r i k e s B a c k " ) . a d d ( " R e t u r n o f t h e J e d i " ) . a d d ( " A N e w H o p e " ) ; e p i s o d e s . h a s ( " A N e w H o p e " ) ; e p i s o d e s . s i z e = = = 3 ;
React.js. A Simple Component i m p o r t R e a c t , { C o m p o n e n t } f r o m " r e a c t " ; c l a s s E u r o P y t h o n e x t e n d s C o m p o n e n t { r e n d e r ( ) { r e t u r n ( ; } } R e a c t . r e n d e r ( < d i v > H e l l o , E u r o P y t h o n 2 0 1 5 ! < / d i v > ) < E u r o P y t h o n / > , d o c u m e n t . g e t E l e m e n t B y I d ( " r e a c t - c o n t a i n e r " ) ) ;
React.js. A Stateful Component c l a s s E u r o P y t h o n e x t e n d s C o m p o n e n t { s t a t e = { c l i c k s : 0 } , h a n d l e C l i c k = ( ) = > { t h i s . s e t S t a t e ( { c l i c k s : t h i s . s t a t e . c l i c k s + 1 } ) ; } , r e n d e r ( ) { c o n s t { c l i c k s } = t h i s . s t a t e ; r e t u r n ( } } R e a c t . r e n d e r ( < d i v > T h i s b u t t o n c l i c k e d { c l i c k s } t i m e ( s ) . < b r / > < b u t t o n o n C l i c k = { t h i s . h a n d l e C l i c k } > C l i c k M e < / b u t t o n > < / d i v > ) < E u r o P y t h o n / > , d o c u m e n t . g e t E l e m e n t B y I d ( " r e a c t - c o n t a i n e r " ) ) ;
React.js. Using Components i m p o r t M a r k d o w n f r o m " . / c o m p o n e n t s / M a r k d o w n " ; c l a s s C o m m e n t F o r m e x t e n d s C o m p o n e n t { s t a t e = { c o m m e n t : " " , p r e v i e w : f a l s e } , h a n d l e C o m m e n t C h a n g e = ( e v t ) = > { t h i s . s e t S t a t e ( { c o m m e n t : e v t . t a r g e t . v a l u e } ) ; } , h a n d l e P r e v i e w C l i c k = ( e v t ) = > { t h i s . s e t S t a t e ( { p r e v i e w : t r u e } ) ; } , r e n d e r ( ) { c o n s t { c o m m e n t , p r e v i e w } = t h i s . s t a t e ; i f ( p r e v i e w ) r e t u r n r e t u r n ( ; } } < M a r k d o w n > { c o m m e n t } < / M a r k d o w n > ; < d i v > < t e x t a r e a o n C h a n g e = { t h i s . h a n d l e C o m m e n t C h a n g e } p l a c e h o l d e r = " Y o u r C o m m e n t " v a l u e = { c o m m e n t } / > < b u t t o n o n C l i c k = { t h i s . h a n d l e P r e v i e w C l i c k } > P r e v i e w < / b u t t o n > < / d i v > )
React.js. Fetching data c l a s s C o m m e n t s e x t e n d s C o m p o n e n t { s t a t e = { d a t a : [ ] , s t a t u s : n u l l } , c o m p o n e n t D i d M o u n t ( ) { t h i s . l o a d D a t a ( ) ; } , l o a d D a t a = ( ) = > { f e t c h ( a p i U r l ) . t h e n ( r e s p o n s e = > { i f ( r e s p o n s e . o k ( ) ) { r e t u r n P r o m i s e . r e s o l v e ( r e s p o n s e . j s o n ( ) ) ; } r e t u r n P r o m i s e . r e j e c t ( n e w E r r o r ( r e s p o n s e . s t a t u s T e x t | | r e s p o n s e . s t a t u s ) ) ; } ) . t h e n ( j s o n = > { t h i s . s e t S t a t e ( { d a t a : j s o n , s t a t u s : t r u e } ) ; } , ( ) = > { t h i s . s e t S t a t e ( { d a t a : [ ] , s t a t u s : f a l s e } ) ; } ) ) ; } , . . .
React.js. Fetching data . . . r e n d e r ( ) { c o n s t { d a t a , s t a t u s } = t h i s . s t a t e ; i f ( s t a t u s = = = n u l l ) { . . . / / L o a d i n g } e l s e i f ( s t a t u s = = = f a l s e ) { . . . / / S e r v e r e r r o r } e l s e i f ( ! d a t a . l e n g t h ) { . . . / / E m p t y d a t a } e l s e { . . . / / V a l i d d a t a } } }
React.js. One-way data binding c l a s s C o m m e n t s e x t e n d s C o m p o n e n t { . . . r e n d e r ( ) { c o n s t c o n t e n t = t h i s . s t a t e . d a t a . m a p ( i t e m = > ( ) ; } } c l a s s C o m m e n t e x t e n d s C o m p o n e n t { s t a t i c d e f a u l t P r o p s = { d a t a : { } } , p r o p T y p e s = { d a t a : P r o p T y p e s . s h a p e ( { . . . } ) } , r e n d e r ( ) { r e t u r n ( . . . ) } } < C o m m e n t d a t a = { i t e m } k e y = { " c o m m e n t - " + i t e m . i d } / > )
React.js. One-way data binding Comments is Higher Order Component Comment is Dumb Component Higher Order Components Data loading Events handling Dumb Components Set of reusable Components Shareable across one and many projects
And More React DOM is separate package from 0.14 React Native, React Canvas Routing via react-router Reusable Components react-bootstrap react-dnd Flux Relay GraphQL
asyncio Asynchronous I/O, event loop, coroutines, and tasks https://docs.python.org/3/library/asyncio.html i m p o r t a s y n c i o @ a s y n c i o . c o r o u t i n e d e f h e l l o ( ) : r e t u r n " H e l l o , w o r l d ! " l o o p = a s y n c i o . g e t _ e v e n t _ l o o p ( ) c o n t e n t = l o o p . r u n _ u n t i l _ c o m p l e t e ( h e l l o ( ) ) p r i n t ( c o n t e n t ) l o o p . c l o s e ( ) Included in Python 3.4 and later Available in Python 3.3 with $ p i p i n s t a l l a s y n c i o Backported to Python 2.7 as trollius (I don't recommend to use it anyway)
aiohttp HTTP client/server for asyncio Latest version: 0 . 1 6 . 5 http://aiohttp.readthedocs.org/ i m p o r t a s y n c i o i m p o r t a i o h t t p @ a s y n c i o . c o r o u t i n e d e f f e t c h _ p a g e ( u r l ) : r e s p o n s e = y i e l d f r o m a i o h t t p . r e q u e s t ( ' G E T ' , u r l ) a s s e r t r e s p o n s e . s t a t u s = = 2 0 0 r e t u r n ( y i e l d f r o m r e s p o n s e . r e a d ( ) ) l o o p = a s y n c i o . g e t _ e v e n t _ l o o p ( ) c o n t e n t = l o o p . r u n _ u n t i l _ c o m p l e t e ( f e t c h _ p a g e ( ' h t t p : / / e p 2 0 1 5 . e u r o p y t h o n . e u / ' ) ) p r i n t ( c o n t e n t ) l o o p . c l o s e ( )
aiohttp.web Web framework for asyncio http://aiohttp.readthedocs.org/en/v0.16.5/web.html i m p o r t a s y n c i o f r o m a i o h t t p i m p o r t w e b @ a s y n c i o . c o r o u t i n e d e f h e l l o ( r e q u e s t ) : r e t u r n w e b . R e s p o n s e ( b o d y = ' H e l l o , w o r l d ! ' , c o n t e n t _ t y p e = ' t e x t / p l a i n ' ) a p p = w e b . A p p l i c a t i o n ( ) a p p . r o u t e r . a d d _ r o u t e ( ' G E T ' , ' / ' , h e l l o ) $ g u n i c o r n - k a i o h t t p . w o r k e r . G u n i c o r n W e b W o r k e r - w 9 - t 6 0 a p p : a p p
aiopg Accessing PostgreSQL database from the asyncio Latest version: 0 . 7 . 0 http://aiopg.readthedocs.org/ i m p o r t a s y n c i o f r o m a i o p g i m p o r t c r e a t e _ p o o l @ a s y n c i o . c o r o u t i n e d e f h e l l o ( d s n ) : p o o l = y i e l d f r o m c r e a t e _ p o o l ( d s n ) w i t h ( y i e l d f r o m p o o l . c u r s o r ( ) ) a s c u r s o r : y i e l d f r o m c u r s o r . e x e c u t e ( ' S E L E C T 1 ' ) s e l e c t e d = y i e l d f r o m c u r s o r . f e t c h o n e ( ) a s s e r t s e l e c t e d = = ( 1 , ) l o o p = a s y n c i o . g e t _ e v e n t _ l o o p ( ) l o o p . r u n _ u n t i l _ c o m p l e t e ( h e l l o ( ' d b n a m e = a i o p g u s e r = . . . p a s s w o r d = . . . h o s t = . . . ' ) ) l o o p . c l o s e ( )
aioredis / asyncio_redis Accessing Redis datasotre from the asyncio a i o r e d i s from a i o - l i b s Latest version: 0 . 1 . 5 http://aioredis.readthedocs.org/ a s y n c i o _ r e d i s from third-party developers Latest version: 0 . 1 3 . 4 http://asyncio-redis.readthedocs.org/
Architecture All starts from view functions (handlers) View functions should be a coroutine, and return w e b . R e s p o n s e i m p o r t a s y n c i o f r o m a i o h t t p i m p o r t w e b @ a s y n c i o . c o r o u t i n e d e f i n d e x ( r e q u e s t ) : r e t u r n w e b . R e s p o n s e ( b o d y = ' H e l l o , w o r l d ! ' , c o n t e n t _ t y p e = ' t e x t / p l a i n ' ) It's good idea to put all view functions to v i e w s . p y module
Architecture Next you need to create an w e b . A p p l i c a t i o n And register handler for a request f r o m a i o h t t p i m p o r t w e b f r o m . i m p o r t v i e w s a p p = w e b . A p p l i c a t i o n ( ) a p p . r o u t e r . a d d _ r o u t e ( ' G E T ' , ' / ' , v i e w s . i n d e x ) Obvious to put application code to a p p . p y module
Architecture Now you ready to serve your application I recommend to use Gunicorn $ g u n i c o r n - b 0 . 0 . 0 . 0 : 8 0 0 0 - k a i o h t t p . w o r k e r . G u n i c o r n W e b W o r k e r - w 9 - t 6 0 p r o j e c t . a p p : a p p Add - - r e l o a d flag to automatically reload Gunicorn server on code change
Handling GET/POST data In views.py, @ a s y n c i o . c o r o u t i n e d e f s e a r c h ( r e q u e s t ) : " " " S e a r c h b y q u e r y f r o m G E T p a r a m s . " " " q u e r y = r e q u e s t . G E T [ ' q u e r y ' ] l o c a l e = r e q u e s t . G E T . g e t ( ' l o c a l e ' , ' u k _ U A ' ) . . . r e t u r n w e b . R e s p o n s e ( . . . )
Handling GET/POST data In views.py, @ a s y n c i o . c o r o u t i n e d e f s u b m i t ( r e q u e s t ) : " " " S u b m i t f o r m P O S T d a t a . " " " d a t a = y i e l d f r o m r e q u e s t . p o s t ( ) # N o w P O S T d a t a a v a i l a b l e a s ` ` r e q u e s t . P O S T ` ` . . . r e t u r n w e b . R e s p o n s e ( . . . )
Handling GET/POST data In app.py, a p p . r o u t e r . a d d _ r o u t e ( ' G E T ' , ' / s e a r c h ' , v i e w s . s e a r c h ) a p p . r o u t e r . a d d _ r o u t e ( ' P O S T ' , ' / s u b m i t ' , v i e w s . s u b m i t )
Handling variable routes In views.py, @ a s y n c i o . c o r o u t i n e d e f p r o j e c t ( r e q u e s t ) : p r o j e c t _ i d = r e q u e s t . m a t c h _ i n f o [ ' p r o j e c t _ i d ' ] . . . r e t u r n w e b . R e s p o n s e ( . . . )
Handling variable routes In app.py, a p p . r o u t e r . a d d _ r o u t e ( ' G E T ' , ' / p r o j e c t s / { p r o j e c t _ i d } ' , v i e w s . p r o j e c t ) Or even, a p p . r o u t e r . a d d _ r o u t e ( ' G E T ' , ' / p r o j e c t s / { p r o j e c t _ i d : \ d + } ' , v i e w s . p r o j e c t )
Named routes, reverse constructing, and redirect In app.py, a p p . r o u t e r . a d d _ r o u t e ( ' G E T ' , ' / p r o j e c t s ' , v i e w s . p r o j e c t s , n a m e = ' p r o j e c t s ' ) a p p . r o u t e r . a d d _ r o u t e ( ' P O S T ' , ' / p r o j e c t s ' , v i e w s . a d d _ p r o j e c t )
Named routes, reverse constructing, and redirect In views.py, @ a s y n c i o . c o r o u t i n e d e f a d d _ p r o j e c t ( r e q u e s t ) : d a t a = y i e l d f r o m r e q u e s t . p o s t ( ) . . . u r l = r e q u e s t . a p p . r o u t e r [ ' p r o j e c t s ' ] . u r l ( ) r e t u r n w e b . H T T P F o u n d ( u r l ) @ a s y n c i o . c o r o u t i n e d e f p r o j e c t s ( r e q u e s t ) : . . .
You don't need to f r o m a p p i m p o r t a p p Or f r o m f l a s k i m p o r t c u r r e n t _ a p p either Request contains a p p instance for all your needs In app.py, f r o m . i m p o r t s e t t i n g s . . . a p p [ ' s e t t i n g s ' ] = s e t t i n g s
You don't need to f r o m a p p i m p o r t a p p In views.py, @ a s y n c i o . c o r o u t i n e d e f s e a r c h ( r e q u e s t ) : s e t t i n g s = r e q u e s t . a p p [ ' s e t t i n g s ' ] q u e r y = r e q u e s t . G E T [ ' q u e r y ' ] l o c a l e = r e q u e s t . G E T . g e t ( ' l o c a l e ' , s e t t i n g s . D E F A U L T _ L O C A L E ) . . . r e t u r n w e b . R e s p o n s e ( . . . )
Middlewares w e b . A p p l i c a t i o n accepts optional middlewares factories sequence Middleware Factory should be a coroutine and returns a coroutine In app.py, @ a s y n c i o . c o r o u t i n e d e f t r i v i a l _ m i d d l e w a r e ( a p p , h a n d l e r ) : @ a s y n c i o . c o r o u t i n e d e f m i d d l e w a r e ( r e q u e s t ) : r e t u r n ( y i e l d f r o m h a n d l e r ( r e q u e s t ) ) r e t u r n m i d d l e w a r e . . . a p p = w e b . A p p l i c a t i o n ( m i d d l e w a r e s = [ t r i v i a l _ m i d d l e w a r e ] )
Middlewares Ready to use Middlewares User Sessions, a i o h t t p _ s e s s i o n Debug Toolbar, a i o h t t p _ d e b u g t o o l b a r In app.py, i m p o r t a i o h t t p _ d e b u g t o o l b a r f r o m a i o h t t p _ d e b u g t o o l b a r i m p o r t t o o l b a r _ m i d d l e w a r e _ f a c t o r y f r o m a i o h t t p _ s e s s i o n i m p o r t s e s s i o n _ m i d d l e w a r e f r o m a i o h t t p _ s e s s i o n . c o o k i e _ s t o r a g e i m p o r t E n c r y p t e d C o o k i e S t o r a g e a p p = w e b . A p p l i c a t i o n ( m i d d l e w a r e s = [ t o o l b a r _ m i d d l e w a r e _ f a c t o r y , s e s s i o n _ m i d d l e w a r e ( E n c r y p t e d C o o k i e S t o r a g e ( b ' 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 ' ) ) ] ) a i o h t t p _ d e b u g t o o l b a r . s e t u p ( a p p )
Middlewares Handling Exceptions In app.py, @ a s y n c i o . c o r o u t i n e d e f e r r o r h a n d l e r _ m i d d l e w a r e ( a p p , h a n d l e r ) : @ a s y n c i o . c o r o u t i n e d e f m i d d l e w a r e ( r e q u e s t ) : t r y : r e t u r n ( y i e l d f r o m h a n d l e r ( r e q u e s t ) ) e x c e p t w e b . H T T P E r r o r a s e r r : # A s i t s p e c i a l c a s e w e c o u l d p a s s ` ` e r r ` ` a s s e c o n d a r g u m e n t t o # e r r o r h a n d l e r r e t u r n ( y i e l d f r o m v i e w s . e r r o r ( r e q u e s t , e r r ) ) r e t u r n m i d d l e w a r e
User Sessions aiohttp_session Latest version: 0 . 1 . 1 Supports storing session data in encrypted cookie or redis Enabled by passing s e s s i o n _ m i d d l e w a r e to middleware factories sequence In views.py, f r o m a i o h t t p _ s e s s i o n i m p o r t g e t _ s e s s i o n @ a s y n c i o . c o r o u t i n e d e f l o g i n ( r e q u e s t ) : . . . s e s s i o n = y i e l d f r o m g e t _ s e s s i o n ( r e q u e s t ) s e s s i o n [ ' u s e r _ i d ' ] = u s e r _ i d r e t u r n w e b . R e s p o n s e ( . . . )
Rendering Templates Jinja2 & Mako supported via aiohttp_jinja2 & aiohttp_mako Jinja2 Support In app.py, i m p o r t a i o h t t p _ j i n j a 2 i m p o r t j i n j a 2 . . . a i o h t t p _ j i n j a 2 . s e t u p ( a p p , l o a d e r = j i n j a 2 . F i l e S y s t e m L o a d e r ( ' / p a t h / t o / t e m p l a t e s ' ) )
Rendering Templates Jinja2 Support In views.py, i m p o r t a i o h t t p _ j i n j a 2 @ a i o h t t p _ j i n j a 2 . t e m p l a t e ( ' i n d e x . h t m l ' ) d e f i n d e x ( r e q u e s t ) : r e t u r n { ' i s _ i n d e x ' : T r u e }
Rendering Templates Jinja2 Support In views.py, f r o m a i o h t t p _ j i n j a 2 i m p o r t r e n d e r _ t e m p l a t e @ a s y n c i o . c o r o u t i n e d e f i n d e x ( r e q u e s t ) : r e t u r n r e n d e r _ t e m p l a t e ( ' i n d e x . h t m l ' , r e q u e s t , { ' i s _ i n d e x ' : T r u e } )
Rendering JSON In utils.py, i m p o r t u j s o n f r o m a i o h t t p i m p o r t w e b d e f j s o n _ r e s p o n s e ( d a t a , * * k w a r g s ) : # S o m e t i m e s u s e r n e e d s t o o v e r r i d e d e f a u l t c o n t e n t t y p e f o r J S O N k w a r g s . s e t d e f a u l t ( ' c o n t e n t _ t y p e ' , ' a p p l i c a t i o n / j s o n ' ) r e t u r n w e b . R e s p o n s e ( u j s o n . d u m p s ( d a t a ) , * * k w a r g s ) Note: I recommend to use ujson for work with JSON data in Python, cause of speed.
Rendering JSON In views.py, f r o m . u t i l s i m p o r t j s o n _ r e s p o n s e @ a s y n c i o . c o r o u t i n e d e f a p i _ b r o w s e r ( r e q u e s t ) : r e t u r n j s o n _ r e s p o n s e ( { ' p r o j e c t s _ u r l ' : r e q u e s t . a p p . r o u t e r [ ' p r o j e c t s ' ] . u r l ( ) , } )
Serving Static Files Important: It's highly recommend to use nginx, Apache, or other web server for serving static files in production. In app.py, a p p . r o u t e r . a d d _ s t a t i c ( ' / s t a t i c ' , ' / p a t h / t o / s t a t i c ' , n a m e = ' s t a t i c ' )
Serving Static Files In views.py, @ a i o h t t p _ j i n j a 2 . t e m p l a t e ( ' i n d e x . h t m l ' ) d e f i n d e x ( r e q u e s t ) : r e t u r n { ' a p p ' : r e q u e s t . a p p , ' i s _ i n d e x ' : T r u e }
Serving Static Files In index.html, < s c r i p t s r c = " { { a p p . r o u t e r . s t a t i c . u r l ( f i l e n a m e = " d i s t / j s / p r o j e c t . j s " ) } } " t y p e = " t e x t / j a v a s c r i p t " > < / s c r i p t >
Add Route Context Manager f r o m c o n t e x t l i b i m p o r t c o n t e x t m a n a g e r @ c o n t e x t m a n a g e r d e f a d d _ r o u t e _ c o n t e x t ( a p p , v i e w s , u r l _ p r e f i x = N o n e , n a m e _ p r e f i x = N o n e ) : d e f a d d _ r o u t e ( m e t h o d , u r l , n a m e ) : v i e w = g e t a t t r ( v i e w s , n a m e ) u r l = ( ' / ' . j o i n ( ( u r l _ p r e f i x . r s t r i p ( ' / ' ) , u r l . l s t r i p ( ' / ' ) ) ) i f u r l _ p r e f i x e l s e u r l ) n a m e = ' . ' . j o i n ( ( n a m e _ p r e f i x , n a m e ) ) i f n a m e _ p r e f i x e l s e n a m e r e t u r n a p p . r o u t e r . a d d _ r o u t e ( m e t h o d , u r l , v i e w , n a m e = n a m e ) r e t u r n a d d _ r o u t e
Add Route Context Manager In app.py, f r o m . a p i i m p o r t v i e w s a s a p i _ v i e w s w i t h a d d _ r o u t e _ c o n t e x t ( a p p , a p i _ v i e w s , ' / a p i ' , ' a p i ' ) a s a d d _ r o u t e : a d d _ r o u t e ( ' G E T ' , ' / ' , ' i n d e x ' ) a d d _ r o u t e ( ' G E T ' , ' / p r o j e c t s ' , ' p r o j e c t s ' ) a d d _ r o u t e ( ' P O S T ' , ' / p r o j e c t s ' , ' a d d _ p r o j e c t ' ) a d d _ r o u t e ( ' G E T ' , ' / p r o j e c t / { p r o j e c t _ i d : \ d + } ' , ' p r o j e c t ' ) a d d _ r o u t e ( ' P U T ' , ' / p r o j e c t / { p r o j e c t _ i d : \ d + } ' , ' e d i t _ p r o j e c t ' ) a d d _ r o u t e ( ' D E L E T E ' , ' / p r o j e c t / { p r o j e c t _ i d : \ d + } ' , ' d e l e t e _ p r o j e c t ' )
Add Route Context Manager In api/views.py, i m p o r t a s y n c i o f r o m . . u t i l s i m p o r t j s o n _ r e s p o n s e @ a s y n c i o . c o r o u t i n e d e f i n d e x ( r e q u e s t ) : r o u t e r = r e q u e s t . a p p . r o u t e r p r o j e c t _ u r l = r o u t e r [ ' a p i . p r o e j c t ' ] . u r l ( p a r t s = { ' p r o j e c t _ i d ' : 4 2 } ) p r o j e c t _ u r l = p r o j e c t _ u r l . r e p l a c e ( ' 4 2 ' , ' { p r o j e c t _ i d } ' ) r e t u r n j s o n _ r e s p o n s e ( { ' u r l s ' : { ' a d d _ p r o j e c t ' : r o u t e r [ ' a p i . a d d _ p r o j e c t ' ] . u r l ( ) , ' p r o j e c t ' : p r o j e c t _ u r l , ' p r o j e c t s ' : r o u t e r [ ' a p i . p r o j e c t s ' ] . u r l ( ) , } } )
Immutable Settings Python 3.3+ has M a p p i n g T y p e P r o x y Settings shouldn't be changed across the app Welcome, rororo.settings In app.py, f r o m r o r o r o . s e t t i n g s i m p o r t i m m u t a b l e _ s e t t i n g s f r o m . i m p o r t s e t t i n g s d e f c r e a t e _ a p p ( * * o p t i o n s ) : s e t t i n g s _ d i c t = i m m u t a b l e _ s e t t i n g s ( s e t t i n g s , * * o p t i o n s ) a p p = w e b . A p p l i c a t i o n ( ) a p p [ ' s e t t i n g s ' ] = s e t t i n g s _ d i c t . . . r e t u r n a p p
Other Settings & Logging Helpers rororo.settings i n j e c t _ s e t t i n g s s e t u p _ l o c a l e s e t u p _ l o g g i n g s e t u p _ t i m e z o m e t o _ b o o l rororo.logger d e f a u l t _ l o g g i n g _ d i c t u p d a t e _ s e n t r y _ l o g g i n g
Supports DEBUG Mode from Settings c l a s s A p p l i c a t i o n ( w e b . A p p l i c a t i o n ) : d e f _ _ i n i t _ _ ( s e l f , * * k w a r g s ) : s e l f [ ' s e t t i n g s ' ] = k w a r g s . p o p ( ' s e t t i n g s ' ) s u p e r ( ) . _ _ i n i t _ _ ( * * k w a r g s ) d e f m a k e _ h a n d l e r ( s e l f , * * k w a r g s ) : k w a r g s [ ' d e b u g ' ] = s e l f [ ' s e t t i n g s ' ] [ ' D E B U G ' ] k w a r g s [ ' l o o p ' ] = s e l f . l o o p r e t u r n s e l f . _ h a n d l e r _ f a c t o r y ( s e l f , s e l f . r o u t e r , * * k w a r g s )
Schemas You need to validate request & response data Use JSON Schema for validation Describe Schemas with jsl Welcome, rororo.schemas In api/views.py, f r o m r o r o r o . s c h e m a s i m p o r t S c h e m a f r o m . i m p o r t s c h e m a s @ a s y n c i o . c o r o u t i n e d e f a d d _ p r o j e c t ( r e q u e s t ) : s c h e m a = S c h e m a ( s c h e m a s . a d d _ p r o j e c t , r e s p o n s e _ f a c t o r y = j s o n _ r e s p o n s e ) d a t a = s c h e m a . v a l i d a t e _ r e q u e s t ( ( y i e l d f r o m r e q u e s t . p o s t ( ) ) ) . . . r e t u r n s c h e m a . m a k e _ r e s p o n s e ( p r o j e c t _ d i c t )
Schemas Describing Schemas In api/schemas/add_project.py, f r o m j s l i m p o r t D o c u m e n t , I n t e g e r F i e l d , S t r i n g F i e l d c l a s s P r o j e c t ( D o c u m e n t ) : n a m e = S t r i n g F i e l d ( m i n _ l e n g t h = 1 , m a x _ l e n g t h = 3 2 , r e q u i r e d = T r u e ) s l u g = S t r i n g F i e l d ( m i n _ l e n g t h = 1 , m a x _ l e n g t h = 3 2 , r e q u i r e d = T r u e ) d e s c r i p t i o n = S t r i n g F i e l d ( m a x _ l e n g t h = 2 5 5 ) c l a s s R e s p o n s e ( P r o j e c t ) : i d = I n t e g e r F i e l d ( m i n i m u m = 0 , r e q u i r e d = T r u e ) r e q u e s t = P r o j e c t . g e t _ s c h e m a ( ) r e s p o n s e = R e s p o n s e . g e t _ s c h e m a ( ) Or use plain Python dicts instead
JavaScript is quite good right now ES2015 is a great step in right direction Many tools around JS is maturing too Bundling is easy, meet webpack Linting is easy, meet eslint Making UI is easy, meet react And I still not talking about DX tools :)
Asyncio Stack is ready for usage The Future is Here! Use Python 3.4 for all good things a i o h t t p . w e b , easy to start and easy to use a i o p g . s a allows you to forget about ORM r o r o r o contains useful helpers for a i o h t t p . w e b apps If you miss something, port it to Asyncio stack by yourself Don't forget to payback to Open Source Software
Bonus. Python 3.5 a s y n c d e f l o g o u t ( r e q u e s t ) : s e s s i o n = a w a i t g e t _ s e s s i o n ( r e q u e s t ) s e s s i o n . i n v a l i d a t e ( ) r e t u r n R e s p o n s e ( s t a t u s = 2 0 4 ) a s y n c d e f p r o j e c t s ( r e q u e s t ) : p r o j e c t s = [ ] a s y n c w i t h c r e a t e _ e n g i n e ( D S N ) a s c o n n : q u e r y = . . . a s y n c f o r p r o j e c t i n c o n n . e x e c u t e ( q u e r y ) : p r o j e c t s . a p p e n d ( p r o j e c t ) r e t u r n j s o n _ r e s p o n s e ( p r o j e c t s )