SQLite basics

https://www.youtube.com/watch?v=jH39c5-y6kg

To access sqlite3 from the command line

sqlite3 db.db

On the command line,

sqlite3 db.db ‘select * from tab;’

in sqlite3

.schema

Then in sqlite3 you will get a prompt >

select * from table1;
select name from table1 where numbers >3 ;
select numbers from table1 where numbers > 3;
select * from table1 where numbers = 1;
update table1 set name = “Bill” where numbers = 4;
insert into table1(numbers,name) values (“4”, “shane”);
delete from table1 where numbers = 1;
delete * from table1 limit 2

———————————————————–

create table tab((one text, two int);
insert into tab values(“hello”, “10”);
select * from tabs;

To show headers

.headers on
select * from tab;

Exit sqlite3 with any of these

ctrl + d
.exit
.quit

sqlite3 with Python. Start with

import sqlite3

db = sqlite3.connect(‘books.db’)

cur.execute(“”create table if not exists books(
id integer primary key,
title text not null,
author text not null,
price real);””)
)

cur.execute(””Insert into books(id,title,auyhor,price)
values(‘1′,’Untold stories’, ‘Alan Bennett’, ‘17.49’)

db.connect()
db.close(()

————————————————-

create table if not exists beans (id integer primary key, name text, rating integer);
insert into beans values(1,’Exclusive blend’,’percolator’, 65);
or since id is auto incremented,
insert into beans(name,method,rating) values(‘Exclusive blend’,’percolator’, 65);
select * from beans;
select name, method, rating from beans;
select name, method, rating from beans order by desc;
or asc for ascending.
To get the top rating,
select name, method, rating from beans; order by rating desc limit 1;
select * from beans where name = ‘Exclusive blend’;

select * from beans
where name = ‘Exclusive blend’
order by rating desc
limit 1;

Some useful commandline commands.

cmd + spacebar type terminal to bring it up.

Adding text to a file from the commandline an example

echo “[<words>](https://<username>.github.io/<repo>)” >> <filename>
e.g.echo “[gh-pages](https://shanegibney.github.io/shapes)” >> README.md

Use one > to add to a file and two >> to replace content of a file.

Files can be deleted using the -rf flag where the r deletes everything recursively and f forcibly.

rm -rf <filename>

apropos is useful and man to get the man pages for a command and ls to list the file in a directory.

man ls

Press q to quit and enter to return to the terminal.

top shows the processes running live on your machine.

cat for concatenate before a filename shows the contents of a file.

cat <filename>

ls -a

history

Use the arrow up and arrow down to scroll through previously used commands without having to type them out.

Say you have a file in the home directory called shane so a file at shane/boilerplate/style.css and you want to copy cp or move it mv to shane/variablesinRAF go to the boilerplate directory and type,

cp style.css ~/variablesinRAF

where the home directory can be represented by ~.

You can also use cp for copy, mv for move and . for all.

kill followed by the number of the command you wish to kill.

Or preceeding the number with an exclamation mark.

touch followed by the file you wish to create. touch index.html or any file your wish to create. which contains the information we want to publish in gh-pages

Presuming you installed node,

install Node.js

run javascript file with

node <filename>
e.g node index.js

How to setup a PAT(Personal Access Token) a github repository and gh-pages

Setting up a github PAT (Personal Access Token):

Click on avatar photo on github.com

Goto settings on right

Goto Developer settings on left.

Set to no expiration or whatever you want.

And choose repo.

Give the token a name in the description box, any name, you choose.
,
Hit the Generate Token button.

Copy and paste token to a file. This the only chance you will have to do this.

Creating a github repository:

mkdir <directory> creates a directory
::
cd <directory> change directory

Locally on the command line in the directory of the files you want to push.

git init

git add .

git commit -m “initial commit”

git remote add origin https://github.com/<username>/<repo>.git

e.g. git remote add origin https://github.com/shanegibney/flash.git

git push -u origin<repo> main
e.g. git push -u originflash main

username: shanegibney
password: use the PAT here

Creating a gh-pages page:

From repo go to settings
Pages on left

From drop down menu select main branch.
0
Save

After a few minutes visit https://<username>.github.io/<repo>
e.g. https://shanegibney.github.io/flash

In README.md edit and commit changes to include [gh-pages](the gh-pages url)
e.g. [gh-pages](https://shanegibney.github.io/flash)

CRUD Tutorial Video part 4

part 4 of 4

Python Django 4 Crash Course For Beginners | Build a Student Management System | Web Development Part 4

# Register your models here.
admin.site.register(Question)
Next we need to create super user for the django admin
(51:52)
Dpython3 manage.py createsuperuser
Follow the prompts to create the super user, give it an email and a password.
With the development server on as before navigate to http://localhost:8000/admin
Login with the credential and add some data to the database.
When you return to the website you should see the data on the site. This is the ‘r’ in crud complete.
Be sure to turn off the development server before performing migrations.
We are now approximately half way through this tutorial. We will now do the second half.
(1:00:24)
Update views.py as follows
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from .models import Question

# Create your views here.
def index(request):
return render(request, 'questions/index.html', {
'questions': Question.objects.all()
})

def view_student(request, id):
question = Question.objects.get(pk=id)
return HttpResponseRedirect(reverse('index'))/code>
(1:01:52)
Add the last line in the app to urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('', views.view_question, name='view_question'),
]

Go to the font-awesome page copy the code snippet for the circle-info.
Add to the end of the table in index.html.
Go to https://bootswatch.com/zephyr/ and down to the dialog section.
Copy the modal code below the button.

Django CRUD Tutorial Video part 3

part 3 of 4

Python Django 4 Crash Course For Beginners | Build a Student Management System | Web Development

(23:12)

python3 manage.py runserver

Next we create a model for our data in
model.py in the app

from django.db import models

# Create your models here.
class Question(models.Model):
#level = models.CharField(max_length=10, default='HL')
year = models.PositiveBigIntegerField(default=2019)
question_number = models.PositiveIntegerField(default=1)
part = models.CharField(max_length=4, blank=True, null=True)
sub_part = models.CharField(max_length=3, blank=True, null=True)
ceist = models.TextField(default='What is...?')
answer = models.TextField(default= 'The answer is...?')
formula = models.TextField(default='F=ma')
definition = models.TextField(default='The definition of...')
topic = models.TextField(default='Mechanics')
sub_topic = models.TextField(default='momentum')
type = models.TextField(default='long question')
mark = models.PositiveIntegerField(default='4')

def __str__(self):
return f'Question: {self.year}Q.{self.question_number} {self.ceist}'

All except the first line ‘class’ need to be indented for python to work.
(28:03)
Next create a migration with
python3 manage.py makemigrations
To apply these changes to the underlying database
python3 manage.py migrate
Go to https://bootswatch.com/
Go down to the Zephry theme and download the bootstrap.min.css file.
(33:24)
Inside the app directory create a new directory called ‘static’
(33:32)
Inside that create a new directory called ‘css’ and then place the bootstrap.min.css file which you just downloaded in it.
(35:21)
Go to https://getbootstrap.com/docs/5.0/getting-started/introduction/
Scroll down to bundle and copy removing integrity…
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Physics Paper Management System</title>
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
<body>
<h1>
Hello, World
</h1>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" ></script>
</body>
</head>
</html>

(36:20)
Click on www.bootswatch.com and go down to the zephyr theme and click preview. Then go down to the Nav bar and copy the code from the top right.
Paste this in to the index.html file in your app and replace ‘nav bar’ with ‘Physics Management System’.
(40:20)
Add a copyright to the end of the file so that the current year shows with a bit of Javascript.
Remove the Hello world and add and add a light card from bootswatch.
But first go to www.bootswatch.com and go to zephyr theme scroll down to single card and click on the top right to get the code and copy it and paste into the file.
(41:05)
Go to https://cdnjs.com/libraries/font-awesome and copy the all link tag to the file just below the title.

(41:47)
Next go to https://hfontawesome.com/. Go to ‘start free’ ‘icons’ and search for ‘graduation cap’. Copy the code and paste it into the file. With fa-lg make it large and put an icon for list in front of ‘All Questions’ and a plus in front of “Add Questions’.
(Add an ‘if; statement to this template.
Add to the if loop as if the there is no questions in the database. use django admin admin
Again using www.bootswatch.com go to table and add the default table to the if statement.
Make sure the views.py file in the app is as follows,
from django.shortcuts import render
from .models import Question
# Create your views here.
def index(request):
return render(request, 'questions/index.html', {
'question': Question.objects.all()
})

The index.html file is as follows,
templates/questions/index.html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Physics Paper Management System</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
<body class="flex"d-flex-column-min-vh-100>
<nav class="navbar navbar-expand-lg bg-primary" data-bs-theme="dark">
<div class="container-fluid">
<a class="navbar-brand" href="#"><i class="fa-solid fa-graduation-cap fa-lg"></i> Physics Management System</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarColor01" aria-controls="navbarColor01" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarColor01">
<ul class="navbar-nav me-auto">
<li class="nav-item">
<li class="nav-item">
<a class="nav-link" href="#"><i class="fa-solid fa-list fa-lg"></i>All Questions</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#"><i class="fa-solid fa-plus fa-lg"></i>Add Question</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container-fluid">
<h3 class="m-4">All Questions</h3>
<div class="row">
<div class="col-12">
{% if questions %}
<div class="card bg-light ms-4 me-4 mb-4">
<div class="card-header">
<i class="fa-solid fa-list fa-lg"></i>
</div>
<div class="card-body">
<h4 class="card-title">Light card title</h4>
<p class="card-text">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Part</th>
</tr>
</thead>
<tbody>
{% for question in questions %}
<tr>
<td>{{ question.part }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</p>
</div>
</div>
{% else %}
<h5 class="alert alert-primary ms-4 me-4">No question in database to display</h5>
{% endif %}
</div>
</div>
</div>
<footer class="mt-auto mb-4">
<div class="text-center">
<span>
Copyright © <script>document.write(new Date().getFullYear())</script> Shane Gibney Physics
</span>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" ></script>
</body>
</head>
</html>

We need to add data to the database. We need to register our data with django admin
admin.py in the app should look like this,
from django.contrib import admin
from .models import Question

Django CRUD Tutorial part 2

Part 2 of 4

Python Django 4 Crash Course For Beginners | Build a Student Management System | Web Development

(9:40)

To create a django project type the following command in the project directory,

django-admin startproject physics_project

Replace the last word ‘physics_project’ with anything you like.

Run with the django project from the directory with manage.py file with,

python3 manage.py runserver

Then in your browser navigate to

http://localhost:8000

or the IP address is

127.0.0.1:8000

To stop the development server press ctrl+c.

Again in the directory containing manage.py

(14:22)

python manage.py startapp questions

The app is called ‘questions’ but you can call it anything you like.

Once we created this app we must install it into our project.

Go to settings.py and add ‘questions’ to bottom of the list of apps in the INSTALLED APPS list surrounded by a single comma and followed by a comma.

In settings.py

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

'questions',
]...

Open the views.py file inside the students app.

Define a function based view which takes a request as an argument. Add the name of the template.

(17:14)

views.py

from django.shortcuts import render

# Create your views here.
index(request):
return render(request, 'questions/index.html')

( 17:38 )

Inside the questions directory create a new directory called ‘templates’ and in that create a directory called ‘questions’ inside which you create a file called index.html.

(18:33)

templates/questions/index.html


<!DOCTYPE html>
<html lang="en">
<head>
<itle>Physics Paper Management System</title>
<body>
<h1>
Hello, World
</h1>
</body>
</head>
</html>

Add a new file which you create urls.py to the questions app.

In the main urls.py file beneath settings.py add , include and path('',include('questions.urls')) like this

(22:30)

urls.py

...
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
path('admin/', admin.site.urls),
path('',include('questions.urls'))
]
...

Run the server check everything is working. You should just see ‘Hello, world’ on the screen.

(23:12)

python3 manage.py runserver

Django CRUD Tutorial Video part 1

Part 1 of 4

Python Django 4 Crash Course For Beginners | Build a Student Management System | Web Development

Download Python3 from https://www.python.org/downloads/

and to confirm python was installed correctly run in the terminal

python3 --version

It should give

Python 3.11.3

or later.

Go to code.visualstudio.com to download VS Code.

In your home directory create a new directory,

mkdir physics-management-system

Then,

cd physics-management-system

Next create a virtual environment for your Django project.

python3 -m venv .env

Then in your project directory type the command

python3 -m venv venv

This should create a new directory called ‘env’.

Where the last ‘venv’ is the name of the virtual environment and can be anything you like.

Now that the virtual environment has been created to activate type the following in the project directory.

On MAC type

source venv/bin/activate

and to deactivate the virtual environment type,

deactivate

Django can be obtained from https://pypi.org

To install Django run from the terminal the command

pip install django

Presuming brew is installed on a macbook pro although this should work on any UNIX system. Go to the terminal with ctrl + space opens the search light and search for’terminal’. Then update brew and install the ‘tree’ command.
brew update
brew upgrade
brew install tree
tree -L 2

Static ip on Pi Zero

This tutorial explains how to get a static ip address on a Pi Zero not on a Pi Zero W with builtin wireless.The tutorial is from https://thepihut.com/blogs/raspberry-pi-tutorials/how-to-give-your-raspberry-pi-a-static-ip-address-update Simply add
interface eth0

static ip_address=192.168.0.10/24
static routers=192.168.0.1
static domain_name_servers=192.168.0.1

or for wired connection

interface wlan0

static ip_address=192.168.0.200/24
static routers=192.168.0.1
static domain_name_servers=192.168.0.1

to the beginning of $ sudo nano dhcpcd.conf in /etc/dhcpcdcd.conf

Where the static ip_address=… is the new static ip you are trying to create/24 don’t forget /24 at the end.
And the
static routers =… and the
static domain_name_servers= is the address of your router.

Sequelize ORM basics with Express and SQLite

Notes on ‘Introduction to Sequelize ORM on Node.js’ by Patrick Shroeder on plurasight.com (excellent course!)

Sequelize can be used with sql db’s. Create an entry point server.js to node.js,

touch server.js

Then create a package.json file,

$ npm init -y

Wrote to /Users/shanegibney/sequelizeORMCoursePlurasight/package.json:

{
“name”: “sequelizeORMCoursePlurasight”,
“version”: “1.0.0”,
“description”: “”,
“main”: “server.js”,
“scripts”: {
“test”: “echo \”Error: no test specified\” && exit 1″,
“start”: “node server.js”
},
“keywords”: [],
“author”: “”,
“license”: “ISC”
}

Install these npm packages,

$ npm i express sequelize sqlite3 -S

The -S flag saves them to package.JSON

Add to server.js

const express = require('express');
const Sequelize = require('sequelize');

const app = express();
const port = 8001;

app.listen(port, () => {
console.log('Running server on port ' + port);
})

Run with,

$ npm start

Add a Sequleize instance which is a constructor function,

const connection = new Sequelize('db', 'user', 'pass', {
host: 'localhost',
dialect: 'sqlite',
storage: 'db.sqlite',
operatorsAliases: false
})

the db, user and pass can be anything you want because the DB is local.

Next add,

connection
.authenticate()
.then(() => {
console.log('Connection to database established successfully.');
})
.catch(err => {
console.log('Unable to connect to the database: ', err);
})

and server.js should now be,

const express = require('express');
const Sequelize = require('sequelize');

const app = express();
const port = 8001;

const connection = new Sequelize('db', 'user', 'pass', {
host: 'localhost',
dialect: 'sqlite',
storage: 'db.sqlite',
operatorsAliases: false
})

connection
.authenticate()
.then(() => {
console.log('Connection to database established successfully.');
})
.catch(err => {
console.log('Unable to connect to the database: ', err);
})

app.listen(port, () => {
console.log('Running server on port ' + port);
})

Stop server with ctrl+c and restart it,

$ npm started

Hopefully you get the message,

Connection to database established successfully.

A new file should be added to the project directory called db.sqlite

Sequelize maps JavaScript object to SQL tables using Models.

Here is an example of a Model called user,

User = {
name: Sequelize.STRING,
bio: Sequelize.STRING
};

This gets mapped to a table in the database called Users, note the Model is called ‘User’ but the table is called ‘Users’. This table will be created by Sequelize.

We need to create a variable to hold our Model reference,

const User =
connection.define('User', {
name: Sequelize.STRING,
bio: Sequelize.TEXT
})

The second ‘User’ is our Model name. This is placed in server.js

Next we need to sync the Model with the database. We can call sync() on the define,

connection.define('User', {
name: Sequelize.STRING,
bio: Sequelize.TEXT
}).sync();

or more commonly on the connection,

connection.sync();

When invoking sync() we can pass it a optional objects such as logging, to see the raw sql commands being used,

connection.sync({
logging: console.log
});

In server.js chain the sync() method to the connection and pass it the logging option.

The authenticate method can be removed because calling sync() performs the same operation.

Restart server as before. This will also output the raw sql command. Extra columns are also created such as id, createdAt and updatedAt.

After sync() we can use the create method on the User Model to add data,

connection
.sync({
logging: console.log
})
.then(() => {
User.create({
name: 'Luke',
bio: 'Luke bio entry'
})
})
// .authenticate()
.then(() => {
console.log('Connection to database established successfully.');
})
.catch(err => {
console.log('Unable to connect to the database: ', err);
})

server.js is now,

const express = require('express');
const Sequelize = require('sequelize');

const app = express();
const port = 8001;

const connection = new Sequelize('db', 'user', 'pass', {
host: 'localhost',
dialect: 'sqlite',
storage: 'db.sqlite',
operatorsAliases: false
})

const User = connection.define('User', {
name: Sequelize.STRING,
bio: Sequelize.TEXT
})

connection
.sync({
logging: console.log
})
.then(() => {
User.create({
name: 'Luke',
bio: 'Luke bio entry'
})
})
// .authenticate()
.then(() => {
console.log('Connection to database established successfully.');
})
.catch(err => {
console.log('Unable to connect to the database: ', err);
})

app.listen(port, () => {
console.log('Running server on port ' + port);
})

To view the data,

$ sqlite3 db.sqlite
SQLite version 3.28.0 2019-04-16 19:49:53
Enter ".help" for usage hints.
sqlite> .tables
Users
sqlite> select * from Users;
1|Luke|Luke bio entry|2019-09-08 14:37:08.758 +00:00|2019-09-08 14:37:08.758 +00:00
sqlite> .headers ON
sqlite> select * from Users;
id|name|bio|createdAt|updatedAt
1|Luke|Luke bio entry|2019-09-08 14:37:08.758 +00:00|2019-09-08 14:37:08.758 +00:00
sqlite>

There is a Db browser for sqlite.

Primary keys uniquely indentify a row and also can be used for association with other tables.

UUID is a data type that stands for Universal Unique Identifier. Testing UUID inside out User Model. We will use ‘force’ which will drop the Users table first before creating it.

const User = connection.define('User', {
uuid: {
type: Sequelize.UUID,
primaryKey: true,
defaultValue: Sequelize.UUIDV4
},
name: Sequelize.STRING,
bio: Sequelize.TEXT
})

Without defaultValue that column will be null. This creates a unique identifier for the row instead of just an number as the primary key. We you vierw th data in the table now you will see a unqiue identifier for the primary key. It will look like this,

5f63759a-e176-4cbe-b2b3-082fed5e98fe

Using freezeTableName set to tru means that the name we give our Model will match the name we give our table. This prevents the default pluralization of the table name.

const connection = new Sequelize('db', 'user', 'pass', {
host: 'localhost',
dialect: 'sqlite',
storage: 'db.sqlite',
operatorsAliases: false,
define: {
freezeTableName: true
}
})

Turn off timestamps,

const User = connection.define('User', {
uuid: {
type: Sequelize.UUID,
primaryKey: true,
defaultValue: Sequelize.UUIDV4
},
name: Sequelize.STRING,
bio: Sequelize.TEXT
}, {
timestamps: false
})

Attribute qualifiers allow us to customise attributes before they become columns, these include primaryKey, defaultValue and allowNull. These objects are placed in the value field of an attributes name.

Adding a validator to the name attribute, you must make the value of the attribute an object

name: {…}

name: {
type: Sequelize.STRING,
validate: {
len: [3,]
}
},

so that name must have a minimum of 3 characters. len[3,10] additionally gives a maximum length on 10 but that is optional.

The validator ‘contains’ ensures the content contains a certain string and an error message also,

bio: {
type: Sequelize.TEXT,
validate: {
contains: {
args: ['foo'],
msg: 'Error: Field must contain foo'
}
}
}

We will create an Express route,

app.get('/', (req, res) => {
User.create({
name: 'Joe',
bio: 'Luke bio foo entry 2'
})
.then(user => {
res.json(user);
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

We can modify data before it gets to the database using hooks or lifecycle events. There are four of these,

beforeValidate: () => {
console.log('beforeValidate');
}

is used before validation.

afterValidate: () => {
console.log('afterValidate');
}

is used after validation.

beforeCreate: () => {
console.log('beforeCreate');
}

is used after validation and before the column data is created.

afterCreate: () => {
console.log('afterCreate');
}

is used after the column data is created.

To use a hook, add the property ‘hooks’ as a thrid parameter to the Model after all the attributes have been declared.

The first three types of hooks accept a parameter that represents the Model object. The afterCreate hook also accepts a parameter that represents the final response object. A common use case for hooks is when we need to hash a users password before it is sent to the database.

Next we will remove the validation for ‘name’ and ‘bio’ and change the ‘name’ attribute to ‘first’ and add another attribute called ‘last’ and also an attribute ‘full_name’. All of these are of type STRING.

const User = connection.define('User', {
uuid: {
type: Sequelize.UUID,
primaryKey: true,
defaultValue: Sequelize.UUIDV4,
allowNull: false
},
first: Sequelize.STRING,
last: Sequelize.STRING,
full_name: Sequelize.STRING,
bio: Sequelize.TEXT
}, {
hooks: {
beforeValidate: () => {
console.log('before validate');
},
afterValidate: () => {
console.log('after validate');
},
beforeCreate: () => {
console.log('before create');
},
afterCreate: () => {
console.log('after create');
}
}
})

Add a new user,

connection
.sync({
logging: console.log,
force: true
})
.then(() => {
User.create({
first: 'Luke',
last: 'Johnson',
bio: 'New bio entry here'
})
})
// .authenticate()
.then(() => {
console.log('Connection to database established successfully.');
})
.catch(err => {
console.log('Unable to connect to the database: ', err);
})

In the terminal you can see the responses to each of the lifecycle events or hooks.

Running server on port 8001
Executing (default): DROP TABLE IF EXISTS `User`;
Executing (default): DROP TABLE IF EXISTS `User`;
Executing (default): CREATE TABLE IF NOT EXISTS `User` (`uuid` UUID NOT NULL PRIMARY KEY, `first` VARCHAR(255), `last` VARCHAR(255), `full_name` VARCHAR(255), `bio` TEXT, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL);
Executing (default): PRAGMA INDEX_LIST(`User`)
Executing (default): PRAGMA INDEX_INFO(`sqlite_autoindex_User_1`)
before validate
Connection to database established successfully.
after validate
before create
Executing (default): INSERT INTO `User` (`uuid`,`first`,`last`,`bio`,`createdAt`,`updatedAt`) VALUES ($1,$2,$3,$4,$5,$6);
after create

We want to combine the first and last values into full_name. We will use the beforeCreate to accept a parameter that represents the user Model. Then inside the body of that function we will combine the user’s first and last name.

Here we use back ticks and string interpolation.

beforeCreate: (user) => {
user.full_name = `${user.first} ${user.last}`
console.log('before create');
},

In the sqlite database we can see that the full_name column contains the value ‘LukeJohnson’

server.js

const express = require('express');
const Sequelize = require('sequelize');

const app = express();
const port = 8001;

const connection = new Sequelize('db', 'user', 'pass', {
host: 'localhost',
dialect: 'sqlite',
storage: 'db.sqlite',
// operatorsAliases: false,
define: {
freezeTableName: true
}
})

const User = connection.define('User', {
uuid: {
type: Sequelize.UUID,
primaryKey: true,
defaultValue: Sequelize.UUIDV4,
allowNull: false
},
first: Sequelize.STRING,
last: Sequelize.STRING,
full_name: Sequelize.STRING,
bio: Sequelize.TEXT
}, {
hooks: {
beforeValidate: () => {
console.log('before validate');
},
afterValidate: () => {
console.log('after validate');
},
beforeCreate: (user) => {
user.full_name = `${user.first} ${user.last}`
console.log('before create');
},
afterCreate: () => {
console.log('after create');
}
}
})

app.get('/', (req, res) => {
User.create({
name: 'Joe',
bio: 'Luke bio foo entry 2'
})
.then(user => {
res.json(user);
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

connection
.sync({
logging: console.log,
force: true
})
.then(() => {
User.create({
first: 'Luke',
last: 'Johnson',
bio: 'New bio entry here'
})
})
// .authenticate()
.then(() => {
console.log('Connection to database established successfully.');
})
.catch(err => {
console.log('Unable to connect to the database: ', err);
})

app.listen(port, () => {
console.log('Running server on port ' + port);
})

Generate data with generatedata.com and create a file users.json.
Require this in to server.js with,

const _USERS = require('./users.json');

Change the User Model to match the users.json file,
we add this file to the Model using the method bulkCreate which accepts an array with an optional second argument. bulkCreate will create and accept multiple instance in bulk.

The User Model is now,

const User = connection.define('User', {
name: Sequleize.STRING,
email: {
type: Sequleize.STRING,
validate: {
isEmail: true
}
},
password: {
type: Sequelize.STRING,
validate: {
isAlphanumeric: true
}
}
})

Onto connection.sync() we can chain a .then() method calling bulkCreate on our Users Model and passing in _USERS which we created previously and add a success and error message to this.

Remove the force option as we do not want to remove the table after it is initially created. But initially we do want force to overwrite the previous table. Also commented out the logging message.

server.js

const express = require('express');
const Sequelize = require('sequelize');
const _USERS = require('./users.json');

const app = express();
const port = 8001;

const connection = new Sequelize('db', 'user', 'pass', {
host: 'localhost',
dialect: 'sqlite',
storage: 'db.sqlite',
// operatorsAliases: false,
define: {
freezeTableName: true
}
})

const User = connection.define('User', {
name: Sequelize.STRING,
email: {
type: Sequelize.STRING,
validate: {
isEmail: true
}
},
password: {
type: Sequelize.STRING,
validate: {
isAlphanumeric: true
}
}
})

app.get('/', (req, res) => {
User.create({
name: 'Joe',
bio: 'Luke bio foo entry 2'
})
.then(user => {
res.json(user);
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

connection
.sync({
// logging: console.log,
// force: true // set to true to drop table each time server runs
})
.then(() => {
User.bulkCreate(_USERS)
.then(users => {
console.log('Success adding users');
})
.catch(err => {
console.log(err);
})
})
.then(() => {
console.log('Connection to database established successfully.');
})
.catch(err => {
console.log('Unable to connect to the database: ', err);
})

app.listen(port, () => {
console.log('Running server on port ' + port);
})

Next we will use POST to send data to the DB. This won’t actually work at the moment but it gives the structure and API endpoint,

app.post('/post', (req, res) => {
const newUser = req.body.user;
User.create(newUser)
.then(user => {
res.json(user);
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

Remove the comment creating bulk users. We will create a new route called ‘findall’.

app.get('/findall', (req, res) => {
User.findAll()
.then(user => {
res.json(user);
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

Navigate in the browser to,

http://localhost:8001/findall

But what if want only a subset of users? Use the where operator.

Testing the where functionality,

app.get('/findall', (req, res) => {
User.findAll({
where: {
name: 'David'
}
})
.then(user => {
res.json(user);
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

To use the Op method we need to import it at the top of server.js

const Op = Sequelize.Op;

This will allow us to use filters on the queries.

To match all records that start with ‘Be’,

where: {
name: {
[Op.like]: 'Be%'
}
}

You can also find an entry by id,

app.get('/findOne', (req, res) => {
// User.findById('55')
//findById() has been replaced with findByPk() as of Sequelize v5
User.findByPk('55')
.then(user => {
res.json(user);
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

To update an item we first call the method update on the Model the parameters we are updating. In the second parameter we can use ‘where’.

After an update Express does not return the newly updated row only information on the number of rows updated. That is why we change the response from ‘users’ to ‘rows’.

app.put('/update', (req, res) => {
console.log("updating");
User.update({
name: 'Michael Keaton',
password: 'password'
}, {
where: {
id: 55
}
})
.then(rows => {
res.json(rows);
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

We need to use POSTMAN to test the /update route. The browser will not work. Remember to make a PUT request to http://localhost:8001/update in POSTMAN and check if the row with id 55 has updated using http://localhost:8001/findOne or look in the sqlite db itself from the command line.

To complete the CRUD operations, we will look at deleting data.

app.delete('/remove', (req, res) => {
User.destroy({
where: {
id: '50'
}
})
.then(user => {
res.send('User successfully deleted');
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

Again you will need to use POSTMAN and http://localhost:8001/remove with a DELETE request.

Server.js

const express = require('express');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
// const _USERS = require('./users.json');

const app = express();
const port = 8001;

const connection = new Sequelize('db', 'user', 'pass', {
host: 'localhost',
dialect: 'sqlite',
storage: 'db.sqlite',
// operatorsAliases: false,
define: {
freezeTableName: true
}
})

const User = connection.define('User', {
name: Sequelize.STRING,
email: {
type: Sequelize.STRING,
validate: {
isEmail: true
}
},
password: {
type: Sequelize.STRING,
validate: {
isAlphanumeric: true
}
}
})

app.get('/findall', (req, res) => {
User.findAll({
where: {
name: {
[Op.like]: 'Be%'
}
}
})
.then(user => {
res.json(user);
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

app.get('/findOne', (req, res) => {
// User.findById('55')//findById() has been replaced with findByPk() as of Sequelize v5
User.findByPk('55')
.then(user => {
res.json(user);
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

app.delete('/remove', (req, res) => {
User.destroy({
where: {
id: '50'
}
})
.then(user => {
res.send('User successfully deleted');
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

app.put('/update', (req, res) => {
console.log("updating");
User.update({
name: 'Michael Keaton',
password: 'password'
}, {
where: {
id: 55
}
})
.then(rows => {
res.json(rows);
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

app.post('/post', (req, res) => {
const newUser = req.body.user;
User.create(newUser)
.then(user => {
res.json(user);
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

connection
.sync()
.then(() => {
console.log('Connection to database established successfully.');
})
.catch(err => {
console.log('Unable to connect to the database: ', err);
})

app.listen(port, () => {
console.log('Running server on port ' + port);
})

Associations form relationships between tables. These relationships are used to create joins. Given two tables Users and Posts, each had a primary id and other attributes. In the Posts table add a column that holds the primary key of the user called UserId. Querying this the response would look like this,

[{
"id": 1,
"title": 'Post name',
"content": 'Post content',
"UserId": 2
"User": {
"name": 'Joe',
....other attributes...
}
}]

Above we have id, title, content from the Posts table and then UserId of 2 gives us the object User in the Users table where the UserId is 2 and so we get ‘Joe’.

Creating associations:

1. Define association between Models

Post.belongsTo(User);

This adds the UserId column to the Posts table and is added after we define the Models.

2. Add include property with value as associated model.

Whenever we execute a query on the Posts table we need to add an include property inside the query with the associated model as the value.

Post.findByPk('1', {
include: [User]
})

So if we wanted to fetch a single post by its id we would also include the User Model in an array like this,

include: [User]

This would return all of the user’s attributes. If however we wanted to only return certain attributes we could wrap the value of the attributes in an object like this,

include: [{
model: User
attributes: ['name'] //specify attributes here
}]

specifying the model and then the attributes.

Next we will create a new Model called Posts and create an association to User. In server.js we will remove all routes except the findAll route which we will edit.

Just before the routes we will create the new Model Post,

const Post = connection.define('Post', {
id: {
primaryKey: true,
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4
},
title: Sequelize.STRING,
content: Sequelize.TEXT
})

Then above the connection and sync() method we will add the associations,

Post.belongsTo(User);

Just after the sync() method we will add data to the Post table,

connection
.sync()
.then(() => {
Post.create({
UserId: 1,
title: 'First post',
content: 'post content 1'
})
})....]

We need a new route to view this data,

app.get('/allposts', (req, res) => {
Post.findAll({
include: [User]
})
.then(posts => {
res.json(posts);
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

Restart server and check this route.

The UserID column is referred to as a Foreign Key. It is automatically generated for us and is Pascal Cased. We may want to choose the name for our foreign key instead of the default. To do this we add an object as the second argument of our belongsTo method, with a key of foreignKey and a value of whatever we want to name of the foreign key to be,

Post.belongsTo(User, { foreignKey: 'userID'});

Replace the Post.belongsTo(..) in server.js with the above. Then we need to uncomment force set to true so that the tables get dropped. We will uncomment the bulkCreate so as to add the user.json data to the database.

An alias will renamed a Model, when it is used as an association this can be useful. In the current Post response we get back ‘User’ with their attributes.

[
{
"id": "d84c65ad-9b28-4fd6-bf94-ad64675d723b",
"title": "First post",
"content": "post content 1",
"createdAt": "2019-09-09T16:52:06.521Z",
"updatedAt": "2019-09-09T16:52:06.521Z",
"UserId": 1,
"User": {
"id": 1,
"name": "Declan",
"email": "neque.sed.sem@consectetuereuismod.com",
"password": "HOM40YJD6VF",
"createdAt": "2019-09-08T18:30:12.023Z",
"updatedAt": "2019-09-08T18:30:12.023Z"
}
}
]

But we can change User to UserRef. To this we need to add a key value pair to the object which is the second parameter in belongsTo().

Previously we had,

Post.belongsTo(User, {
foreignKey: 'userID'
});

This key value pair will have

as : 'UserRef'

So now we have,

Post.belongsTo(User, {
as : 'UserRef',
foreignKey: 'userID'
});

Additionally we need to make sure that anytime we add the user model to our query that we reference the alias using the ‘as’ property,

User.findByPk('1', {
include: [{
model: User,
as: 'UserRef'
}]
})

The route now becomes,

app.get('/allposts', (req, res) => {
Post.findAll({
include: [{
model: User,
as: 'UserRef'
}]
})
.then(posts => {
res.json(posts);
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

Also include the bulkCreate method and make sure force set to true. Also add Post.create with the foreign key column’s new name ‘userID’ instead of the default ‘UserId’,

.then(() => {
User.bulkCreate(_USERS)
.then(users => {
console.log('Success adding users');
})
.catch(err => {
console.log(err);
})
})
.then(() => {
Post.create({
userID: 1,
tiitle: 'First post',
content: 'post content 1'
})
})

Then restart server.

This should return,

[{
"id": "16184133-1df3-464b-bb21-e35f5654e19a",
"title": null,
"content": "post content 1",
"createdAt": "2019-09-10T15:48:06.485Z",
"updatedAt": "2019-09-10T15:48:06.485Z",
"userID": 1,
"UserRef": {
"id": 1,
"name": "Declan",
"email": "neque.sed.sem@consectetuereuismod.com",
"password": "HOM40YJD6VF",
"createdAt": "2019-09-10T15:48:06.479Z",
"updatedAt": "2019-09-10T15:48:06.479Z"
}
}]

We an see the foreignKey has been renamed to ‘userId’ and the associated table to ‘UserRef’.

Associations: One-to-One, One-to-Many and Many-to-Many

For One-to-One associations we use the belongsTo() or hasOne() methods. hasOne() is the same as belongsTo() except that it places the foreign key in the opposite table.

This example would place a foreignKey of PostId in the User table.

User.hasOne(Post);

One-to-One associations only return a single item.

One-to-Many associations use hasMany() and the syntax is,

User.hasMany(Post);

A user may have many posts, and each post has a foreignKey of UserId in the Post table. A query returns an array of posts for the individual posts the associated user has made.

In a Many-to-Many assoication we use belongsToMany(). This type of association is used on BOTH models,

User.belongsToMany(Post);
Post.belongsToMany(User);

This will create a join table that contains just two columns. Each column will contain the primary key reference for each of the Models. The response that we get back when either of these tables is included will always be an array items.

Next we will build a One-toMany association.

1. Define association between models using hasmany(),

Post.hasMany(Comment, {as : 'All_Comments'});

In this example a post can have many comments. When the association is created a foreignKey of PostId will be added to the Comments table. We can optionally add an alias or a foreignKey as a second parameter. When making arequest you must include the associated Model along with an alias if there is one.

Post.findByPk('1', {
include: Comment, as: 'All_Comments'
})

The response we get back when making a query on the Post table will comntain an array of all the comments that have been added.

{
"id": 1,
"title": 'Post name',
"content": 'Post content'
"All_Comments": [{
"the_comment": 'first'
}, {
"the_comment": 'hi'
}, {
"the_comment": 'sup yo'
}, ]
}

We will build an example. First remove the freezeTableName property so that we will get the pluralised table name.

We will remove the id field of UUID from Post. This will make it easier to reference a Post id when we create our comments.

Next we create a Comments Model which will have one attribute the_comment and it is of type STRING,

const Comment = connection.define('Comment', {
the_comment: Sequelize.STRING
})

Now create a new association for Post to Comments. Since a post can have many comments we will use hasMany()

Note: ERD is an Entity-Relationship Diagram
https://www.lucidchart.com/pages/ER-diagram-symbols-and-meaning

Now the route,

app.get('/singlepost', (req, res) => {
Post.findByPk('1', {
include: [{
model: Comment,
as: 'All_Comments',
attributes: ['the_comment'] // the attributes we want to get back
}]
})
.then(posts => {
res.json(posts);
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

server.js is now,

const express = require('express');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
const _USERS = require('./users.json');

const app = express();
const port = 8001;

const connection = new Sequelize('db', 'user', 'pass', {
host: 'localhost',
dialect: 'sqlite',
storage: 'db.sqlite',
// operatorsAliases: false,
// define: {
// enable or disable the default pluralisation of table names
// freezeTableName: true
// }
})

const User = connection.define('User', {
name: Sequelize.STRING,
email: {
type: Sequelize.STRING,
validate: {
isEmail: true
}
},
password: {
type: Sequelize.STRING,
validate: {
isAlphanumeric: true
}
}
})

const Post = connection.define('Post', {
// id: {
// primaryKey: true,
// type: Sequelize.UUID,
// defaultValue: Sequelize.UUIDV4
// },
title: Sequelize.STRING,
content: Sequelize.TEXT
})

const Comment = connection.define('Comment', {
the_comment: Sequelize.STRING
})

app.get('/allposts', (req, res) => {
Post.findAll({
include: [{
model: User,
as: 'UserRef'
}]
})
.then(posts => {
res.json(posts);
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

app.get('/singlepost', (req, res) => {
Post.findByPk('1', {
include: [{
model: Comment,
as: 'All_Comments',
attributes: ['the_comment'] // the attributes we want to get back
}]
})
.then(posts => {
res.json(posts);
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

//puts foreignKey userID in Post table
Post.belongsTo(User, {
as: 'UserRef',
foreignKey: 'userId'
});

Post.hasMany(Comment, {
as: 'All_Comments',
}); //foreignKey = PostId in Comment table

connection
.sync({
force: true
})
.then(() => {
User.bulkCreate(_USERS)
.then(users => {
console.log('Success adding users');
})
.catch(err => {
console.log(err);
})
})
.then(() => {
Post.create({
userId: 1,
title: 'First post',
content: 'post content 1'
})
})
.then(() => {
Post.create({
userId: 1,
title: 'Second post',
content: 'post content 2'
})
})
.then(() => {
Post.create({
userId: 2,
title: 'Third post',
content: 'post content 3'
})
})
.then(() => {
Comment.create({
PostId: 1,
the_comment: 'first comment'
})
})
.then(() => {
Comment.create({
PostId: 1,
the_comment: 'second comment here'
})
})
.then(() => {
console.log('Connection to database established successfully.');
})
.catch(err => {
console.log('Unable to connect to the database: ', err);
})

app.listen(port, () => {
console.log('Running server on port ' + port);
})

and running this should return,

{
"id": 1,
"title": "First post",
"content": "post content 1",
"createdAt": "2019-09-10T19:41:54.978Z",
"updatedAt": "2019-09-10T19:41:54.978Z",
"userId": 1,
"All_Comments": [
{
"the_comment": "first comment"
},
{
"the_comment": "second comment here"
}
]
}

so this returning a single post along with its two comments. We get back the associated userID but not any of the model’s data. To get the associated Model’s data we add to the route like this,

app.get('/singlepost', (req, res) => {
Post.findByPk('1', {
include: [{
model: Comment,
as: 'All_Comments',
attributes: ['the_comment'] // the attributes we want to get back
}, {
model: User,
as: UserRef
}]
})
.then(posts => {
res.json(posts);
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

Comment out force so that the tables do not get dropped and the .then() methods that add initial data, including bulkCreate().

This should return,

{
"id": 1,
"title": "First post",
"content": "post content 1",
"createdAt": "2019-09-10T19:50:29.542Z",
"updatedAt": "2019-09-10T19:50:29.542Z",
"userId": 1,
"All_Comments": [
{
"the_comment": "first comment"
},
{
"the_comment": "second comment here"
}
],
"UserRef": {
"id": 1,
"name": "Declan",
"email": "neque.sed.sem@consectetuereuismod.com",
"password": "HOM40YJD6VF",
"createdAt": "2019-09-10T19:50:29.538Z",
"updatedAt": "2019-09-10T19:50:29.538Z"
}
}

Many-to-Many association

1. Define a relationship between two Models

Use the belongsToMany() method. We need to define this association on both Models.

// foreignKey = ProjectId and UserID in UserProject table
User.belongsToMany(Project, {
as: 'Tasks',
through: 'UserProject'
});
User.belongsToMany(User, {
as: 'Workers',
through: 'UserProject'
});

So UserProject is a join table between Project and User. Notice that the table name UserProject does not get automatically pluralised.

2. Add include property with value as associated model.

To retrieving a user or project we just call a query method such as findByPk() and include the Project Model along with the alias. And we can ask for only certain attributes.

3. Add in optional attributes for either model.

User.findByPk(‘1’, {
include: Project, as: ‘Tasks’, attributes: [‘name’]
})

The response should be of the form,

{
"id": 1,
"name": 'Hakeem',
"email",
'1234@email.com'
"Tasks": [{
"name": 'project 1'
}, {
"name": 'project 4'
}]
}

The user with an id of 1 gets queried and includes an array of two projects that have been associated with this user and it includes the alias that we have given of tasks.

When creating a Many-to-Many or a One-to-Many association, Sequelize also provides getters and setters methods. These members allow us to perform CRUD operations on members of an association. There are seven get and set methods but only four are commonly used. These are ‘set’, ‘get’, ‘add’ and ‘remove’.

‘set’ is used when initially creating an association, and it accepts an array of id’s, setWorkers([]). For these methods the syntax is camel cased. So in ‘setWorkers’, the ‘set’ part of this is the name of the ‘setter’ or ‘getter’ followed by the name of the alias which is in this case, ‘Workers’. And that is how we get ‘setWorkers([])’.

‘add’ is used to add a single member to an association, addWorkers().

‘get’ can be used to find all associated members, getWorkers()

‘remove’ can be used to remove a single associated member removeWorkers().

Example syntax to set a new worker,

Project.create({
name: 'project name'
}).then((project) => {
project.setWorkers([1, 2]);
})

After creating a new project we then want to add a few new workers to this project. We can do this by chaining on the setWorkers([1, 2]) method to the response, passing in an array of id’s that we want to add.

Remember we are using ‘Workers’ as an alias for our User. Inside the UserProject table we will have two new columns added, ‘UserID and ProjectId’, associating two users with a project.

After a project is created we may want to add a new worker to the project. To do this we can use the addWorkers() method,

app.put('/addWorker', (req, res)
=> {Project.findByPk(4)
.then((project)=> {
project.addWorkers(5)
})...

This uses a put request and inside the function we first find a project by its id. Then we take that found project and chain on the addWorkers(5) method passing in the id of the worker we want to add.

Next we will create an example of a Many-to-Many association.

Create a new model called Project, with a single attribute of ‘title’ and type STRING.

const Project = connection.define('Project', {
title: Sequelize.STRING
})

Then create the belongsToMany() associations for a user belonging to many projects and a project belonging to many users. We add aliases ‘Tasks’ and ‘Workers’ to both associations, ‘Tasks’ for User and ‘Workers’ for Project. Remember that the UserProjects table gets created with ID’s of ProjectId and UserId columns,

// Creates a UserProjects table with ID's for the ProjectId and UserID
User.belongsToMany(Project, {
as: 'Tasks',
through: 'UserProjects'
});
Project.belongsToMany(User, {
as: 'Workers',
through: 'UserProjects'
});

Create two new projects chained to .sync()

connection
.sync({
// force: true
})
.then(() => {
Project.create({
title: 'project 1'
}).then((project) => {
project.setWorkers([4, 5]);
})
})
.then(() => {
Project.create({
title: 'project 2'
})
})...

The first project will have a title of ‘project 1’ and then we setWorkers 4 and 5 to be included with this project on creation. We also add in another project with the title of ‘project 2’ without setting any workers.

If we run the app now we should expect to see two new tables along with the new database entries.

Next we will look at adding a single worker to the project2. For this we need a new route,

app.put('/addWorker', (req, res) => {
Project.findByPk(2) //project has id of 2
.then((project) => {
project.addWorkers(5) //user has id of 5
})
.then(posts => {
res.send('User added');
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

and we need to use POSTMAN with the PUT method.

http://localhost:8001/addWorker

This should return the success method of ‘User added’. In the UserProjects table you should see a new row, which has a new user of id 5, has been added to project 2. That is UserId of 5 and ProjectID of 2.

Next how to query users and get back all their projects while filtering for attributes. For this we will create a new route,

app.get('/getUserProjects', (req, res) => {
User.findAll({
attributes: ['name'],
include: [{
model: Project,
as: 'Tasks',
attributes: ['title']
}]
})
.then(output => {
res.json(output);
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

This will find all users but only retrieve the attribute of ‘name’, then the model of Project, along with the alias that we have assigned of ‘Tasks’, while only getting back the attribute of title.

This gives all users, just their names as well as any associated project titles,

[
{
"name": "Declan",
"Tasks": []
},
{
"name": "Wallace",
"Tasks": []
},
{
"name": "Maisie",
"Tasks": []
},
{
"name": "Ayanna",
"Tasks": [
{
"title": "project 1",
"UserProjects": {
"createdAt": "2019-09-11T12:06:12.585Z",
"updatedAt": "2019-09-11T12:06:12.585Z",
"UserId": 4,
"ProjectId": 1
}
}
]
},
{
"name": "Rose",
"Tasks": [
{
"title": "project 1",
"UserProjects": {
"createdAt": "2019-09-11T12:06:12.585Z",
"updatedAt": "2019-09-11T12:06:12.585Z",
"UserId": 5,
"ProjectId": 1
}
},
{
"title": "project 2",
"UserProjects": {
"createdAt": "2019-09-11T16:49:17.227Z",
"updatedAt": "2019-09-11T16:49:17.227Z",
"UserId": 5,
"ProjectId": 2
}
}
]
},
{
"name": "Ishmael",
"Tasks": []
},
..............
{
"name": "Nash",
"Tasks": []
}
]

Server.js is,

const express = require('express');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
const _USERS = require('./users.json');

const app = express();
const port = 8001;

const connection = new Sequelize('db', 'user', 'pass', {
host: 'localhost',
dialect: 'sqlite',
storage: 'db.sqlite',
// operatorsAliases: false,
// define: {
// enable or disable the default pluralisation of table names
// freezeTableName: true
// }
})

const User = connection.define('User', {
name: Sequelize.STRING,
email: {
type: Sequelize.STRING,
validate: {
isEmail: true
}
},
password: {
type: Sequelize.STRING,
validate: {
isAlphanumeric: true
}
}
})

const Post = connection.define('Post', {
title: Sequelize.STRING,
content: Sequelize.TEXT
})

const Comment = connection.define('Comment', {
the_comment: Sequelize.STRING
})

const Project = connection.define('Project', {
title: Sequelize.STRING
})

app.get('/allposts', (req, res) => {
Post.findAll({
include: [{
model: User,
as: 'UserRef'
}]
})
.then(posts => {
res.json(posts);
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

app.get('/singlepost', (req, res) => {
Post.findByPk('1', {
include: [{
model: Comment,
as: 'All_Comments',
attributes: ['the_comment'] // the attributes we want to get back
}, {
model: User,
as: 'UserRef'
}]
})
.then(posts => {
res.json(posts);
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

app.put('/addWorker', (req, res) => {
Project.findByPk(2) //project has id of 2
.then((project) => {
project.addWorkers(5) //user has id of 5
})
.then(posts => {
res.send('User added');
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

app.get('/getUserProjects', (req, res) => {
User.findAll({
attributes: ['name'],
include: [{
model: Project,
as: 'Tasks',
attributes: ['title']
}]
})
.then(output => {
res.json(output);
})
.catch(error => {
console.log(error);
res.status(404).send(error);
})
})

//puts foreignKey userID in Post table
Post.belongsTo(User, {
as: 'UserRef',
foreignKey: 'userId'
});

Post.hasMany(Comment, {
as: 'All_Comments',
}); //foreignKey = PostId in Comment table

// Creates a UserProjects table with ID's for the ProjectId and UserID
User.belongsToMany(Project, {
as: 'Tasks',
through: 'UserProjects'
});
Project.belongsToMany(User, {
as: 'Workers',
through: 'UserProjects'
});

connection
.sync({
// force: true
})
.then(() => {
console.log('Connection to database established successfully.');
})
.catch(err => {
console.log('Unable to connect to the database: ', err);
})

app.listen(port, () => {
console.log('Running server on port ' + port);
})

Deploy Express.js on AWS

Following the tutorial: A Minimal Node.js and Express Server Setup and Deployment on dev.to

$ mkdir express_hello_world
$ cd express_hello_world/
$ npm init
$ npm install express
$ npm install g express
$ touch server.js

add to the file server.js

const express = require("express")
const app = express()
app.get("/", function(req, res) {
res.send("Hello World")
})
app.listen(3000)

Run the server locally

$ node server.js

and navigate in your browser to,

http://localhost:3000

Go to,

Select EC2

and “Launch Instance”

Select, Amazon Linux 2 AMI

Click “Review and Launch”

Click “Launch”

Choose “Create new key pair” as “express_hello_world”,

and “Download Key Pair” and then hit “Launch Instance”.

You should see,

Now we have downloaded the pem file which is the private key.

$ mkdir -p ~/Documents/AWS/keys/express_hello_world/
$ cd ~/Documents/AWS/keys/express_hello_world/
$ cp ~/Downloads/express_hello_world.pem ./
$ chmod 400 express_hello_world.pem
$ ssh -i "express_hello_world.pem" ec2-user@34.243.253.15

From the EC2 dashboard click “instances” to get the IPv4 Public IP of the AWS server,

This will get a bash prompt on the EC2 AWS instance,

In this prompt install node.js,

$ curl --silent --location https://rpm.nodesource.com/setup_8.x | sudo bash -
$ sudo yum -y install nodejs

Check the node version with,

$ node --version
v8.16.1

On the remote AWS server create a new user,

$ sudo useradd express_hello_world

Copy the application to AWS, so on the local machine,

$ cd ~/express_hello_world/
$ rm -rf node_modules/
$ sudo scp -r -i ~/Downloads/express_hello_world.pem ~/express_hello_world/ ec2 user@34.243.253.15:express_hello_world_app
$ sudo ssh -i ~/Downloads/express_hello_world.pem ec2-user@34.243.253.15
$ sudo chmod 600 ./express_hello_world_app/*
$ sudo chown -R express_hello_world:express_hello_world ./express_hello_world_app
$ sudo mv ./express_hello_world_app /home/express_hello_world/express_hello_world_app

more to come….

Subtraction binary numbers

Calculate 8D-3D

Convert to binary,

8D = 00001000B
3D = 00000011B

Because computers can only fundamentally add, we convert the 3D to its two’s complement and adding this to the 8D is the same as subtracting 3D.

To convert 3D to its two’s complement representation, first get the one’s complement. This simply means invert each digit so that 1’s become 0’s and 0’s become 1’s.

one’s complement of 3D = 00000011B

is 11111100B.

The twos’ complement of this means adding 1.

11111101B

Then add,

00001000B
+ 11111101B
__________
(1)00000101B

The leading (1) can be ignored,

101B=5D as expected.

Vue-cli add Bootstrap-vue

Install vue-cli. Here we’re using

$ vue –version (double hyphen)

3.8.3

$ vue create myproject

$ cd myproject

$ npm i

$ vue add bootstrap-vue

Check it out at https://bootstrap-vue.js.org/

Run the dev server with,

$ npm run serve

Navigate in the browser to,

localhost:8080

although the port may be different.

Test this by adding to /src/components/HelloWorld.vue an element with a specific bootstrap class, i.e.

<button class=”btn btn-info”>hit me</button>

Simple Node.js App

./lib/maths.js file:

var math = {};

math.random = function(min, max){  

var random =Math.floor(Math.random() * (+max – +min)) + +min; 

   return random;

}

math.sum = function(min, max){

        return min + max;

}

module.exports = math;

./index.js file:

var mathLib = require(‘./lib/maths’);

var app = {};

var min = 1;

var max = 10;

var randomNum = mathLib.random(min, max);

var sum = mathLib.sum(min,max);

console.log(‘Random number between ‘ + min + ‘ and ‘ + max + ‘: ‘ + randomNum);

console.log(‘Sum of ‘ + min + ‘ + ‘ + max + ‘ = ‘ + sum);

With node.js installed run,

$ node index.js

MEVN Stack

MEVN – mySQL Express Vue-cli 3.x. Node

These notes are adopted from an original tutorial https://www.youtube.com/watch?v=9Kju3DovLrg but to use vue-cli3.x.

node 10.15.0
npm 6.4.1
express 4.16.4
vue 3.3.0
mysql 8.0.13
phpmyadmin 8.0.13

MySQL-Express-Vue-cli3.x.-Node

Using phpmyadmin or from command line create a new MySQL database called ‘nodejs-tasks’.

Use user ‘root’ and add a password.

How to use mysql on the command line: http://www.shanegibney.com/shanegibney/notes-on-apache-php-mysql-on-mojave/

and also http://www.shanegibney.com/shanegibney/connect-to-mysql-database-from-commandline/

Create a table called tbl_tasks with just two columns.
Id (int) auto-increment

task_name (text)

Create a home directory for this project,

$ mkdir MEVN-MySQL-Express-Vue-Node

and cd into it,

$ cd MEVN-MySQL-Express-Vue-Node

In the project’s home directory MEVN-MySQL-Express-Vue-Node run

$ npm init

This will create a package.json files

Add dependencies,

$ npm install body-parser cors express mysql2 nodemon sequelize –save

The package.json should now look like this,

{ "name": "mevn-mysql-express-vue-node", 
"version": "1.0.0",
"description": "mysql express vue-cli node CRUD app",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "me",
"license": "ISC",
"dependencies": {
"body-parser": "^1.18.3",
"cors": "^2.8.5",
"express": "^4.16.4",
"mysql2": "^1.6.4",
"nodemon": "^1.18.9",
"sequelize": "^4.42.0"
}
}

Install these dependencies,

$ npm i

This should create an additional directory node_modules in your project’s home directory.

Create a new file server.js

$ touch server.js

This file needs to be,

var express = require("express") 
var bodyParser = require("body-parser") 
// var tasks = require("./routes/tasks") 
var cors = require("cors") 
var port = 3000 
var app = express() 
app.use(cors()) 
app.use(bodyParser.json()) app.use(bodyParser.urlencoded({extended: false})) 
// app.use("/api", tasks) app.listen(port, function(){ console.log('Server started on port ' + port) } )

In the project’s home directory create a new directory, change directory into it (cd) and then create a new file db.js

$ mkdir database
$ cd database
$ touch db.js

Into db.js put

const Sequelize = require("sequelize") 
const db = {} const sequelize = new Sequelize("database_name_goes_here", "root", "password-goes-here", {   
host: "localhost",   
dialect: "mysql",   
operatorsAliases: false,   
pool: {     
    max: 5,     
    min: 0,     
    acquire: 30000,     
    idle: 10000   
} 
}) db.sequelize = sequelize // db.sequelize = sequelize module.exports = db

Remember to put in the database password.

Sequelize is a promise-based ORM for Node.js v4 and up

In the project’s home directory create a directory called model and place inside it a file called Task.js

$ mkdir model
$ cd model
$ touch Task.js

const Sequelize = require("sequelize") 
const db = require("../database/db.js") 
module.exports = db.sequelize.define(   
"tbl_tasks",   {     
id: {       
type: Sequelize.INTEGER,       
primaryKey: true,       
autoIncrement: true     
},     
task_name: {       
type: Sequelize.STRING     
},     
topic: {       
type: Sequelize.STRING     
},     
sub_topic: {       
type: Sequelize.STRING     
}   
},   
{     
timestamps: false    
} 
) 

Again in the project’s home directory create a directory called routes and in it place a file called tasks.js

$ mkdir routes
$ cd routes
$ touch tasks.js

var express = require("express") 
var router = express.Router() 
const Task = require("../model/Task") 
//Get task 
router.get("/tasks", (req, res) => {   
Task.findAll()   
.then(tasks => {     
res.json(tasks)   
})   
.catch(err => {     
res.send("error: " + err)   
}) 
}) 
//Add task 
router.post("/task", (req, res) => {   
if(!req.body.task_name){     
res.status(400)     
res.json({       
error: "Bad Data for POST"     
})   
}else{     
Task.create(req.body)     
.then(() => {       
res.send("Task Added")     
})     
.catch(err => {       
res.send("Error: " + err)     
})   
} 
}) 
//Delete task 
router.delete("/task/:id", (req, res) => {   
Task.destroy({     
where: {       
id: req.params.id     
}   
})   
.then(() => {     
res.send("Task Deleted!")   
})   
.catch(err => {     
res.send("error: " + err)   
}) 
}) 
//Update task 
router.put("/task/:id", (req, res) => {   
if(!req.body.task_name) {     
res.status(400)     
res.json({       
error: "Bad Data..."     
})   
}else{     
Task.update(       
{         
task_name: req.body.task_name,         
topic: req.body.topic,         
sub_topic: req.body.sub_topic       
},       
{         
where: {           
id: req.params.id         
}       
}     
)       
.then(() => {          
res.send("Task Updated")       
})       
.error(err => res.send(err))   
} 
}) 
module.exports = router 

Next create a script called start to run server.js which will use nodemon. Nodemon will automatically restart the server if any changes are made. This is very useful in production and saves you having to stop and start the server every time you change something.

In package.json add to scripts “start” like this,

"scripts": {   
start": "nodemon server.js",   
test": "echo \"Error: no test specified\" && exit 1" },

Then to start server running,

$ npm start

The POST part of tasks.js is as follows,

//Add task 
router.post("/task", (req, res) => {   
if(!req.body.task_name){     
res.status(400)     
res.json({       
error: "Bad Data for POST"     
})   
}else{     
Task.create(req.body)     
.then(() => {       
res.send("Task Added")     
})     
.catch(err => {       
res.send("Error: " + err)     
})   
} 
})

module.exports = router

Must use x-www-form-urlencoded radio button option under ‘Body’ in Postman.

In Postman add a POST request to http://localhost:3000/api/task and add a key of task_name and value of for example ‘blah blah’.

In Postman test for GET with http://localhost:3000/api/tasks

In tasks.js the ability to delete by id,

//Delete task 
router.delete("/task/:id", (req, res) => {   
Task.destroy({     
where: {       
id: req.params.id     
}   
})   
.then(() => {     
res.send("Task Deleted!")   
})   
.catch(err => {     
res.send("error: " + err)   
}) 
})

Test it in Postman using DELETE and the url http://localhost:3000/api/task/1 to delete the entry with id 1, and use JSON in the Postman dropdown menu.

For updating the data,

//Update task 
router.put("/task/:id", (req, res) => {   
if(!req.body.task_name) {     
res.status(400)     
res.json({       
error: "Bad Data..."     
})   
}else{     
Task.update(       
{         
task_name: req.body.task_name,         
topic: req.body.topic,         
sub_topic: req.body.sub_topic       
},       
{         
where: {           
id: req.params.id         
}       
}     
)       
.then(() => {          
res.send("Task Updated")       
})       
.error(err => res.send(err))   
} 
}) 

In Postman test with PUT and http://localhost:3000/api/task/2 to update the entry with id 2, and in the key add task_name and then a string in value.

Next add the frontend framework Vue-cli3. This will create a new directory called ‘client’

To start a project in the old vue-cli you used ‘vue init’ but the new vue/cli use instead ‘vue create project_name ‘. This will enable you to either use a preset or custom config. The new cli is heavily plugin driven. Plugins such as ESLint, PWA etc. This can then be used as a new preset for future projects. After the project has been set up you add plugins with,

$ vue add @vue/plugin-name

The old cli was installed with

$ sudo npm install -g vue-cli (Don’t use!!!)

The new vue/cli is installed globally with

$ sudo npm install -g @vue/cli

This will replace the vue binary which you used to run vue init.

To check the version,

$ vue –version
3.3.0

To create a new project, (In old cli to create projects you used $ vue init project_name)

$ vue create project_name

babel allows you to use next generation Javascript features. Best to select ESLint default and dedicated config files. But router does need to be installed and best to do it when project is being created.

Note: in your computer’s home directory a hidden file .vuerc should have been created. This is the global configuration for the new vue/cli but nothing needs to be done with it here.

Navigate into the newly create directory project_name

$ cd project_name

To run the development server use the run ‘serve’, where ‘serve’ is an automatically generated script which can be found in the project’s home directory in package.json. This script runs the development server.

$ npm run server

If git is not installed you will get errors which can be ignored. git can be easily installed globally with,

$ npm install -g git

You may need to place sudo before every npm command. To avoid this change the permissions on ~/.npm with,

$ sudo chown -R $(whoami) ~/.npm

Install axios and bootstrap

$ sudo npm install –save bootstrap axios

This will add a bootstrap and an axios directory to node_modules in your project.

It may be necessary to also,

$ sudo npm install

You need to add vue.config.js in the project’s root directory next to package.json.
(For more on this see https://cli.vuejs.org/config/#devserver-proxy)

So vue.config.js becomes,

// vue.config.js
module.exports = {
  devServer: {
    proxy: {
      '^/api': {
        target: 'http://localhost:3000',
        changeOrigin: true
      }
    }
  },
  lintOnSave: false
}

Just after the other imports add to client/src/main.js

require(‘../node_modules/bootstrap/dist/css/bootstrap.css’)

The major difference between require and import, is that require will automatically scan node_modules to find modules, but import, which comes from ES6, won’t.

Remove the logo from src/views/Home.vue line,

Vue logo

In src/router.js add the route for the new List component,

import Vue from 'vue' 
import Router from 'vue-router' 
import List from '@/components/List' 
Vue.use(Router) export default 
new Router({   
routes: [     
{       
path: '/',       
name: 'List',       
component: List     
}   
] 
})

Create a new component by adding this file src/components/List.vue

<template>
  <div class="hello">
     <div id="todo-list-example" class="container">
        <div class="row">
           <div class="col-md-10 mx-auto">
              <h1 class="text-center">Analysis</h1>
              <form v-on:submit.prevent="addNewTask">
              <div class="row">
                <div class="col-md-3">
                   <label for="tasknameinput">Task Name</label>
                   <input v-model="taskname" type="text" id="tasknameinput" class="form-control" placeholder="Add New Task">
                </div>
                <div class="col-md-3">
                   <label for="topicnameinput">Topic</label>
                   <input v-model="topicname" type="text" id="topicnameinput" class="form-control" placeholder="Add New Topic">
                </div>
                <div class="col-md-2">
                   <label for="subtopicnameinput">Sub-Topic</label>
                   <input v-model="subtopicname" type="text" id="subtopicnameinput" class="form-control" placeholder="Add New Sub-Topic">
                </div>

                <div class="col-md-2">
                  <label for="topicname">topic</label>
                  <select v-model="selectedDrink" @change="selectDrink" id="topicname" class="form-control">
                    <option v-for="(drink,index) in drinks" :value="index">{{ drink.label }}</option>
                  </select>
                </div>

                <div class="col-md-2">
                  <label for="subtopicname">sub-topic</label>
                  <select v-model="selectedOption" v-if="selectedDrink != -1" id="subtopicname" class="form-control">
                    <option v-for="option in drinks[selectedDrink].options">{{ option }}</option>
                  </select>
                </div>
              <!-- <span>Selected: {{ selected }}</span> -->
              </div>
                 <button v-if="this.isEdit == false" type="submit" class="btn btn-success btn-block mt-3">
                    Submit
                 </button>
                 <button v-else v-on:click="updateTask()" type="button" class="btn btn-primary btn-block mt-3">
                    Update
                 </button>
              </form>

              <table class="table">
                <tr>
                  <th>Task Name</th>
                  <th>Topic</th>
                  <th>Sub Topic</th>
                </tr>
                 <tr v-for="(todo) in todos" v-bind:key="todo.id" v-bind:title="todo.task_name" v-bind:edit_topic="todo.topic" v-bind:edit_subtopic="todo.sub_topic" >
                    <td class="text-left">{{todo.task_name}}</td>
                    <td class="text-left">{{todo.topic}}</td>
                    <td class="text-left">{{todo.sub_topic}}</td>
                    <td class="text-right">
                       <button class="btn btn-info" v-on:click="editTask(todo.task_name, todo.topic, todo.sub_topic, todo.id)">Edit</button>
                       <button class="btn btn-danger" v-on:click="deleteTask(todo.id)">Delete</button>
                    </td>
                 </tr>
              </table>
           </div>
        </div>
     </div>
  </div>
</template>

<script>
import axios from 'axios'
export default {
name: 'List',
  data() {
     return {
           drinks:[
         {
           label:"Beer",
           options:["Sam Adams","Anchor Steam","St. Arnold"]
         },
         {
           label:"Soda",
           options:["Pepsi","Coke","RC"]
         },
         {
           label:"Coffee",
           options:["Starbucks","Dunkin Donuts","Gross Hotel Room"]
         }
       ],
       selectedDrink:-1,
       selectedOption:'',
        todos: [],
        id: '',
        taskname: '',
        topicname: '',
        subtopicname: '',
        isEdit: false
     }
  },
  mounted () {
     this.getTasks()
     //this.sortDynamicDropDownMenu()
  },
  methods: {
    selectDrink:function() {
       this.selectedOption = '';
     },
    sortDynamicDropDownMenu(){
       //console.log("qwertyui")
       // console.log("hello we are result.data - this.todos[0].task_name; " + this.todos[0].task_name )
       for (var i = 0; i < this.todos.length; i++) {
         console.log(this.todos[i].task_name);
       }
    },
     getTasks(){
        axios.get("/api/tasks").then(
           result => {
              console.log(result.data)
              this.todos = result.data
              this.sortDynamicDropDownMenu()
           },
           error => {
              console.error(error)
           }
        )
     },
     addNewTask() {
         axios.post("/api/task", {task_name: this.taskname, topic: this.topicname, sub_topic: this.subtopicname})
         .then((res) => {
             this.taskname = ''
             this.topicname = ''
             this.subtopicname = ''
             this.getTasks()
         }).catch((err) => {
             console.log(error)
         })
     },
     editTask(title, edit_topic, edit_subtopic, id){
        this.id = id
        this.taskname = title
        this.topicname = edit_topic
        this.subtopicname = edit_subtopic
        this.isEdit = true
     },
     updateTask() {
        axios.put(`/api/task/${this.id}`, {task_name: this.taskname, topic: this.topicname, sub_topic: this.subtopicname})
        .then((res) => {
           this.taskname = ''
           this.topicname = ''
           this.subtopicname = ''
           this.isEdit = false
           this.getTasks()
           console.log(res)
        })
        .catch((err) => {
          console.log(err)
        })
      },
      deleteTask(id){
         axios.delete(`/api/task/${id}`)
            .then((res) => {
            this.taskname = ''
            this.getTasks()
            console.log(res)
            })
            .catch((err) => {
                console.log(err)
            })
      }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
th {
   text-align: center;
}
td, tr {
   text-align: center;
}
</style>
   

Navigating to localhost:8080 should allow you to see the dashboard.
Of course the mysql server and Express server needs to also be running.

To run Express.js in the same directory at server.js run

$ node server.js

In the client directory run vue with,

$ npm run server

To run the mysql from the commandline,

$ sudo mysql -u root -p

To view the databases,

mysql> show databases;

Selecta database to use,

mysql> use tbl_tasks;

View the tables in that database,

mysql> show tables;

View the contents of that table,

mysql> select * from tbl_tasks;

If everything is working, any changes made on the frontend should be visible here in the database.

Apache, PHP, MySQL, & phpMyAdmin on MacOS Mojave

Install phpmyadmin by downloading the MySQL Community Server dmg file for mac. See https://dev.mysql.com/downloads/mysql/

Move this to /Library/WebServer/Documents

You will be able to access it at http://localhost/phpmyadmin but usually the http:// is not necessary.

However this will not work until you have php running with Apache. We will look at how to do that below.

Find the apachectl (command control service) command’s path,

$ which apachectl

returns,

/usr/sbin/apachectl

To restart, stop or start the apachectl server use.

$ sudo /usr/sbin/apachectl -k ( restart || stop || start )

The -k flag will give additional information when the command runs.

Check the $PATH variable to see that the command has been added to it,

$ echo $PATH

If apachectl is not in $PATH just add it to end of the ~/.bash_profile file. Open it with,

$ sudo nano ~/.bash_profile

and add,

/usr/sbin/apachectl

If you don’t add this just continue to use the full path to the command,

$ sudo /usr/sbin/apachectl -k ( restart || stop || start )

The apachectl process can be found using,

$ ps aux | grep httpd

Note that ‘httpd’ is the Apache process.

Also useful sometimes is,

$ sudo launchctl unload -w /System/Library/LaunchDaemons/org.apache.httpd.plist

To find the Apache version,

$ httpd -v

You can check the basic configuration with,

$ apachectl configtest

PHP

Check which version of php is installed,

$ php -v

php 7.1.19 should come preinstalled with MoJave.

To open the PHP interactive shell,

$ php -a

and then type,

phpinfo();

This will give you all information about php on your system, including the php version number.

To activate PHP7 with Apache, in /etc/apache2/httpd.conf uncomment the line (no # at the beginning),

LoadModule php7_module libexec/apache2/libphp7.so

Don’t forget to restart the Apache server,

$ sudo /usr/sbin/apachectl -k start

If you get,

Thu Jan 03 18:53:33.992705 2019] [so:warn] [pid 15334] AH01574: module php7_module is already loaded, skipping
httpd (pid 8355) already running

This suggests that I am loading the module twice.

So comment out the line

LoadModule php7_module        /usr/local/php5/libphp7.so

in /etc/apache2/other/+php-osx.conf

if it is not commented out.

Browsing to localhost in the browser should look for index.html in your default system document root,

/Library/WebServer/Documents/index.html

But you could also put a file index.php there to see if php is running. Try,

<?php 
echo 'Hello World!!!';
?>

or

<?php 
phpinfo();
?>

In this case in the browser navigate to localhost/index.php

When logging in to phpmyadmin from the brwoser at localhost/phpmyadmin you may get the error,

mysqli_real_connect(): (HY000/2002): Connection refused

Go to ‘System Preferences’ and click on the mysql icon which is usually at the bottom of this window. There you will be able to turn on or off the mysql server. If this turns off automatically almost immediately after you turn it on then you need to ‘initialize database’. Then you can check it from the command line with,

$ mysql -u root -p

and you will be prompted for the password.

If when you navigate in the browser to http://localhost/phpmyadmin and you still get errors, then you may need to go to /Library/WebServer/Documents and copy,

$ sudo cp config.sample.inc.php config.inc.php

and change,

$cfg['Servers'][$i]['host'] = 'localhost';

to

$cfg['Servers'][$i]['host'] = '127.0.0.1';

Then in the terminal access mysql with,

$ mysql -u root -p

and give password when prompted to do so.

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password_goes_here';

replace the password with your own.

The mysql server should restart automatically but to be sure on mac go to ‘System Preferences’ and click on the MySQL icon, open it and then ‘Stop MySQL Server’.

Then when you return to http://localhost/phpmyadmin you should be able to login successfully.

Convert csv to json with Python

Read in file.csv

name,age
bob,10
bill,12
matt,20
will,30
sid,40

using python script.py

#!/usr/bin/python
import csv, json

csvFilePath = "file.csv"
jsonFilePath = "file.json"

#read the csv and add the data to a dictionary

data = {}
with open (csvFilePath) as csvFile:
    csvReader = csv.DictReader(csvFile)
    for csvRow in csvReader:
        id = csvRow["id"]
        data[id] = csvRow

print(data)

# write the data toa json file
with open(jsonFilePath, "w") as jsonFile:
    jsonFile.write(json.dumps(data, indent = 4))

If you need to convert to an array of objects,

#!/usr/bin/python
import csv , json

csvFilePath = "file.csv"
jsonFilePath = "file.json"
arr = []
#read the csv and add the arr to a arrayn

with open (csvFilePath) as csvFile:
    csvReader = csv.DictReader(csvFile)
    print(csvReader)
    for csvRow in csvReader:
        arr.append(csvRow)

print(arr)

# write the data to a json file
with open(jsonFilePath, "w") as jsonFile:
    jsonFile.write(json.dumps(arr, indent = 4))

file.json

[
    {
        "age": "10", 
        "name": "bob"
    }, 
    {
        "age": "12", 
        "name": "bill"
    }, 
    {
        "age": "20", 
        "name": "matt"
    }, 
    {
        "age": "30", 
        "name": "will"
    }, 
    {
        "age": "40", 
        "name": "sid"
    }
]

VirtualBox (VM), Vagrant and Ubuntu

Install VirtualBox and Vagrant from their respective sites. To check they have been installed,

$ VBoxManage --version
5.2.18r124319
$ vagrant --version
2.1.5

In the normal terminal start vagrant. This will also create an Ubuntu box,

$ vagrant up

This also starts the Ubuntu box. We can connect to it using ssh,

$ vagrant ssh 

This logs us into the VM (virtual machine) which is running Ubuntu. The default user is called ‘vagrant’. Logout with ‘exit’. Stop the VM in the local terminal (not in the VM) with,

$ vagrant halt

We can completely remove the VM with,

$ vagrant destroy

This does not remove vagrant.

Next we’ll use a vagrant VM to run Ubuntu.

$ vagrant up

This logs use in as vagrant which is not the root user. To sudo to root,

$ sudo su -

With vagrant running ($ vagrant up) and ssh into it ($ vagrant ssh),

To Completely uninstall Node

I found that,

$ brew uninstall node

or

$ brew --force uninstall node

would not uninstall node as,

$ node -v

would still give a version number.

To completely uninstall node + npm do the following:

  • In /usr/local/lib and delete any node and node_modules
  • In /usr/local/include and delete any node and node_modules directory
  • If you installed with brew install node, then run ‘brew uninstall node’ in your terminal and then
    check your Home directory for any local or lib or include folders, and delete any node or node_modules from there
  • In /usr/local/bin delete any node executable

and also,

$ sudo rm -rf /opt/local/bin/node /opt/local/include/node /opt/local/lib/node_modules
$ sudo rm -rf /usr/local/bin/npm /usr/local/share/man/man1/node.1 /usr/local/lib/dtrace/node.d

To install on MacOS,

$ curl "https://nodejs.org/dist/latest/node-${VERSION:-$(wget -qO- https://nodejs.org/dist/latest/ | sed -nE 's|.*>node-(.*)\.pkg.*|\1|p')}.pkg" > "$HOME/Downloads/node-latest.pkg" && sudo installer -store -pkg "$HOME/Downloads/node-latest.pkg" -target "/"

Vue Routes

Full code on github and implemmented on gh-pages

‘vue-router’ is a separate package to Vue and is installed by ‘webpack’.

Routes are set up in router/index.js

Import to Router from vue-router which is in node_modules.

import Router from 'vue-router'
... new Router...

creates an instance of Router which is an object with a property ‘routes’ which is an array of object. Each of those objects is a route with properties for ‘path’, ‘name’ which is what it is called and ‘component’, the component that is called when the route is used.

{
      path: '/about',
      name: 'About',
      component: About
    }

We need to import the component and we use the @ which brings the path to the root of the project which is the ‘src’ directory.

import About from '@/components/About'

The component gets loaded into the root component App.vue using,

<router-view/>

and where that tag is in the App.vue. This would allow us to place a navbar, header and footer for example around this About component. So that when you go to …/about only the about component in the page changes and the whole page doesn’t refresh.

Next create a NavBar component and place it in the App.vue template.

This will work,

<a href="/about">About</a>

however is vue it should be done like this,

<router-link to="/about>About</router-link>

When <router-link> is rendered to the browser it becomes <a> so in the css we still reference it as ‘a’ not as ‘router-link’.

We can data bind using the ‘to’ attribute in <router-link> like this,

:to={ name: 'About'}

where ‘name’ is the property in the ‘routes’ array of object in the main.js routes. This makes the route dynamic.

Using this data bind to change to ‘/about’ to something else such as ‘abt’ you only need to change,

path: 'abt',

in the routes array of objects.

Next setting up a route that has a unique user id which will change data in a component to reflect this. The router may look like this,

http://localhost:8080/#/users/123

The link will be,

<router-link :to="{ name: 'ViewProfile', params: { user_id: id }}">

Create a new component called ViewProfile.vue and then index.js add a new route. This will have a route parameter which we’ll call ‘user_id’ which prefixed with a ‘:’.

{
      path: '/profile/:user_id',
      name: 'ViewProfile',
      component: ViewProfile
    }

and import the ViewProfile component,

import ViewProfile from '@/components/ViewProfile'

In ViewProfile.vue’s data we use $route which is the current route taht we are on, which for example may be ‘/profile/123’. In data we add to this ‘.params’ for ‘user_id’ which is what we named it in the routes.

  data(){
    return {
       userId: this.$route.params.user_id
    }
  },

We can then output the userId property in the template,

<h2>Profile for {{ userId }}</h2>

If go from one profile to another the userId in the template will not update. this is because we are not destroying the component each time. If we go to ‘home’ or ‘about’, or another component and then to profile it will update.

This can be solved by asking the component to watch the $route object which we used in the data(). We add a ‘watch’ property to the component and ask it to watch the $route object and call a function updateID when the $route object changes.

  watch: {
      $route :'updateId'
    }

Create the function updateId in the methods property, so that it will update the ‘userId’ to whatever the parameter on the route is,

  methods: {
      updateId(){
        this.userId = this.$route.params.user_id
      }
    },

Now if we go from

http://localhost:8080/#/profile/34

to a different user_id in the url,

http://localhost:8080/#/profile/123

the template will update correctly.

Next we’ll create links to the different profiles. In the Navbar data we’ll create an array of userIds,

  data(){
    return {
      userIds: ['1','2','3','4']
    }
  }

In the Navbar template we’ll use v-for to iterate through the userIds array. We use ‘:to: to bind data to this route.

name: 'ViewProfile'

is the name of the component and

'params: { user_id }'

where ‘user_id’ is the name of the parameter as used in the main.js routes, which is set to ‘id’, the ‘id’ for the particular iteration of the v-for loop.

<ul>
      <li v-for="(id, index) in userIds" :key="index">
      <router-link :to="{ name: 'ViewProfile', params: { user_id: id }}">
         <span>Profile {{ id }}</span>
      </router-link>
    </li>
    </ul>

Often it is useful to be able to go backward or forward through links pressed in a website. This is programmatically redirecting users. First we want a button so that a user can get to ‘home’. We add a click event to this button which calls a function called ‘goHome’. This function is defined in ‘methods’.

In the goHome method we use ‘this.$router’. Not to be confused with ‘this.$route’ which is the route that we are currently viewing. ‘this.$router’ keeps track of our entire navigation history. We use .push to push on the new route that we’d like the user to be redirected to.

<li> <button @click="goHome">Redirect to Home</button> </li>

$router keep track of the navigation history. We can add buttons with click events which call methods which in turn take us backward or forward through our navigation history, or just to a component as in this example,

  <ul>
      <li> <button @click="goBack">Go back</button> </li>
      <li> <button @click="goHome">Redirect to Home</button> </li>
      <li> <button @click="goForward">Go forward</button> </li>
    </ul>

The methods use,

 this.$router.go(-1)

to go back through the navigation history and

 this.$router.go(1)

to go forward. The methods then are,

  methods: {
      goHome(){
        console.log("hellooooôö");
         this.$router.push({ name: 'Home'})
         console.log("hello home");
     },
     goBack(){
       this.$router.go(-1)
     },
     goForward(){
       this.$router.go(1) 
     }
   }

The ‘#’ symbol in the url sends the request to the browser and not the serve such as this,

http://localhost:8080/#/about

If we take out the ‘#’ the requests is sent to the server,

http://localhost:8080/about

By adding a property of mode : ‘history’ to the Router instance in main.js we can prevent the ‘#’ displaying. However this will mean additional configuration will need to be made to a server, but it is fine when we are just using the development server.

export default new Router({
  mode: 'history',
  routes: [{...

Creating active style links is done with the class ‘router-link-exact-active’ means we are on the exact url for that link, ‘router-link-active’ means we are on a subset of this link also. Use the ‘router-link-exact-active’ class to style the links that we are currently on.

a.router-link-exact-active {
  color: purple;
}

Vue Computed Properties

Full code on github and running on gh-pages

Computed properties are used to alter rendered data without altering it in the data() object.

Create a search box and attach a v-model to it called ‘searchTerm’.

<input type="text" v-model="searchTerm">

Add ‘searchTerm’ to data().

data(){
    return {
      posts: [],
      searchTerm: ''
    }
  },

Add the property ‘computed’ and to it add the ‘filteredPosts()’ function like this,

computed: {
    filteredPosts(){
      return this.posts.filter(post => {
         return post.title.match(this.searchTerm)
      })
    }
  },

Filter iterates through the array and returns elements based on whether they return a ‘true’ or ‘false’ value.

This matches ‘post.title’ to the user’s ‘searchTerm’. If this returns ‘true’ the filter will not remove this from the array.

Now rather than iterating through ‘posts’ in the template using v-for we’ll instead iterate through filteredPosts,

v-for="item in filteredPosts"

The complete component looks like this,

<template lang="html">
  <div>
    <h1>I am the child component</h1>
    <input type="text" v-model="searchTerm">
    <h4> <a href="https://github.com/shanegibney/vue-axios-requests">return to repo</a> </h4>
    <div v-for="item in filteredPosts" :key="item.id">
      <h3>{{ item.title }}</h3>
      <p>{{ item.body | snippet }}</p>
    </div>
  </div>
</template>

<script>
import axios from 'axios'
export default {
  name: 'Child',
  data(){
    return {
      posts: [],
      searchTerm: ''
    }
  },
  computed: {
    filteredPosts(){
      return this.posts.filter(post => {
         return post.title.match(this.searchTerm)
      })
    }
  },
  created(){
    axios.get('https://jsonplaceholder.typicode.com/posts/')
    .then(response => {
        // console.log(response)
        this.posts = response.data
    }).catch(err => {
      console.log(err)
    })
  }
}
</script>

<style lang="css">
h1, h2 {
  color: lightblue;
}
h3, p{
  text-align: left;
}
</style>

Vue Filters

Filtering data to truncate a paragraph and add an ellipsis.

In the child component’s template apply a filter called ‘snippet’,

<p>{{ item.body | snippet }}</p>

To use the filter globally we create it in main.js.

Vue.filter('snippet', val => {

})

Create a Vue object and put a ‘filter’ method on it which takes two parameters, the name of the filter ‘snippet’ and a function which is written as an ES6 arrow function. That function uses a parameter of ‘val’ which is item.body in our case, what was left of the pipe in the template

We first check that the val exists with ‘!val’, also we check that it is a string with ‘typeof != ‘string”. If either of these are true then we return an empty string,

if (!val || typeof(val) != 'string') return ''

The filter in main.js looks ,like this,

Vue.filter('snippet', val => {
  if (!val || typeof(val) != 'string') return ''
  val = val.slice(0, 50)
  return val + '...'
})

Vue Making Request with Axios

Full code on github and implemented on gh-pages

https://jsonplaceholder.typicode.com/ can be used as a fake API for requesting fake json data.

Install Axios,

$ npm install axios --save
$ npm install

In our Child component,

import axios from 'axios

This gets axios from the node_modules directory.

Using the created hook we can axios.get we can get the fake json from jsonplaceholder.typicode.com/posts/

      created(){
    axios.get('https://jsonplaceholder.typicode.com/posts/')
    .then(response => {
        // console.log(response)
        this.posts = response.data
    }).catch(err => {
      console.log(err)
    })
  }

We also used the catch() method to log the ‘err’ object for the case where the promise does not return the request to get the json.

An empty posts array needs to be returned by data() {…}

...
data(){
    return {
      posts: []
    }
  },...

Axios uses a promise here which allows use to chain a then() method on to the end of it, which takes a response object. This uses the ES6 arrow function => to log the response.

<div v-for="item in posts" :key="item.id">
      <h3>{{ item.title }}</h3>
      <p>{{ item.body }}</p>
    </div>

Vue Lifecycle Hooks

Full code on github and implemented on gh-pages

Lifecycle hooks are functions that fire at various events in the lifecycle of a component.

Lifecycle diagram

beforeCreate – Fires before the component is created

created – fires after the component has been created but not yet mounted to the DOM

beforeMount – fires before component is mounted to the DOM

mounted – is fired when componet has been mounted to the DOM

beforeUpdate – fires just before data changes

update – fires after has changed

beforeDestroy – fires just before a component is destroyed. This does not mean that its code is removed.

destroyed – fires after a component has been destroyed.

These hooks are placed inside the export default object itself and not inside the methods property.

<template lang="html">
  <div>
    <h1>{{ title }}</h1>
    <button @click="alterTitle()">Change title</button>
  </div>
</template>

<script>
export default {
  name: 'Child',
  data () {
    return {
      title: 'Old title'
    }
  },
  methods: {
    alterTitle () {
      this.title = 'Much improved title'
    }
  },
  beforeCreate () {
    alert('beforeCreate: before componet is created')
  },
  created () {
    alert('created: component created but not yet added to DOM')
  },
  beforeMount () {
    alert('beforeMount: before component is mounted to the DOM but when it has been created')
  },
  mounted () {
    alert('mounted: after component is mounted to the DOM. This doesn\'t work for me')
  },
  beforeUpdate () {
    alert('beforeUpdate: before data is changed if it is about to change')
  },
  updated () {
    alert('updated: after data has been changed')
  },
  beforeDestroy () {
    alert('beforeDestroy: before component is destroyed')
  },
  destroy () {
    alert('destroy: after component has been destroyed')
  }
}
</script>

<style lang="css" scoped>
h1 {
  color: lightblue;
}
</style>

Vue Component Scope

Normally the css from a component will automatically apply to its parent component.

But this can be limited in the child component by adding ‘scoped’ to the child’s style tag.

<style scoped>
h1 {
   color:#444;
}
</style>

If there are two child components nested inside the parent component, the last child tag in the parent will give its scope to the other components, unless scoped is used, in which case the scope will be limited to only that component.

Vue Custom Event

Full code on github and demo on gh-pages

Set up a vue-cli project and add a nested Child.vue component to App.vue (how to do that)

Add data a click event to the Child.vue component which calls a function remove and passes to it people.name

<span @click="remove(individual.name)">.......</span>

Then we need to create the remove() method in the child component’s export default object,

  ...
methods: {
    remove(name){
      this.$emit('delete',{name})
    }
  },...

Here ‘this’ is the export default object and $emit, emits the event which we are calling ‘delete’ and it passes ‘name’. ‘delete’ is a custom event. ‘name’ is shorthand for ‘name:name’ which can be written just as ‘name’ when both are the same in ES6.

We are using the following data in Child.vue

  data(){
    return {
      title: 'Family',
      people: [
        {name:'Bessy', age: '25'},
        {name:'Bill', age: '35'},
        {name:'Barbara', age: '45'},
        {name:'Bob', age: '55'}
      ]
    }
  }

In the root component App.vue the custom event has to be added to the Child tag,

<Child :people="people" @remove="removePeople"/>

Then in the root component App.vue we need to add the method removePeople()

  methods: {
    removePeople(payload){
      // console.log(payload);
      this.people=this.people.filter(individual=>{
        return individual.name!== payload.name
      })
    }
  }

Vue Passing Data from Parent to Child Component

Full code is on github and gh-pages

Create a child component, nested in the App.vue root component, how-to do that.

Add data to the the App.vue export default object,

  ...
  data(){
    return {
      title: 'People',
      people: [
        {name: 'Joe', qualified: 'false', reg: '123'},
        {name: 'Jill', qualified: 'true', reg: '231'},
        {name: 'June', qualified: 'false', reg: '312'},
        {name: 'Jack', qualified: 'true', reg: '132'}
      ]
    }
  }

Pass data through the child tag in the root component’s template.

<Child :people="people"/>

‘:’ is the data binding and without it only the string ‘people’ would be passed.

Use ‘props’ to define the data in the child component using,

props: ['people'],

which also goes in the export defualt object.

The Child.vue component looks like this,

<template lang="html">
  <div>
    <h1>This is the child component</h1>
    <div v-for="(individual, index) in people" :key="index">
      <span>{{ index+1 }}.   {{ individual.name }}</span>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Child',
  props: ['people'],
  data(){
    return {}
  }
}
</script>

<style lang="css">
h1 {
   color: #444;
}
</style>