React, Babel + Eslint Configuration

If you want to configure your npm package to have JavaScript static code analysis then you will need to configure some form of js linter. For the purposes of this demo I will use eslint.

Before We Begin:

Packages:

Package Installations:

npm install --save-dev babel
npm install --save-dev babel-core
npm install --save-dev babel-loader
npm install --save-dev babel-preset-es2015
npm install --save-dev babel-eslint //OPTIONAL
npm install --save-dev eslint
npm install --save-dev eslint-loader
npm install --save-dev eslint-plugin-react
npm install --save-dev uglifyjs-webpack-plugin

Webpack.config.js:

Webpack 3 Plugins

Add “NoEmitOnErrorsPlugin” to ensure that emitting doesn’t occur if errors are encountered.

Add “UglifyJsPlugin” to minimize your js

Add “AggressiveMergingPlugin” to get more aggressive chunk merging.

Don’t forget the loaders for babel and eslint.

const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

#Under module.exports add
plugins: [
    	new webpack.NoEmitOnErrorsPlugin(),
    	new UglifyJsPlugin(),
    	new webpack.optimize.AggressiveMergingPlugin()
],
module: {
    	loaders: [
			{
				enforce: "pre",
				test: /\.jsx?$/,
				exclude: /(node_modules|thirdparty)/,
				loader: "eslint-loader",
			},
			{
				test: /\.jsx?$/,
				exclude: /(node_modules)/,
				loader: "babel-loader",
				query: {
					presets: ["es2015","react"],
				}
			},
    	]
}


.eslintrc

You can set anything in your .eslintrc file. Here are just some that I use. All the rules starting with “react” are from “eslint-plugin-react” package.

{
    "parser": "babel-eslint",
    "env": {
        "browser": true,
        "mocha": true,
        "es6": true
    },
    "globals": {
        "require": true
    },
    "plugins": [
        "react"
    ],
    "rules": {
        "new-cap": 0, //disabled 
        "strict": 0, //disabled 
        "semi": [2, "always"], //semi-colon required
        "no-underscore-dangle": 0, //no underscores before and after off
        "no-use-before-define": 1, //Make sure you define then use
        "eol-last": 0, //File doesn't need a newline at end
	"no-trailing-spaces": [2, { skipBlankLines: true }],
        "no-unused-vars": [1, {"vars": "all", "args": "none"}],
        "quotes": [
            2,
            "double"
        ],
        "jsx-quotes": [2, "prefer-double"], //Must use double quotes
        "react/jsx-boolean-value": 1, //warning when no value is set for boolean
        "react/jsx-no-undef": 2, //error: undeclared variables
        "react/jsx-uses-react": 1, //Warn: Prevent incorrectly unused
        "react/jsx-uses-vars": 1, //Warn: Prevent unused variables
        "react/no-did-mount-set-state": 0, //Off: Prevent setState in componentDidMount
        "react/no-did-update-set-state": 0, //Off: Prevent setState in componentDidUpdate
        "react/no-multi-comp": 1, //Warn: Prevent only one component per file
        "react/no-unknown-property": 1, //Warn: Prevent unknown DOM
        "react/react-in-jsx-scope": 1, //Warn: Prevent missing React
        "react/self-closing-comp": 1 //Warn: Prevent extra closing tags
    }
}

.eslintignore

Use this file to ignore any files or folders.

app/folder/**

.babelrc

This is the config file for babel.

{
 "presets": [ "es2015" ]
}

Package.json

If you want to run eslint from npm then you have to modify your package.json with the following line. –ext is what extension you want to test against.

If you want a third party to run reports on your eslint then add to the end “-f checkstyle > eslint.xml

"scripts": {
    "eslint": "eslint app --ext .jsx --ext .js"
}

Django: React Website

In this tutorial I will demonstrate how to create a Django + React website using Django 2.0. You must have Eclipse installed before you continue. If you have it already installed and configured you can continue on. We will require Postgres 9.4, nodejs before you continue. You can get Nodejs from here. You can get Postgres 9.4 from here.

Pip Django Install:
pip install django
pip install django-webpack-loader
Django Version:

If you are not sure what version you are running do the following

python -c "import django; print(django.get_version())"
Eclipse Create Project:

 

 

 

 

 

 

Eclipse Setup Project:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Eclipse Django DB Settings:

 

 

 

 

 

 

 

 

 

 

 

 

 

Eclipse Django Setup Successful:

Once you click “Finish” your project will look like the following.

 

 

 

Folder Structure:
  • Under djangoApp project.
  • folder: static
  • folder: djangoApp
    • folder: templates
      • file: index.html
      • folder: base
        • file: base.html
  • folder: assets
    • folder: bundles
    • folder: js
      • file: index.jsx
Node:

Inside the djangoApp application do the following

npm init
npm install --save-dev jquery react react-dom webpack webpack-bundle-tracker babel-loader babel-core babel-preset-es2015 babel-preset-react
npm install create-react-class --save
webpack.config.js:
var path = require('path')
var webpack = require('webpack')
var BundleTracker = require('webpack-bundle-tracker')

module.exports = {
    //the base directory (absolute path) for resolving the entry option
    context: __dirname,
    //the entry point we created earlier. Note that './' means 
    //your current directory.
    entry: {
		"index": [path.resolve(__dirname, "./assets/js/index.jsx")],
	},
	output: {
		path: path.resolve('./assets/bundles/'),
		filename: "[name]-[hash].js",
	},
    plugins: [
        //tells webpack where to store data about your bundles.
        new BundleTracker({filename: './webpack-stats.json'}), 
        //makes jQuery available in every module
        new webpack.ProvidePlugin({ 
            $: 'jquery',
            jQuery: 'jquery',
            'window.jQuery': 'jquery' 
        })
    ],
    module: {
        loaders: [
		{
			test: /\.jsx?$/,
			exclude: /(node_modules)/,
			loader: 'babel-loader',
			query: {
				presets: ['react','es2015']
			}
		}
        ]
    }
}
djangoApp\Settings.py:

Installed Apps

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'webpack_loader',
]

Add/Edit the following template directive

TEMPLATES = [
 {
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [os.path.join(BASE_DIR, 'djangoApp', 'templates'),],
    'APP_DIRS': True,
    'OPTIONS': {
        'context_processors': [
            'django.template.context_processors.debug',
            'django.template.context_processors.request',
            'django.contrib.auth.context_processors.auth',
            'django.contrib.messages.context_processors.messages',
        ],
    },
},]

Add the following static directive

STATIC_URL = '/static/'

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'assets'),
]

Modify DATABASES

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'YOUR_DB_NAME',
        'USER': 'YOUR_USER',
        'PASSWORD': 'YOUR_PASSWORD',
        'HOST': 'localhost',
        'PORT': 5432
    }
}

Webpack Loader

WEBPACK_LOADER = {
    'DEFAULT': {
        'BUNDLE_DIR_NAME': 'bundles/',
        'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'),
    }
}
djangoApp\views.py:

We will create our index page view. Notice the third dict. Those are variables passed to the template to make our site dynamic

from django.shortcuts import render

def index(request):
    return render(request, 'index.html', {'title': 'Index Page', 'script_name': 'index'})
djangoApp\urls.py:

Add the following imports

from django.conf.urls import url
#This is the index view we created above
from djangoApp.views import index

urlpatterns = [
    url(r'^$', index, name='index'),
    path('admin/', admin.site.urls),
]
djangoApp\templates\base\base.html:

Let’s setup our base template and setup our blocks that the other templates will inherit from.

<html>
	<head>
		<title>{% block title %}{% endblock %}</title>
	</head>
	<body>
		{% block content %}
		{% endblock %}
	</body>
</html>
djangoApp\templates\index.html:

The important parts here are the extends otherwise your base.html template won’t be inherited. As well the {% with %} and title variable makes our template dynamic and allows us to incorporate react in our site.

{% extends "base/base.html"  %}
{% load render_bundle from webpack_loader %}
{% load staticfiles %}
{% block title %}
	{{title}}
{% endblock %}
{% block content %}
	<div id="container"></div>
	{% with script=script_name %}
		{% render_bundle script 'js' %}
	{% endwith %} 
{% endblock %}
assets\js\index.jsx:

This is our react class.

var React = require('react');
var ReactDOM = require('react-dom');
var createReactClass = require('create-react-class');

var App = createReactClass({
    render: function() {
        return (
            <h1>
            React App Page
            </h1>
        )
    }
});

ReactDOM.render(<App />, document.getElementById('container'));
Database Setup/Migration:

For this tutorial we used postgres. At this time please make sure you create your djangoApp db and user you specified in the settings.py file. Then run the following commands in order.

#Migrates the auth
python manage.py migrate auth
#migrates the rest
python manage.py migrate
#Create the user for accessing the django admin ui
#This will ask you for user names and passwords. Don't make it the same as in your settings.py file.
python manage.py createsuperuser
Start Server:
webpack -p
python manage.py runserver

Your site is now running at http://localhost:8000.

Your admin site is now running at http://localhost:8000/admin/.

 

References:

I used this video as a guideline to get the project started. However some didn’t work right and needed to adjust and made adjustments to require just one template, etc.

React: Leaflet Markers

This entry is part 9 of 9 in the series React: Leaflet

In this tutorial I will demonstrate how to add a marker to the map. Refer to the documentation for more information.

Before We Begin:

Text:

LayerGroup onAdd Method

//Define the marker. Since this is text based their is no icon but instead a divIcon
this.textMarker = L.marker([20.5, -0.09],{
	icon: L.divIcon({
		iconSize: [100,16],
		iconAnchor: [22, 30],
		className: "",
	}), 
	zIndexOffset: 75,
});

//Sets the text
this.textMarker.options.icon.options.html = "This is my text";

//Adds the text marker to the layer
this.addLayer(this.textMarker);

LayerGroup onRemove Method

//Remove the text marker you just added
this.removeLayer(this.textMarker);

Results: Now you will see as you turn the layer on and off from the context menu the text will show or hide.

Icon:

Import Image:

import myImagefrom "../images/myImage.png";

Create Icon/Marker onAdd Method

//Define the icon you will be using
var myIcon = L.icon({
	iconUrl: myImage,
	iconSize:     [75, 75], // size of the icon
	iconAnchor:   [22, 94], // point of the icon which will correspond to marker's location
});

//Define the icon based marker
this.marker = L.marker([20.5, -40.09],{
	icon: myIcon,
	opacity: 0.7,
	zIndexOffset: 30
 });

//Adds the icon marker to the layer
this.addLayer(this.marker);

onRemove Method

//Remove the marker you just added
this.removeLayer(this.marker);

Results: Now you will see as you turn the layer on and off from the context menu the icon will show or hide.

Set Latitude/Longitude:

You can set the latitude and longitude in one of two ways.

//Set the latitude and longitude

//Method 1:
L.marker([20.5, -0.09])

//Method 2:
this.textMarker.setLatLng([20.5, -0.09]);

Event(s):

onClick

marker.on("click", function(e) {
	var marker = e.target;
	//Do my work here
}.bind(this));

MouseOver

marker.on("mouseover", function(e) {
	var marker = e.target;
	//Do my work here
}.bind(this));

MouseOut

marker.on("mouseout", function(e) {
	var marker = e.target;
	//Do my work here
}.bind(this));

Popup:

BindPopup

marker.bindPopup(L.popup(),{
	offset: L.point(0,-10) //You can add an offset if you want to
});

OpenPopup

Let’s say you want to open the popup during a mouseover event.

marker.on("mouseover", function(e) {
	var marker = e.target;

	//Set the content to display in the popup
	marker.setPopupContent("My Text");
	//Now open the popup
	marker.openPopup();
}.bind(this));

ClosePopup

Let’s say you want to open the popup during a mouseout event.

marker.on("mouseout", function(e) {
	var marker = e.target;
	//Close the popup now
	marker.closePopup();
}.bind(this));

React: Leaflet Icon

This entry is part 8 of 9 in the series React: Leaflet

In this tutorial I will demonstrate how to add an icon to the map. Refer to the documentation for more information.

Before We Begin:

Create Folders/Files:

  • app
    • folder: leaflet
      • folder: images
        • file: yourimage.png

Import Image(s):

import yourimage from "../images/yourimage.png";

Create Icon:

var myIcon = L.icon({
     iconUrl: plusimg,
     iconSize:     [75, 75], // size of the icon
     iconAnchor:   [22, 94], // point of the icon which will correspond to marker's location
});

Add to Map:

[51.5, -0.09] refers to the position on the map you want to add it to. Refer to the marker tutorial for further information on markers.

L.marker([51.5, -0.09], {icon: myIcon}).addTo(this.map);

React: Leaflet Control

This entry is part 7 of 9 in the series React: Leaflet

In this tutorial I will demonstrate how to add a control to the map. Refer to the documentation for more information.

Before We Begin:

LayerControl

You can now do anything you want to do here.

var MyControl = L.Control.extend({
    onAdd: function (map){
        //Reference to the map
        this._map = map;

        //This is the container to return so it is available on the map
        var container = L.DomUtil.create("div");
        return container;
    },
    onRemove: function(){
        //Removes the control
        L.Control.prototype.onRemove.call(this);
        this._map = null;
    }
});

Add Control to Map

The options you add in the class definition are the options that the control gets.

var myControl = new MyControl({ position: "bottomright"});
myControl.addTo(this.map);

React: Leaflet LayerGroup

This entry is part 6 of 9 in the series React: Leaflet

In this tutorial I will demonstrate how to add a layer group control. Refer to the documentation for more information.

Before We Begin:

LayerGroup

You can now do anything you want to do here.

var MyLayerGroup = L.LayerGroup.extend({
    onAdd: function(map) {
        //Reference to the map
        this._map = map;
    },
    onRemove: function(){
        //Removes the layer
        L.LayerGroup.prototype.onRemove.call(this, map);
    },
    initialize: function (options) {
        L.LayerGroup.prototype.initialize.call(this);
        //The options sent in from initialisation
        L.Util.setOptions(this, options);
    }
});

Add LayerGroup to Map

var myLayerGroup = new MyLayerGroup();
myLayerGroup.addTo(this.map);

Overlay

If you want to add the layergroup to overlays menu.

this.layerControl.addOverlay(myLayerGroup, "My Layer Group");

React: Leaflet DomEvent

This entry is part 5 of 9 in the series React: Leaflet

In this tutorial I will demonstrate how to add events to your html control. Refer to the documentation for more information.

Before We Begin:

OnClick

L.DomEvent.on(htmlControl, "click", (e) => {
 	//Do your work here
});

 

 

 

React: Leaflet html Controls

This entry is part 4 of 9 in the series React: Leaflet

In this tutorial I will demonstrate how to add html controls to your leaflet map. Refer to the documentation for more information.

Before We Begin:

Really you can create any html control just substitute div for whatever you want. Below are just some basic examples.

Div

var divContainer = L.DomUtil.create("div", "myClassName");

Image

var img = L.DomUtil.create("img", "myClassName");

Label

var label = L.DomUtil.create("label", "myClassName");

Span

var span = L.DomUtil.create("span", "myClassName");

Input

var input = L.DomUtil.create("input", "myClassName");

BR

var br = L.DomUtil.create("br", "myClassName");

You can modify the html control with additional values such as

#style
divContainer.style.display = "";

#id
divContainer.id = "myId";

#name
divContainer.name = "myId";

#img src
divContainer.src = "";

#title
divContainer.title = "";

#innerHTML
divContainer.innerHTML = "";

#type
divContainer.type= "checkbox";

#checked
divContainer.checked= false;


Parent Control

You can add the control to a parent control in the following way.

var divContainer = L.DomUtil.create("div", "myClassName");

var subContainer = L.DomUtil.create("div", "myClassName", divContainer);

React: Leaflet EasyButton

This entry is part 3 of 9 in the series React: Leaflet

In this tutorial I will demonstrate how to add a easy button to your leaflet map. This will just be a basic example. For more information refer to the documentation.

Before We Begin:

Node Package Install:

npm install leaflet-easybutton --save

Edit app/home/leaflet/js/map.jsx:

//Add the leaflet easybutton package
require("leaflet-easybutton");
require("leaflet-easybutton/src/easy-button.css");

//Somehwere on your page add the following.


//Check out https://github.com/CliffCloud/Leaflet.EasyButton for more uses
L.easyButton("<div id="tag" class="glyphicon glyphicon-tag" />", function(btn, map) {
 	map.setView([42.3748204,-71.1161913],16);
}, { position: "topleft"}).addTo(this.map);

React: Leaflet Modal

This entry is part 2 of 9 in the series React: Leaflet

In this tutorial I will demonstrate how to add a modal dialog to your leaflet map. Refer to the documentation for more information.

Before We Begin:

Node Package Install:

npm install leaflet-modal --save

Edit app/home/leaflet/js/map.jsx:

//Add the leaflet modal package
require("leaflet-modal");
require("leaflet-modal/dist/leaflet.modal.min.css");

//Somewhere on your page add the following. You can even put it on a onclick event or whatever

//Create a test control for your modal control.
this.modalContainer = L.DomUtil.create("div");
map.openModal({
	zIndex: 10000, 
	element: this.modalContainer, //The container to display in the modal 
	OVERLAY_CLS: "overlay", //This is a built in class which you can change if you want
	MODAL_CLS: "modal", //This is a built in class which you can change if you want
	height: 200, width: 200, 
	MODAL_CONTENT_CLS: "modal-content",  //This is a built in class which you can change if you want
	CLOSE_CLS: "modal-close-hide" //The property for close class
});

Edit app/home/leaflet/css/map.css:

.modal-close-hide {
	/*Class for hiding the modal*/
	display: none;
}

React: Basic Leaflet Map Example

This entry is part 1 of 9 in the series React: Leaflet

In this tutorial I will walk you through incorporating leaflet map in your application. This is just a basic setup walkthrough. We do not deal with overlays or extending controls.

Documentation:

Leaflet
Leaflet Providers

Before We Begin:

If you have not done so already refer to how to Build a React/Python Site to get your basic site up and running.

Node Package Installs:

npm install leaflet-providers --save
npm install leaflet --save

Create Needed Folders/Files:

  • app
    • folder: leaflet
      • folder: css
        • file: map.css
      • folder: js
        • file: map.jsx

Webpack:

Add the following loader for handling images to your webpack.config.js file.

{ test: /.*\.(gif|png|jpe?g|svg)$/i, loader: "file-loader?hash=sha512&digest=hex&name=[name].[ext]" }

Edit app/leaflet/js/map.jsx:

We are going to create the map control. Refer to comments below for explanations.

window.jQuery = window.$ = require("jquery");
//React packages
var React = require("react");
var createReactClass = require("create-react-class");

//Leaflet packages
import L from "leaflet";
import "leaflet/dist/leaflet.css";

//LeafLet providers
require("leaflet-providers");

//map css you will create
import "../css/map.css";

var LeafLetMap = createReactClass({
    getInitialState: function() {
        return {};
    },
    displayMap: function() {
        //Setup/initialize the map control
        window.map = this.map = L.map("map", {
            attributionControl: false, //http://leafletjs.com/reference-1.2.0.html#control-attribution
        }).setView([0,0], 1);
        
        //A simple scale control that shows the scale of the current center of screen in metric (m/km) and imperial (mi/ft) 
        L.control.scale({ //http://leafletjs.com/reference-1.2.0.html#control-scale
            metric: false,
            imperial: true
        }).addTo(this.map);
        
        //You can check out all the free providers here https://github.com/leaflet-extras/leaflet-providers
        //Below are just examples
        this.layers = {
                OpenMapSurferRoads: L.tileLayer.provider('OpenMapSurfer.Roads'),
                HyddaFull: L.tileLayer.provider('Hydda.Full'),
                StamenTerrain: L.tileLayer.provider('Stamen.Terrain'),
                EsriWorldStreetMap: L.tileLayer.provider('Esri.WorldStreetMap'),
                EsriWorldTopoMap: L.tileLayer.provider('Esri.WorldTopoMap'),
                EsriWorldTerrain: L.tileLayer.provider('Esri.WorldTerrain'),
                OpenTopoMap: L.tileLayer.provider('OpenTopoMap'),
                OpenStreetMapBlackAndWhite: L.tileLayer.provider('OpenStreetMap.BlackAndWhite'),
                OpenStreetMapHOT: L.tileLayer.provider('OpenStreetMap.HOT')
            };

        //Add the default layer you want to use
        this.layers.OpenMapSurferRoads.addTo(this.map);

        //Add the layer control to the top left corner
        this.layerControl = L.control.layers(this.layers, {}, { position: "topleft" }).addTo(map);
    },
    componentDidMount: function() {
        //Done after mounting so that it sees the div you are going to map to
        this.displayMap();
    },
    render: function() {
        return (
            <div className="div-flex">
                <div id="map" key="map" className="map"></div>
            </div>
        );
    }
});

module.exports = { LeafLetMap:LeafLetMap };

Edit app/leaflet/css/map.css:

.map {
	/*Changes the width and height of the map*/
	height: 400px;
	width: 800px;
}

div.leaflet-top {
	/*OPTIONAL: This is only needed if your controls start going behind the map.*/
	z-index: 1001;
}

.leaflet-control-layers-toggle {
	/*Changes the width and height of the layer control*/
	height: 30px !important;
	width: 30px !important;
}

Edit app/home/js/home.jsx:

//Import your leaflet map control from the page you created above
import { LeafLetMap } from "../../leaflet/js/map.jsx";

//In the render function add your control
render: function() {
		return (
			<div className="div-flex">
		        	<LeafLetMap ref="map" />
			</div>
		);
	}

 

 

 

React Add CSS to Your Site

If you haven’t already done so please follow this tutorial in setting up your React/Python site.

Folder Structure:
  • You need to add file(s) to your existing structure.
  • Inside testApp create the following:
    • folder: app
      • folder: home
      • folder: css
        • file: home.css
NPM:

We need to add style-loader and css-loader to our application.

npm install style-loader --save
npm install css-loader --save
npm install create-react-class --save
Webpack.config.js Setup:

Now that we have installed our loaders we need to setup webpack.config.js to use our new loaders. The below will go in the “loaders” array.

	{
		test: /\.css$/,
		loader: 'style-loader!css-loader'
	}

Things to note:

  • The ! in the loader section just means it applies to both.
  • The .css in the test area says any css file.
Home.jsx Modification:

We need to pull in our css file for home.jsx. Notice the “require” now in our home.jsx file and that we added a class to our div.

var React = require("react");
var ReactDOM = require("react-dom");
var createReactClass = require("create-react-class");

require("../css/home.css");

var Home = createReactClass({
	render: function() {
		return (<div className="div-hi">Hi</div>);
	}
});

ReactDOM.render(<Home />, document.getElementById("app"));
Home.css Setup:

Put anything you want in the css file as long as it matches the classname we set in home.jsx.

.div-hi {
	color: red;
}
Run:

We can now run the following to build and deploy our site. It will be available on http://localhost:5000/. Once you login you will be able to see our red “hi”. As long as you following the building a react python site creation.

webpack
flask run

Flask: React Website

This whole tutorial describes in depth how to create a React website with Python. You must have Eclipse installed before you continue. If you have it already installed and configured you can continue on. Note that you should probably have HTML Editor and TypeScript IDE installed for Eclipse.

FYI: I am using Windows at the moment for this tutorial but you can substitute Ubuntu in if you so chose.

We will require Postgres 9.4, nodejs before you continue. You can get Nodejs from here. You can get Postgres 9.4 from here.

In this tutorial we use Flask. If you are not familiar with it go here. Flask is a lightweight Python web framework based on Werkzeug and Jinja 2.

Folder Structure:
  • You will need to create a folder called “testApp”.
  • Inside testApp create the following:
    • folder: app
      • file: models.py
      • file: __init__.py
      • folder: auth
        • file: __init__.py
        • file: views.py
      • folder: home
        • file: __init__.py
        • file: views.py
        • folder: js
          • file: home.jsx
      • folder: templates
        • file: base.html
        • file: login.html
        • file: register.html
    • file: config.py
    • file: requirements.txt
    • file: webpack.config.js
    • file: run.py
    • folder: instance
      • file: config.py
    • folder: static
      • folder: common
        • folder: css
          • file: base.css
      • file: manifest.json
    • .babelrc
Install Python Packages:

Open cmd/terminal and navigate into your testApp folder and run the following commands.

pip install flask-sqlalchemy && pip freeze > requirements.txt
pip install flask-login && pip freeze > requirements.txt
pip install flask-migrate && pip freeze > requirements.txt
pip install psycopg2 && pip freeze > requirements.txt
pip install flask-Webpack && pip freeze > requirements.txt
pip install Flask-WTF && pip freeze > requirements.txt
pip install flask-bootstrap && pip freeze > requirements.txt

Couple things to note:

  • The “&& pip freeze > requirements.txt” saves the install in requirements.txt.
  • flask-migrate: database migrations package
  • flask-sqlalchemy: model engine for your database.
  • flask-login: provides user session management for Flask
  • psycopg2: Postgres driver
  • flask-Webpack: A Flask extension to manage assets with Webpack
  • flask-WTF: flask form validation
  • flask-bootstrap: An extension that includes Bootstrap in your project, without any boilerplate code
Routing:

For the purpose of this example we are using the basic Flask implementation. IE: @home.route(‘/’). However if you want to do a more advanced routing do Resource.

Database:

Open PgAdmin III and create yourself a database called “testApp”. Also create a user with password granting access to testApp database. Permission as you see fit. Don’t forget to write down your password :).

Setup Config.py:

Read here for SqlAlchemy configruation options. DEBUG is for flask debugging.

class Config(object):
    """
    This is the shared common configurations
    """

    # Put any configurations here that are common across all environments

class DevelopmentConfig(Config):
    """
    This is the development configurations
    """
    
    DEBUG = True
    SQLALCHEMY_ECHO = True #Display queries to console

class ProductionConfig(Config):
    """
    This is the production configurations
    """
    DEBUG = False
    SQLALCHEMY_ECHO = False #Do not Display queries to console

app_config = {
    'development': DevelopmentConfig,
    'production': ProductionConfig
}

Things to Note:

Notice how we don’t have any database connections in this config.py. That is because we really shouldn’t checkin to source control our database connection strings, etc.

Setup instance config.py:

We open the file config.py inside our “instance” folder and add this line only.

SQLALCHEMY_DATABASE_URI = "postgresql://##USER##:##PASSWORD##@##HOST##/testApp"
Setup __init__.py in “app” folder:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_migrate import Migrate
from flask_bootstrap import Bootstrap
import os

#Importing from the config.py
from config import app_config

# db variable initialization
db = SQLAlchemy()

#The flask login manager
login_manager = LoginManager()

webpack = Webpack()

def create_app(config_name):
    #This will be either "development" or "production" mapped to what we write in the config.py application
    #static_folder is where the static folder will be located
    app = Flask(__name__, instance_relative_config=True, static_folder=os.path.join(os.getcwd(), "static"))
    print('Running in %s' % (config_name))
    app.config.from_object(app_config[config_name])
    app.config.from_pyfile('config.py')
    #You need a secret key to be able to utilise the database connection
    app.secret_key = 'Awesome App'
    Bootstrap(app)
    db.init_app(app)
    #This will make it so our chunked js files are able to be loaded on the template
    app.config.update({'WEBPACK_MANIFEST_PATH': '../manifest.json'})
    webpack.init_app(app)

    #if a user tries to access a page that they are not authorized to, it will redirect to the specified view and display the specified message.
    login_manager.init_app(app)
    login_manager.login_message = "You must be logged in to access this page."
    #auth.login is not the route but actually the class path.
    login_manager.login_view = "auth.login"
    
    #This let's us do our migrations
    migrate = Migrate(app, db)

    #Bring in our new tables
    from app import models

    #Our blueprints for our app

    #This is how you get authenticated
    from .auth import auth as auth_blueprint
    app.register_blueprint(auth_blueprint)
	
    #Bring in the home module
    from .home import home as home_blueprint
    app.register_blueprint(home_blueprint)

    return app
Environment Variable Setup:

Flask has two environment variables that you can set which point to the environment to use and the run.py file. These are FLASK_CONFIG and FLASK_APP. I set my environment to “development” and the app to use “run.py”. Notice that “development” maps to the key value pair in config.py.

Setup run.py:

Notice how we utilise the FLASK_CONFIG from the environment variables to setup our environment and grab the right config class.

import os #We need this to get the OS ENV VARIABLE 'FLASK_CONFIG'

#You are going to import the create_app from the __init__.py file
from app import create_app

#In our environment variables we create "FLASK_CONFIG" and set our value either development or production
config_name = os.getenv('FLASK_CONFIG')
app = create_app(config_name)

if __name__ == '__main__':
    app.run()

Now the fun really starts!!!

Setup models.py:

We setup our User model ensuring security of our password. Later on I will show you what happens with flask-migrate.

from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash

from app import db

class User(UserMixin, db.Model):
    """
    Create an Users table
    """

    # Ensures table will be named in plural and not in singular
    # as is the name of the model
    __tablename__ = 'users'

    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(60), index=True, unique=True)
    username = db.Column(db.String(60), index=True, unique=True)
    first_name = db.Column(db.String(60), index=True)
    last_name = db.Column(db.String(60), index=True)
    password_hash = db.Column(db.String(128))

    @property
    def password(self):
        """
        Prevent pasword from being accessed
        """
        raise AttributeError('password is not a readable attribute.')

    @password.setter
    def password(self, password):
        """
        Set password to a hashed password
        """
        self.password_hash = generate_password_hash(password)

    def verify_password(self, password):
        """
        Check if hashed password matches actual password
        """
        return check_password_hash(self.password_hash, password)

    def __repr__(self):
        return ''.format(self.username)
Generate User Table:

Using flask-migrate we can now auto generate our User table into Postgres testApp database.

  1. Run “flask db init” to create our migration repository
    1. A “migrations” folder is created automatically.
  2. Run “flask db migrate”
    1. This generates the migration script
  3. Run “flask db upgrade”
    1. This creates the missing migrations into the database. AKA the users table.
    2. You will notice another table also got created “alembic_version”. This is how it stores the version it is at.
  4. Confirm that the db was migrated successfully.
Create Our Templates:

We use the base.html file for our react pages using the “app” id on the div. The login.html is for our login form and register.html is to register a new user. You can use the base.css file in the static/common/css folder to style it how you want. I recommend using flex.

base.html:

<!DOCTYPE html>
{% import "bootstrap/utils.html" as utils %}
{% import "bootstrap/wtf.html" as wtf %}
{% extends "bootstrap/base.html" %}
<html>
	<head>
		{% block head %}
		{{ super() }}
		<title>{{title}}</title>
		<link rel="stylesheet" type="text/css" href={{ url_for("static", filename="common/css/base.css") }} />
		{% endblock %}
	</head>
	{% block content %}
	<div class="container-app">
		<div class="container-header">Team Link</div>
	    <div class="container-body" id="app"></div>
	    <script type="text/javascript" src={{ asset_url_for(script_name) }}></script>
	</div>
	{% endblock %}
</html>

login.html:

<!DOCTYPE html>
{% import "bootstrap/utils.html" as utils %}
{% import "bootstrap/wtf.html" as wtf %}
{% extends "bootstrap/base.html" %}
<html>
	<head>
		{% block head %}
		{{ super() }}
		<title>{{title}}</title>
		<link rel="stylesheet" type="text/css" href={{ url_for("static", filename="common/css/base.css") }} />
		{% endblock %}
	</head>
	{% block content %}
	<div class="container-app">
		<div class="container-header">My Awesome App</div>
	    <div class="container-body" id="app">
			<div class="panel panel-default">
				<div class="panel-heading">
					<h3 class="panel-title">Login</h3>
				</div>
				<div class="panel-body">
					{{ wtf.quick_form(form) }}
				</div>	    
				{{ utils.flashed_messages() }}
				Click here to <a href="/register">register</a>
			</div>
		</div>
	</div>
	{% endblock %}
</html>

register.html

<!DOCTYPE html>
{% import "bootstrap/utils.html" as utils %}
{% import "bootstrap/wtf.html" as wtf %}
{% extends "bootstrap/base.html" %}
<html>
	<head>
		{% block head %}
		{{ super() }}
		<title>{{title}}</title>
		<link rel="stylesheet" type="text/css" href={{ url_for("static", filename="common/css/base.css") }} />
		{% endblock %}
	</head>
	{% block content %}
	<div class="container-app">
		<div class="container-header">Team Link</div>
	    <div class="container-body" id="app">
			<div class="panel panel-default">
				<div class="panel-heading">
					<h3 class="panel-title">Register</h3>
				</div>
				<div class="panel-body">
    				{{ wtf.quick_form(form) }}
				</div>	    
				{{ utils.flashed_messages() }}
				Click here to <a href="login">login</a>
			</div>
		</div>
	</div>
	{% endblock %}
</html>
Setup home __init__.py:

This creates the blueprint that we have in app.__init__.py.

from flask import Blueprint

home = Blueprint('home', __name__)

#This is the views.py from the home directory.
from . import views
Setup home views.py:

@login_required let’s flask know that you need to be logged in to get to this page. Don’t forget to see “render_template” method. How it has “script_name” in it and it uses base.html template. “script_name” was utilised in base.html. It brings in our js file for us on each page we go to.

from flask import render_template
from flask_login import login_required

#This is our blueprint we setup in __init__.py
from . import home

@home.route('/')
@login_required
def homepage():
    """
    Render the homepage template on the / route
    """
    return render_template('base.html', script_name='home.js', title="Welcome")
Setup React home JSX file:

React uses jsx files. So in my home directory I have a js folder and inside that we have our home.jsx file. Let’s set that up to something really basic. Remember above I said in the “render_template” we use the div id “app”. The ReactDOM will put our class in that spot. I will show you later how that is done.

var React = require("react");
var ReactDOM = require("react-dom");
var createReactClass = require("create-react-class");

var Home = createReactClass({
	render: function() {
		return (<div>Hi</div>);
	}
});

ReactDOM.render(<Home />, document.getElementById("app"));
Node Init:

We need to go to our root directory testApp from command prompt and run the following in the exact order.

npm init
npm install react react-dom --save
npm install webpack webpack-dev-server --save
npm install --save-dev babel babel-core babel-loader babel-preset-es2015 babel-preset-react
npm install create-react-class --save
npm install bootstrap --save
npm install jquery --save
npm install clean-webpack-plugin --save-dev
npm install manifest-revision-webpack-plugin --save-dev
npm install sync-exec --save-dev
npm install uglifyjs-webpack-plugin --save-dev

Things to Note:

  • npm init: Creates package.json file
  • npm install –save: will save the install to package.json
  • Each package install went into a new directory called “node_modules”.
Setup webpack.config.js:

We want to transition our jsx files to .js. Remember back in app.__init__.py we setup the static folder location. Checkout the “path” key below and now we know how it knows where it’s assets are going to be located. Our entry key value pair is the locations of each of our jsx files to create assets from. Then we have our loaders.

var path = require("path");
var webpack = require('webpack');
var ManifestRevisionPlugin = require('manifest-revision-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');

module.exports = {
	entry: {
		"home": [path.resolve(__dirname, "./app/home/js/home.jsx")],
	},
	output: {
        path: path.join(__dirname, "static"),
		publicPath: "/static/",
		filename: "[name]-[hash].js"
	},
	plugins: [
                new CleanWebpackPlugin(["static/*.js", "static/manifest.json"], {root: __dirname, verbose: true, dry: false, exclude: ["base.css"]}),
		new ManifestRevisionPlugin(path.resolve(__dirname, "./manifest.json"), {rootAssetPath: './static', ignorePaths: ['./node_modules']}),
		new webpack.NoEmitOnErrorsPlugin(),
		new UglifyJsPlugin(),
		new webpack.optimize.AggressiveMergingPlugin(),
		new webpack.HotModuleReplacementPlugin()
	],
	module: {
		loaders: [
			{
				test: /\.jsx?$/,
				exclude: /(node_modules)/,
				loader: 'babel-loader',
				query: {
					presets: ['react','es2015']
				}
			}
		]
	}
};
.babelrc

Set the following in your file at the root directory.

{
 "presets": [ "es2015", "react" ]
}
Let’s Test Node & Webpack:

Open command prompt and navigate to our testApp folder and run “webpack”. You will notice that a “static” folder is created in our root directory. In it we will now see “home.js” file. Remember back above we set __init__.py static folder and in home.views.py file we said in render_template script_name “home.js”. This is how it all maps together.

Setup auth __init__.py:

This creates the blueprint that we have in app.__init__.py.

from flask import Blueprint

auth = Blueprint('auth', __name__)

#This is the views.py from the auth directory.
from . import views

Setup auth views.py:

from flask import flash, redirect, render_template, url_for
from flask_login import login_required, login_user, logout_user
from flask_wtf import FlaskForm
from wtforms import PasswordField, StringField, SubmitField, ValidationError
from wtforms.validators import DataRequired, Email, EqualTo
from .. import db, login_manager
from ..models import User

from . import auth

class RegistrationForm(FlaskForm):
    """
    Form for users to create new account
    """
    email = StringField('Email', validators=[DataRequired(), Email()])
    username = StringField('Username', validators=[DataRequired()])
    first_name = StringField('First Name', validators=[DataRequired()])
    last_name = StringField('Last Name', validators=[DataRequired()])
    password = PasswordField('Password', validators=[
                                        DataRequired(),
                                        EqualTo('confirm_password')
                                        ])
    confirm_password = PasswordField('Confirm Password')
    submit = SubmitField('Register')

    def validate_email(self, field):
        if User.query.filter_by(email=field.data).first():
            raise ValidationError('Email is already in use.')

    def validate_username(self, field):
        if User.query.filter_by(username=field.data).first():
            raise ValidationError('Username is already in use.')

class LoginForm(FlaskForm):
    """
    Form for users to login
    """
    email = StringField('Email', validators=[DataRequired(), Email()])
    password = PasswordField('Password', validators=[DataRequired()])
    submit = SubmitField('Login')

@login_manager.user_loader
def load_user(id):
    #This is the how we locate the user in our testApp database
    return User.query.get(int(id))

@auth.route('/register', methods=['GET', 'POST'])
def register():
    form = RegistrationForm()
    if form.validate_on_submit():
        user = User(email=form.email.data,
                            username=form.username.data,
                            first_name=form.first_name.data,
                            last_name=form.last_name.data,
                            password=form.password.data)

        # add user to the database
        db.session.add(user)
        db.session.commit()
        flash('You have successfully registered! You may now login.')

        # redirect to the login page
        return redirect(url_for('auth.login'))

    # load registration template
    return render_template('register.html', form=form, title='Register')

@auth.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        # check whether user exists in the database and whether
        # the password entered matches the password in the database
        user = User.query.filter_by(email=form.email.data).first()
        if user is not None and user.verify_password(
                form.password.data):
            # log user in
            login_user(user)

            # redirect to the dashboard page after login
            return redirect("/")

        # when login details are incorrect
        else:
            flash('Invalid email or password.')

    # load login template
    return render_template('login.html', form=form, title='Login')

@auth.route('/logout')
@login_required
def logout():
    """
    Handle requests to the /logout route
    Log an user out through the logout link
    """
    logout_user()
    flash('You have successfully been logged out.')

    # redirect to the login page
    return redirect(url_for('auth.login'))
Let’s Run our App:

Open command prompt navigate to our testApp folder and run “flask run”. If no mistakes were made you should now be able to navigate to our site. http://localhost:5000.

 

Resources:

In learning how to incorporate flask into python I used the following part one only tutorial as a guide. Very helpful.

HighCharts: Basic Graphing

This entry is part 1 of 2 in the series React: Highcharts

HighCharts is a pretty cool graphing package. Below is an example of how you can create an universal React class for a HighCharts graph.

You will need to install the package. At the time of this writing I am using 5.0.6.

You will also need to import HighCharts and require the charts.

import Highcharts from "highcharts/highcharts.js";
window.Highcharts = Highcharts;
require("highcharts/highcharts-more.js")(Highcharts);

In the state I hold these values to manage how the chart loads and displays data.

getInitialState: function() {
	return {
		chartSettings: null, //Holds the chart settings data
		loaded: false,	//Determines if the chart has been loaded
		chart: null,	//The chart
		data: [],	//The data to utilize for the chart. It's most likely in series format
	};
},

In the component methods check to see when the class has been loaded with data or reset if needed.

componentDidUpdate: function() {
	if (!this.state.loaded) { //The chart hasn't been loaded with data so load it and refresh the chart
		this.setState({
			loaded: true,
			data: this.props.data
		}, () => { this.chart(); });
	}
},
componentWillReceiveProps: function(newprops) {
	if (this.state.loaded && this.props != newprops) { //The chart has been loaded but the data has changed. Refresh the chart after
		this.setState({
			data: newprops.data
		}, () => { this.chart(); });
	}
},

The class the render method is how the chart assigns to the UI.

render: function() {
	return (<div id={this.props.id}></div>
); },

You can create a “chart” method. Which you can use to manage the display of the chart. The main section of it is how to display the chart after you have modified the chart settings. You could also utilize a props for controlling whether to show the loading or not. Totally up to you.

this.setState({
	loaded: true,			//The data and chart has been loaded
	chart: new Highcharts.Chart(chartSettings) //Set the chart
}, () => {
	if (!this.props.data.length == 0) { //If no data is present yet then show a loading image
		this.state.chart.showLoading();
		this.state.chart.redraw();
	} else {			//The data has been loaded.
		this.state.chart.hideLoading();
		this.state.chart.redraw();
	}
});

In the “chart” method you should clean up your existing chart before generating a new one.

if (this.state.chart !== null) {
	this.state.chart.destroy();
	this.state.chart = null;
}

There are so many ways of controlling the chartsettings. I will try to cover a vast majority of the options. The basic definition looks like this.

chartSettings = $.extend(true, {},
	this.props.chartSettings,
	{
		chart: {
			renderTo: this.props.id,	//The id you passed into the class
			backgroundColor: "",
            		type: this.props.chart_type,	//By passing in the chart type it will be open to various types of charts.
            		height: 500,            	//You can specify the height of the graph if you want.
            		zoomType: "xy",            	//If you want to be able to zoom.	
            	},
            	credits: {
			enabled: false	//Turns off the powered by
		},
            	title: {
                	text: this.props.title,
                	style: { color: "white" }
            	},
            	subtitle: {
                	text: this.props.sub_title
            	},
		tooltip: {
		},
		plotOptions: {
		},
		series: thisInstance.state.data
	});

Tooltip has various options. One I like to use is the “formatter” functionality. This will allow you to modify what is displayed on hover.

tooltip: {
	formatter: function(){
		var pointIndex = this.point.index;
		var seriesName = this.point.series.name;
	}
}

There is also xAxis option. You can do a variety of different things. Depending on how you create your graph determines what options you should use. The type in xAxis can have a few different options. I show you “datetime” below. But you can also choose “linear” which is numerical data as well as “category” which allows you to put string data on the X axis.

xAxis: {
	type: "datetime",
	categories: this.props.categories,
	title: {
		enabled: true,
	},
	showLastLabel: true,
	showFirstLabel: true,
	tickInterval: 15,		//I chose to increment to x value by 15 days. But you can choose whatever you want
	labels: {
		formatter: function () {
			if (the type is a date == "date") {
				return Highcharts.dateFormat("%m/%d", this.value);	//You can format however you like
			} else {
				return this.value;
			}
		}
	}
},

There is also yAxis option. You can do a variety of different things. Depending on how you create your graph determines what options you should use. Here is an example.

yAxis: {
	allowDecimals: true,
	title: {
		align: "high"
	},
	labels: {
		overflow: "justify",
		formatter: function() {
			return this.value;
		}
	},
},

You can add onClick events to series points if you want.

plotOptions: {
	series: {
		point: {
			events: {
				click: function(e){
				}
			}
		}
	}
}

There are various graph types. For example “pie”, “bar”, “scatter”, etc. Here are a few different examples of a basic setup.

plotOptions: {
	pie: {
		allowPointSelect: true,		//When you click the pie slice it moves out slightly
		cursor: "pointer",
		shadow: false,
		dataLabels: {
			enabled: true,
			formatter:function(){
			},
			color: "white",
			style: {
				textShadow: false 
			}
		}
	},
	bar: {
		dataLabels: {
			enabled: true,
			allowOverlap: true,	//Labels will overlap. Turns this off if you don't want your labels to overlap.
		}
	},
	scatter: {
		dataLabels: {
			crop: false,		//Labels will not be hidden
		},
		marker: {
			radius: 3,
			states: {
				hover: {
					enabled: true
				}
			}
		},
		states: {
			hover: {
				marker: {
					enabled: false
				}
			}
		}
	}
}

Highcharts: Add Custom Buttons

This entry is part 2 of 2 in the series React: Highcharts

If you’ve never used HighCharts for your graphing needs I highly suggest it. Very customizable and easy to use.

You will need to require the exporting requirements.

import Highcharts from "highcharts/highcharts.js";
window.Highcharts = Highcharts;
require("highcharts/modules/exporting")(Highcharts);

If you would like to add a custom button to your graph you can use the exporting section like below.

exporting: {
	buttons: {
		customButton: {
			text: "Button Text",
			onclick: function () {
			}
		},
	},
}

React: Export Module

Sometimes we need exportable modules for use through our applications. It is pretty straight forward to export a module.

NPM Installs:

npm install create-react-class --save
npm install prop-types --save
npm install react --save
npm install react-dom --save

Export:

 module.exports = {exportedModuleName:ModuleName};

exportedModuleName is the name that you use in other pages.
ModuleName is the name of the module to export.

The module will look something like this.
This one is just a TD module. But really you can do anything you want.

window.jQuery = window.$ = require("jquery"); 
import React from "react"; 
import ReactDOM from "react-dom";
var createReactClass = require('create-react-class');
var propTypes = require('prop-types');

var MyExportableModule = createReactClass({
      render: function() {
            return React.createElement("anyelement", {className: this.props.className, key: this.props.name}, this.props.fields);
      }
});

MyExportableModule.PropTypes = {
      name: React.PropTypes.string.isRequired,
      fields: React.PropTypes.array.isRequired,
      className: React.PropTypes.string
};

 

 

 

React: Page Layout

There are many aspects of React below is just a sample of how you could setup a ReactJs page. Look up what each section does.

Go here to review the React Life Cycle. It is important to review this and understand it so that you dont make mistakes during your development.

NPM Installs:

npm install create-react-class --save
npm install react --save
npm install react-dom --save

Class Setup:

window.jQuery = window.$ = require("jquery");
import React from "react";
import ReactDOM from "react-dom";
import "../css/newpage.css";
var createReactClass = require('create-react-class');

var NewPage = createReactClass ({
      getData: function() {
            var params = {};
            
            $.ajax({
                  url: "/my_web/service_method/",
                  dataType: "json",
                  data: params,
                  success: function(data) {
                        this.setState({
                              "data": data
                        }, () => { 
                              //If you want to do something after you get the data loaded
                        });
                  }.bind(this),
                  error: function(xhr, status, err) {
                        console.err("Bad");
                  }.bind(this)
            });
      },
      getInitialState: function() {
            return{
                  "data": [],
            };
      },
    componentDidMount: function() {
    },
    componentWillMount: function() {
          this.getData();
    },
      render: function() {

            return (
                  <div key="div">
                  </div>
            );
      }
});

ReactDOM.render(<NewPage/>, document.getElementById("app-container"));