One place for hosting & domains

      Simple Laravel CRUD with Resource Controllers


      Creating, reading, updating, and deleting resources is used in pretty much every application. Laravel helps make the process easy using resource controllers. Resource Controllers can make life much easier and takes advantage of some cool Laravel routing techniques. Today, we’ll go through the steps necessary to get a fully functioning CRUD application using resource controllers.

      For this tutorial, we will go through the process of having an admin panel to create, read, update, and delete (CRUD) a resource. Let’s use sharks as our example. We will also make use of Eloquent ORM.

      This tutorial will walk us through:

      • Setting up the database and models
      • Creating the resource controller and its routes
      • Creating the necessary views
      • Explaining each method in a resource controller

      To get started, we will need the controller, the routes, and the view files.

      You can view and clone a repo of all the code covered in this tutorial on GitHub

      Getting our Database Ready

      Shark Migration

      We need to set up a quick database so we can do all of our CRUD functionality. In the command line in the root directory of our Laravel application, let’s create a migration.

      • php artisan migrate:make create_sharks_table --table=sharks --create

      This will create our shark migration in app/database/migrations. Open up that file and let’s add name, email, and shark_level fields.

      app/database/migrations/####_##_##_######_create_sharks_table.php

      <?php
      
      use IlluminateDatabaseSchemaBlueprint;
      use IlluminateDatabaseMigrationsMigration;
      
      class CreatesharksTable extends Migration {
      
          /**
              * Run the migrations.
              *
              * @return void
              */
          public function up()
          {
              Schema::create('sharks', function(Blueprint $table)
              {
                  $table->increments('id');
      
                  $table->string('name', 255);
                  $table->string('email', 255);
                  $table->integer('shark_level');
      
                  $table->timestamps();
              });
          }
      
          /**
              * Reverse the migrations.
              *
              * @return void
              */
          public function down()
          {
              Schema::drop('sharks');
          }
      
      }
      

      Now from the command line again, let’s run this migration. Make sure your database settings are good in app/config/database.php and then run:

      php artisan migrate Our database now has a sharks table to house all of the sharks we CRUD (create, read, update, and delete). Read more about migrations at the Laravel docs.

      Eloquent Model for the sharks

      Now that we have our database, let’s create a simple Eloquent model so that we can access the sharks in our database easily. You can read about Eloquent ORM and see how you can use it in your own applications.

      In the app/models folder, let’s create a shark.php model.

      app/models/shark.php

      
      <?php
      
          class shark extends Eloquent
          {
      
          }
      

      That’s it! Eloquent can handle the rest. By default, this model will link to our sharks table and and we can access it later in our controllers.

      Creating the Controller

      From the official Laravel docs, on resource controllers, you can generate a resource controller using the artisan tool.

      Let’s go ahead and do that. This is the easy part. From the command line in the root directory of your Laravel project, type:

      php artisan controller:make sharkController This will create our resource controller with all the methods we need.

      app/controllers/sharkController.php

      <?php
      
      class sharkController extends BaseController {
      
          /**
              * Display a listing of the resource.
              *
              * @return Response
              */
          public function index()
          {
              //
          }
      
          /**
              * Show the form for creating a new resource.
              *
              * @return Response
              */
          public function create()
          {
              //
          }
      
          /**
              * Store a newly created resource in storage.
              *
              * @return Response
              */
          public function store()
          {
              //
          }
      
          /**
              * Display the specified resource.
              *
              * @param  int  $id
              * @return Response
              */
          public function show($id)
          {
              //
          }
      
          /**
              * Show the form for editing the specified resource.
              *
              * @param  int  $id
              * @return Response
              */
          public function edit($id)
          {
              //
          }
      
          /**
              * Update the specified resource in storage.
              *
              * @param  int  $id
              * @return Response
              */
          public function update($id)
          {
              //
          }
      
          /**
              * Remove the specified resource from storage.
              *
              * @param  int  $id
              * @return Response
              */
          public function destroy($id)
          {
              //
          }
      
      }
      

      Setting Up the Routes

      Now that we have generated our controller, let’s make sure our application has the routes necessary to use it. This is the other easy part (they actually might all be easy parts). In your routes.php file, add this line:

      app/routes.php

      <?php
      
          Route::resource('sharks', 'sharkController');
      

      This will automatically assign many actions to that resource controller. Now if you, go to your browser and view your application at example.com/sharks, it will correspond to the proper method in your sharkController.

      Actions Handled By the Controller

      HTTP Verb Path (URL) Action (Method) Route Name
      GET /sharks index sharks.index
      GET /sharks/create create sharks.create
      POST /sharks store sharks.store
      GET /sharks/{id} show sharks.show
      GET /sharks/{id}/edit edit sharks.edit
      PUT/PATCH /sharks/{id} update sharks.update
      DELETE /sharks/{id} destroy sharks.destroy

      Tip: From the command line, you can run php artisan routes to see all the routes associated with your application.

      The Views

      Since only four of our routes are GET routes, we only need four views. In our app/views folder, let’s make those views now.

      app
      └───views
          └───sharks
              │    index.blade.php
              │    create.blade.php
              │    show.blade.php
              │    edit.blade.php
      

      Making It All Work Together

      Now we have our migrations, database, and models, our controller and routes, and our views. Let’s make all these things work together to build our application. We are going to go through the methods created in the resource controller one by one and make it all work.

      Showing All Resources sharks.index

      Description URL Controller Function View File
      Default page for showing all the sharks. GET example.com/sharks index() app/views/sharks/index.blade.php

      Controller Function index()

      In this function, we will get all the sharks and pass them to the view.

      app/controllers/sharkController.php

      
      <?php
      
      ...
      
          /**
              * Display a listing of the resource.
              *
              * @return Response
              */
          public function index()
          {
              // get all the sharks
              $sharks = shark::all();
      
              // load the view and pass the sharks
              return View::make('sharks.index')
                  ->with('sharks', $sharks);
          }
      ...
      

      The View app/views/sharks/index.blade.php

      Now let’s create our view to loop over the sharks and display them in a table. We like using Twitter Bootstrap for our sites, so the table will use those classes.

      app/views/sharks/index.blade.php

      
      <!DOCTYPE html>
      <html>
      <head>
          <title>Shark App</title>
          <link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
      </head>
      <body>
      <div class="container">
      
      <nav class="navbar navbar-inverse">
          <div class="navbar-header">
              <a class="navbar-brand" href="https://www.digitalocean.com/community/tutorials/{{ URL::to("sharks') }}">shark Alert</a>
          </div>
          <ul class="nav navbar-nav">
              <li><a href="https://www.digitalocean.com/community/tutorials/{{ URL::to("sharks') }}">View All sharks</a></li>
              <li><a href="https://www.digitalocean.com/community/tutorials/{{ URL::to("sharks/create') }}">Create a shark</a>
          </ul>
      </nav>
      
      <h1>All the sharks</h1>
      
      <!-- will be used to show any messages -->
      @if (Session::has('message'))
          <div class="alert alert-info">{{ Session::get('message') }}</div>
      @endif
      
      <table class="table table-striped table-bordered">
          <thead>
              <tr>
                  <td>ID</td>
                  <td>Name</td>
                  <td>Email</td>
                  <td>shark Level</td>
                  <td>Actions</td>
              </tr>
          </thead>
          <tbody>
          @foreach($sharks as $key => $value)
              <tr>
                  <td>{{ $value->id }}</td>
                  <td>{{ $value->name }}</td>
                  <td>{{ $value->email }}</td>
                  <td>{{ $value->shark_level }}</td>
      
                  <!-- we will also add show, edit, and delete buttons -->
                  <td>
      
                      <!-- delete the shark (uses the destroy method DESTROY /sharks/{id} -->
                      <!-- we will add this later since its a little more complicated than the other two buttons -->
      
                      <!-- show the shark (uses the show method found at GET /sharks/{id} -->
                      <a class="btn btn-small btn-success" href="https://www.digitalocean.com/community/tutorials/{{ URL::to("sharks/' . $value->id) }}">Show this shark</a>
      
                      <!-- edit this shark (uses the edit method found at GET /sharks/{id}/edit -->
                      <a class="btn btn-small btn-info" href="https://www.digitalocean.com/community/tutorials/{{ URL::to("sharks/' . $value->id . '/edit') }}">Edit this shark</a>
      
                  </td>
              </tr>
          @endforeach
          </tbody>
      </table>
      
      </div>
      </body>
      </html>
      

      We can now show all of our sharks on a page. There won’t be any that show up currently since we haven’t created any or seeded our database with sharks. Let’s move on to the form to create a shark.

      index-blade

      Creating a New Resource sharks.create

      Description URL Controller Function View File
      Show the form to create a new shark. GET example.com/sharks/create create() app/views/sharks/create.blade.php

      Controller Function create()

      In this function, we will show the form for creating a new shark. This form will be processed by the store() method.

      app/controllers/sharkController.php

      <?php
      ...
          /**
              * Show the form for creating a new resource.
              *
              * @return Response
              */
          public function create()
          {
              // load the create form (app/views/sharks/create.blade.php)
              return View::make('sharks.create');
          }
      ...
      

      The View app/views/sharks/create.blade.php

      app/views/sharks/create.blade.php

      
      <!DOCTYPE html>
      <html>
      <head>
          <title>Shark App</title>
          <link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
      </head>
      <body>
      <div class="container">
      
      <nav class="navbar navbar-inverse">
          <div class="navbar-header">
              <a class="navbar-brand" href="https://www.digitalocean.com/community/tutorials/{{ URL::to("sharks') }}">shark Alert</a>
          </div>
          <ul class="nav navbar-nav">
              <li><a href="https://www.digitalocean.com/community/tutorials/{{ URL::to("sharks') }}">View All sharks</a></li>
              <li><a href="https://www.digitalocean.com/community/tutorials/{{ URL::to("sharks/create') }}">Create a shark</a>
          </ul>
      </nav>
      
      <h1>Create a shark</h1>
      
      <!-- if there are creation errors, they will show here -->
      {{ HTML::ul($errors->all()) }}
      
      {{ Form::open(array('url' => 'sharks')) }}
      
          <div class="form-group">
              {{ Form::label('name', 'Name') }}
              {{ Form::text('name', Input::old('name'), array('class' => 'form-control')) }}
          </div>
      
          <div class="form-group">
              {{ Form::label('email', 'Email') }}
              {{ Form::email('email', Input::old('email'), array('class' => 'form-control')) }}
          </div>
      
          <div class="form-group">
              {{ Form::label('shark_level', 'shark Level') }}
              {{ Form::select('shark_level', array('0' => 'Select a Level', '1' => 'Sees Sunlight', '2' => 'Foosball Fanatic', '3' => 'Basement Dweller'), Input::old('shark_level'), array('class' => 'form-control')) }}
          </div>
      
          {{ Form::submit('Create the shark!', array('class' => 'btn btn-primary')) }}
      
      {{ Form::close() }}
      
      </div>
      </body>
      </html>
      

      We will add the errors section above to show validation errors when we try to store() the resource.
      Tip: When using {{ Form::open() }}, Laravel will automatically create a hidden input field with a token to protect from cross-site request forgeries. Read more at the Laravel docs.

      We now have the form, but we need to have it do something when it the submit button gets pressed. We set this form’s action to be a POST to example.com/sharks. The resource controller will handle this and automatically route the request to the store() method. Let’s handle that now.

      Storing a Resource store()

      Description URL Controller Function View File
      Process the create form submit and save the shark to the database. POST example.com/sharks store() NONE

      As you can see from the form action and the URL, you don’t have to pass anything extra into the URL to store a shark. Since this form is sent using the POST method, the form inputs will be the data used to store the resource.

      To process the form, we’ll want to validate the inputs, send back error messages if they exist, authenticate against the database, and store the resource if all is good. Let’s dive in.

      Controller Function store()

      app/controllers/sharkController.php

      <?php
      ...
          /**
              * Store a newly created resource in storage.
              *
              * @return Response
              */
          public function store()
          {
              // validate
              // read more on validation at http://laravel.com/docs/validation
              $rules = array(
                  'name'       => 'required',
                  'email'      => 'required|email',
                  'shark_level' => 'required|numeric'
              );
              $validator = Validator::make(Input::all(), $rules);
      
              // process the login
              if ($validator->fails()) {
                  return Redirect::to('sharks/create')
                      ->withErrors($validator)
                      ->withInput(Input::except('password'));
              } else {
                  // store
                  $shark = new shark;
                  $shark->name       = Input::get('name');
                  $shark->email      = Input::get('email');
                  $shark->shark_level = Input::get('shark_level');
                  $shark->save();
      
                  // redirect
                  Session::flash('message', 'Successfully created shark!');
                  return Redirect::to('sharks');
              }
          }
      ...
      

      If there are errors processing the form, we will redirect them back to the create form with those errors. We will add them in so the user can understand what went wrong. They will show up in the errors section we setup earlier.

      Now you should be able to create a shark and have them show up on the main page! Navigate to example.com/sharks and there they are. All that’s left is showing a single shark, updating, and deleting.

      created

      Showing a Resource show()

      Description URL Controller Function View File
      Show one of the sharks. GET example.com/sharks/{id} show() app/views/sharks/show.blade.php

      Controller Function show()

      app/controllers/sharkController.php

      <?php
      
      ...
      
          /**
              * Display the specified resource.
              *
              * @param  int  $id
              * @return Response
              */
          public function show($id)
          {
              // get the shark
              $shark = shark::find($id);
      
              // show the view and pass the shark to it
              return View::make('sharks.show')
                  ->with('shark', $shark);
          }
      
      ...
      

      The View app/views/sharks/show.blade.php

      app/views/sharks/show.blade.php

      
      <!DOCTYPE html>
      <html>
      <head>
          <title>Shark App</title>
          <link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
      </head>
      <body>
      <div class="container">
      
      <nav class="navbar navbar-inverse">
          <div class="navbar-header">
              <a class="navbar-brand" href="https://www.digitalocean.com/community/tutorials/{{ URL::to("sharks') }}">shark Alert</a>
          </div>
          <ul class="nav navbar-nav">
              <li><a href="https://www.digitalocean.com/community/tutorials/{{ URL::to("sharks') }}">View All sharks</a></li>
              <li><a href="https://www.digitalocean.com/community/tutorials/{{ URL::to("sharks/create') }}">Create a shark</a>
          </ul>
      </nav>
      
      <h1>Showing {{ $shark->name }}</h1>
      
          <div class="jumbotron text-center">
              <h2>{{ $shark->name }}</h2>
              <p>
                  <strong>Email:</strong> {{ $shark->email }}<br>
                  <strong>Level:</strong> {{ $shark->shark_level }}
              </p>
          </div>
      
      </div>
      </body>
      </html>
      

      show

      Editing a Resource edit()

      Description URL Controller Function View File
      Pull a shark from the database and allow editing. GET example.com/sharks/{id}/edit edit() app/views/sharks/edit.blade.php

      To edit a shark, we need to pull them from the database, show the creation form, but populate it with the selected shark’s info. To make life easier, we will use form model binding. This allows us to pull info from a model and bind it to the input fields in a form. Just makes it easier to populate our edit form and you can imagine that when these forms start getting rather large this will make life much easier.

      Controller Function edit()

      app/controllers/sharkController.php

      
      <?php
      
      ...
      
          /**
              * Show the form for editing the specified resource.
              *
              * @param  int  $id
              * @return Response
              */
          public function edit($id)
          {
              // get the shark
              $shark = shark::find($id);
      
              // show the edit form and pass the shark
              return View::make('sharks.edit')
                  ->with('shark', $shark);
          }
      ...
      

      The View app/views/sharks/edit.blade.php

      app/views/sharks/edit.blade.php

      <!DOCTYPE html>
      <html>
      <head>
          <title>Shark App</title>
          <link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
      </head>
      <body>
      <div class="container">
      
      <nav class="navbar navbar-inverse">
          <div class="navbar-header">
              <a class="navbar-brand" href="https://www.digitalocean.com/community/tutorials/{{ URL::to("sharks') }}">shark Alert</a>
          </div>
          <ul class="nav navbar-nav">
              <li><a href="https://www.digitalocean.com/community/tutorials/{{ URL::to("sharks') }}">View All sharks</a></li>
              <li><a href="https://www.digitalocean.com/community/tutorials/{{ URL::to("sharks/create') }}">Create a shark</a>
          </ul>
      </nav>
      
      <h1>Edit {{ $shark->name }}</h1>
      
      <!-- if there are creation errors, they will show here -->
      {{ HTML::ul($errors->all()) }}
      
      {{ Form::model($shark, array('route' => array('sharks.update', $shark->id), 'method' => 'PUT')) }}
      
          <div class="form-group">
              {{ Form::label('name', 'Name') }}
              {{ Form::text('name', null, array('class' => 'form-control')) }}
          </div>
      
          <div class="form-group">
              {{ Form::label('email', 'Email') }}
              {{ Form::email('email', null, array('class' => 'form-control')) }}
          </div>
      
          <div class="form-group">
              {{ Form::label('shark_level', 'shark Level') }}
              {{ Form::select('shark_level', array('0' => 'Select a Level', '1' => 'Sees Sunlight', '2' => 'Foosball Fanatic', '3' => 'Basement Dweller'), null, array('class' => 'form-control')) }}
          </div>
      
          {{ Form::submit('Edit the shark!', array('class' => 'btn btn-primary')) }}
      
      {{ Form::close() }}
      
      </div>
      </body>
      </html>
      

      Note that we have to pass a method of PUT so that Laravel knows how to route to the controller correctly.

      Updating a Resource update()

      Description URL Controller Function View File
      Process the create form submit and save the shark to the database. PUT example.com/sharks update() NONE

      This controller method will process the edit form. It is very similar to store(). We will validate, update, and redirect.

      Controller Function update()

      app/controllers/sharkController.php

      
      <?php
      
      ...
      
          /**
              * Update the specified resource in storage.
              *
              * @param  int  $id
              * @return Response
              */
          public function update($id)
          {
              // validate
              // read more on validation at http://laravel.com/docs/validation
              $rules = array(
                  'name'       => 'required',
                  'email'      => 'required|email',
                  'shark_level' => 'required|numeric'
              );
              $validator = Validator::make(Input::all(), $rules);
      
              // process the login
              if ($validator->fails()) {
                  return Redirect::to('sharks/' . $id . '/edit')
                      ->withErrors($validator)
                      ->withInput(Input::except('password'));
              } else {
                  // store
                  $shark = shark::find($id);
                  $shark->name       = Input::get('name');
                  $shark->email      = Input::get('email');
                  $shark->shark_level = Input::get('shark_level');
                  $shark->save();
      
                  // redirect
                  Session::flash('message', 'Successfully updated shark!');
                  return Redirect::to('sharks');
              }
          }
      ...
      

      Deleting a Resource destroy()

      Description URL Controller Function View File
      Process the create form submit and save the shark to the database. DELETE example.com/sharks/{id} destroy() NONE

      The workflow for this is that a user would go to view all the sharks, see a delete button, click it to delete. Since we never created a delete button in our app/views/sharks/index.blade.php, we will create that now. We will also add a notification section to show a success message.

      We have to send the request to our application using the DELETE HTTP verb, so we will create a form to do that since a button won’t do.

      Alert: The DELETE HTTP verb is used when accessing the sharks.destroy route. Since you can’t just create a button or form with the method DELETE, we will have to spoof it by creating a hidden input field in our delete form.

      The View app/views/sharks/index.blade.php

      app/views/sharks/index.blade.php

      
      ...
      
          @foreach($sharks as $key => $value)
              <tr>
                  <td>{{ $value->id }}</td>
                  <td>{{ $value->name }}</td>
                  <td>{{ $value->email }}</td>
                  <td>{{ $value->shark_level }}</td>
      
                  <!-- we will also add show, edit, and delete buttons -->
                  <td>
      
                      <!-- delete the shark (uses the destroy method DESTROY /sharks/{id} -->
                      <!-- we will add this later since its a little more complicated than the other two buttons -->
                      {{ Form::open(array('url' => 'sharks/' . $value->id, 'class' => 'pull-right')) }}
                          {{ Form::hidden('_method', 'DELETE') }}
                          {{ Form::submit('Delete this shark', array('class' => 'btn btn-warning')) }}
                      {{ Form::close() }}
      
                      <!-- show the shark (uses the show method found at GET /sharks/{id} -->
                      <a class="btn btn-small btn-success" href="https://www.digitalocean.com/community/tutorials/{{ URL::to("sharks/' . $value->id) }}">Show this shark</a>
      
                      <!-- edit this shark (uses the edit method found at GET /sharks/{id}/edit -->
                      <a class="btn btn-small btn-info" href="https://www.digitalocean.com/community/tutorials/{{ URL::to("sharks/' . $value->id . '/edit') }}">Edit this shark</a>
      
                  </td>
              </tr>
          @endforeach
          ...
      

      Now when we click that form submit button, Laravel will use the sharks.destroy route and we can process that in our controller.

      Controller Function destroy()

      app/controllers/sharkController.php

      <?php
      
      ...
      
          /**
              * Remove the specified resource from storage.
              *
              * @param  int  $id
              * @return Response
              */
          public function destroy($id)
          {
              // delete
              $shark = shark::find($id);
              $shark->delete();
      
              // redirect
              Session::flash('message', 'Successfully deleted the shark!');
              return Redirect::to('sharks');
          }
      ...
      

      Conclusion

      That’s everything! Hopefully we covered enough so that you can understand how resource controllers can be used in all sorts of scenarios. Just create the controller, create the single line in the routes file, and you have the foundation for doing CRUD.

      As always, let us know if you have any questions or comments. We’ll be expanding more on Laravel in the coming articles so if there’s anything specific, throw it in the comments or email us.

      Further Reading: For more Laravel, check out our Simple Laravel Series.



      Source link

      Build a CRUD Web App With Python and Flask – Part One


      In this three-part tutorial, we’ll build a CRUD (Create, Read, Update, Delete) employee management web app using Flask, a microframework for Python. I’ve named the app Project Dream Team, and it will have the following features:

      1. Users will be able to register and login as employees
      2. The administrator will be able to create, update, and delete departments and roles
      3. The administrator will be able to assign employees to a department and assign them roles
      4. The administrator will be able to view all employees and their details

      Part One will cover:

      1. Database setup
      2. Models
      3. Migration
      4. Homepage
      5. Authentication

      Ready? Here we go!

      Prerequisites

      This tutorial builds on my introductory tutorial, Getting Started With Flask, picking up where it left off. It assumes you have, to begin with, the following dependencies installed:

      1. Python 2.7
      2. Flask
      3. virtualenv (and, optionally, virtualenvwrapper)

      You should have a virtual environment set up and activated. You should also have the following file and directory structure:

      ├── dream-team
             ├── app
             │   ├── __init__.py
             │   ├── templates
             │   ├── models.py
             │   └── views.py
             ├── config.py
             ├── requirements.txt
             └── run.py
      

      This project structure groups the similar components of the application together. The dream-team directory houses all the project files. The app directory is the application package, and houses different but interlinked modules of the application. All templates are stored in the templates directory, all models are in the models.py file, and all routes are in the views.py file. The run.py file is the application’s entry point, the config.py file contains the application configurations, and the requirements.txt file contains the software dependencies for the application.

      If you don’t have these set up, please visit the introductory tutorial and catch up!

      Database Setup

      Flask has support for several relational database management systems, including SQLite, MySQL, and PostgreSQL. For this tutorial, we will be using MySQL. It’s popular and therefore has a lot of support, in addition to being scalable, secure, and rich in features.

      We will install the following (remember to activate your virtual environment):

      1. Flask-SQLAlchemy: This will allow us to use SQLAlchemy, a useful tool for SQL use with Python. SQLAlchemy is an Object Relational Mapper (ORM), which means that it connects the objects of an application to tables in a relational database management system. These objects can be stored in the database and accessed without the need to write raw SQL. This is convenient because it simplifies queries that may have been complex if written in raw SQL. Additionally, it reduces the risk of SQL injection attacks since we are not dealing with the input of raw SQL.

      2. MySQL-Python: This is a Python interface to MySQL. It will help us connect the MySQL database to the app.

      $ pip install flask-sqlalchemy mysql-python
      

      We’ll then create the MySQL database. Ensure you have MySQL installed and running, and then log in as the root user:

      $ mysql -u root
      
      mysql> CREATE USER 'dt_admin'@'localhost' IDENTIFIED BY 'dt2016';
      Query OK, 0 rows affected (0.00 sec)
      
      mysql> CREATE DATABASE dreamteam_db;
      Query OK, 1 row affected (0.00 sec)
      
      mysql> GRANT ALL PRIVILEGES ON dreamteam_db . * TO 'dt_admin'@'localhost';
      Query OK, 0 rows affected (0.00 sec)
      

      We have now created a new user dt_admin with the password dt2016, created a new database dreamteam_db, and granted the new user all database privileges.

      Next, let’s edit the config.py. Remove any exisiting code and add the following:

      # config.py
      
      class Config(object):
          """
          Common configurations
          """
      
          # Put any configurations here that are common across all environments
      
      
      class DevelopmentConfig(Config):
          """
          Development configurations
          """
      
          DEBUG = True
          SQLALCHEMY_ECHO = True
      
      
      class ProductionConfig(Config):
          """
          Production configurations
          """
      
          DEBUG = False
      
      app_config = {
          'development': DevelopmentConfig,
          'production': ProductionConfig
      }
      

      It is good practice to specify configurations for different environments. In the file above, we have specifed configurations for development, which we will use while building the app and running it locally, as well as production, which we will use when the app is deployed.

      Some useful configuration variables are:

      1. TESTING: setting this to True activates the testing mode of Flask extensions. This allows us to use testing properties that could for instance have an increased runtime cost, such as unittest helpers. It should be set to True in the configurations for testing. It defaults to False.
      2. DEBUG: setting this to True activates the debug mode on the app. This allows us to use the Flask debugger in case of an unhandled exception, and also automatically reloads the application when it is updated. It should however always be set to False in production. It defaults to False.
      3. SQLALCHEMY_ECHO: setting this to True helps us with debugging by allowing SQLAlchemy to log errors.

      You can find more Flask configuration variables here and SQLAlchemy configuration variables here.

      Next, create an instance directory in the dream-team directory, and then create a config.py file inside it. We will put configuration variables here that will not be pushed to version control due to their sensitive nature. In this case, we put the secret key as well as the database URI which contains the database user password.

      # instance/config.py
      
      SECRET_KEY = 'p9Bv<3Eid9%$i01'
      SQLALCHEMY_DATABASE_URI = 'mysql://dt_admin:dt2016@localhost/dreamteam_db'
      

      Now, let’s edit the app/__init__.py file. Remove any existing code and add the following:

      # app/__init__.py
      
      # third-party imports
      from flask import Flask
      from flask_sqlalchemy import SQLAlchemy
      
      # local imports
      from config import app_config
      
      # db variable initialization
      db = SQLAlchemy()
      
      
      def create_app(config_name):
          app = Flask(__name__, instance_relative_config=True)
          app.config.from_object(app_config[config_name])
          app.config.from_pyfile('config.py')
          db.init_app(app)
      
          return app
      

      We’ve created a function, create_app that, given a configuration name, loads the correct configuration from the config.py file, as well as the configurations from the instance/config.py file. We have also created a db object which we will use to interact with the database.

      Next, let’s edit the run.py file:

      # run.py
      
      import os
      
      from app import create_app
      
      config_name = os.getenv('FLASK_CONFIG')
      app = create_app(config_name)
      
      
      if __name__ == '__main__':
          app.run()
      

      We create the app by running the create_app function and passing in the configuration name. We get this from the OS environment variable FLASK_CONFIG. Because we are in development, we should set the environment variable to development.

      Let’s run the app to ensure everything is working as expected. First, delete the app/views.py file as well as the app/templates directory as we will not be needing them going forward. Next, add a temporary route to the app/__init__.py file as follows:

      # app/__init__.py
      
      # existing code remains
      
      
      def create_app(config_name):
          # existing code remains
      
          # temporary route
          @app.route('/')
          def hello_world():
              return 'Hello, World!'
      
          return app
      

      Make sure you set the FLASK_CONFIG and FLASK_APP environment variables before running the app:

      $ export FLASK_CONFIG=development
      $ export FLASK_APP=run.py
      $ flask run
       * Serving Flask app "run"
       * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
      

      We can see the “Hello, World” string we set in the route. The app is working well so far.

      Models

      Now to work on the models. Remember that a model is a representation of a database table in code. We’ll need three models: Employee, Department, and Role.

      But first, let’s install Flask-Login, which will help us with user management and handle logging in, logging out, and user sessions. The Employee model will inherit from Flask-Login’s UserMixin class which will make it easier for us to make use of its properties and methods.

      $ pip install flask-login
      

      To use Flask-Login, we need to create a LoginManager object and initialize it in the app/__init__.py file. First, remove the route we added earlier, and then add the following:

      # app/__init__.py
      
      # after existing third-party imports
      from flask_login import LoginManager
      
      # after the db variable initialization
      login_manager = LoginManager()
      
      
      def create_app(config_name):
          # existing code remains
      
          login_manager.init_app(app)
          login_manager.login_message = "You must be logged in to access this page."
          login_manager.login_view = "auth.login"
      
          return app
      

      In addition to initializing the LoginManager object, we’ve also added a login_view and login_message to it. This way, if a user tries to access a page that they are not authorized to, it will redirect to the specified view and display the specified message. We haven’t created the auth.login view yet, but we will when we get to authentication.

      Now add the following code to the app/models.py file:

      # app/models.py
      
      from flask_login import UserMixin
      from werkzeug.security import generate_password_hash, check_password_hash
      
      from app import db, login_manager
      
      
      class Employee(UserMixin, db.Model):
          """
          Create an Employee table
          """
      
          # Ensures table will be named in plural and not in singular
          # as is the name of the model
          __tablename__ = 'employees'
      
          id = db.Column(db.Integer, primary_key=True)
          email = db.Column(db.String(60), index=True, unique=True)
          username = db.Column(db.String(60), index=True, unique=True)
          first_name = db.Column(db.String(60), index=True)
          last_name = db.Column(db.String(60), index=True)
          password_hash = db.Column(db.String(128))
          department_id = db.Column(db.Integer, db.ForeignKey('departments.id'))
          role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
          is_admin = db.Column(db.Boolean, default=False)
      
          @property
          def password(self):
              """
              Prevent pasword from being accessed
              """
              raise AttributeError('password is not a readable attribute.')
      
          @password.setter
          def password(self, password):
              """
              Set password to a hashed password
              """
              self.password_hash = generate_password_hash(password)
      
          def verify_password(self, password):
              """
              Check if hashed password matches actual password
              """
              return check_password_hash(self.password_hash, password)
      
          def __repr__(self):
              return '<Employee: {}>'.format(self.username)
      
      
      # Set up user_loader
      @login_manager.user_loader
      def load_user(user_id):
          return Employee.query.get(int(user_id))
      
      
      class Department(db.Model):
          """
          Create a Department table
          """
      
          __tablename__ = 'departments'
      
          id = db.Column(db.Integer, primary_key=True)
          name = db.Column(db.String(60), unique=True)
          description = db.Column(db.String(200))
          employees = db.relationship('Employee', backref="department",
                                      lazy='dynamic')
      
          def __repr__(self):
              return '<Department: {}>'.format(self.name)
      
      
      class Role(db.Model):
          """
          Create a Role table
          """
      
          __tablename__ = 'roles'
      
          id = db.Column(db.Integer, primary_key=True)
          name = db.Column(db.String(60), unique=True)
          description = db.Column(db.String(200))
          employees = db.relationship('Employee', backref="role",
                                      lazy='dynamic')
      
          def __repr__(self):
              return '<Role: {}>'.format(self.name)
      

      In the Employee model, we make use of some of Werkzeug’s handy security helper methods, generate_password_hash, which allows us to hash passwords, and check_password_hash, which allows us ensure the hashed password matches the password. To enhance security, we have a password method which ensures that the password can never be accessed; instead an error will be raised. We also have two foreign key fields, department_id and role_id, which refer to the ID’s of the department and role assigned to the employee.

      Note that we have an is_admin field which is set to False by default. We will override this when creating the admin user. Just after the Employee model, we have a user_loader callback, which Flask-Login uses to reload the user object from the user ID stored in the session.

      The Department and Role models are quite similar. Both have name and description fields. Additionally, both have a one-to-many relationship with the Employee model (one department or role can have many employees). We define this in both models using the employees field. backref allows us to create a new property on the Employee model such that we can use employee.department or employee.role to get the department or role assigned to that employee. lazy defines how the data will be loaded from the database; in this case it will be loaded dynamically, which is ideal for managing large collections.

      Migration

      Migrations allow us to manage changes we make to the models, and propagate these changes in the database. For example, if later on we make a change to a field in one of the models, all we will need to do is create and apply a migration, and the database will reflect the change.

      We’ll begin by installing Flask-Migrate, which will handle the database migrations using Alembic, a lightweight database migration tool. Alembic emits ALTER statements to a database thus implememting changes made to the models. It also auto-generates minimalistic migration scripts, which may be complex to write.

      $ pip install flask-migrate
      

      We’ll need to edit the app/__init__.py file:

      # app/__init__.py
      
      # after existing third-party imports
      from flask_migrate import Migrate
      
      # existing code remains
      
      
      def create_app(config_name):
          # existing code remains
      
          migrate = Migrate(app, db)
      
          from app import models
      
          return app
      

      We have created a migrate object which will allow us to run migrations using Flask-Migrate. We have also imported the models from the app package. Next, we’ll run the following command to create a migration repository:

      $ flask db init
      

      This creates a migrations directory in the dream-team directory:

      └── migrations
          ├── README
          ├── alembic.ini
          ├── env.py
          ├── script.py.mako
          └── versions
      

      Next, we will create the first migration:

      $ flask db migrate
      

      Finally, we’ll apply the migration:

      $ flask db upgrade
      

      We’ve sucessfully created tables based on the models we wrote! Let’s check the MySQL database to confirm this:

      $ mysql -u root
      
      mysql> use dreamteam_db;
      
      mysql> show tables;
      +------------------------+
      | Tables_in_dreamteam_db |
      +------------------------+
      | alembic_version        |
      | departments            |
      | employees              |
      | roles                  |
      +------------------------+
      4 rows in set (0.00 sec)
      

      Blueprints

      Blueprints are great for organising a flask app into components, each with its own views and forms. I find that blueprints make for a cleaner and more organised project structure because each blueprint is a distinct component that addresses a specific functionality of the app. Each blueprint can even have its own cutsom URL prefix or subdomain. Blueprints are particularly convenient for large applications.

      We’re going to have three blueprints in this app:

      1. Home – this will have the homepage and dashboard views
      2. Admin – this will have all administrator (department and role) forms and views
      3. Auth – this will have all authentication (registration and login) forms and views

      Create the relevant files and directories so that your directory structure resembles this:

      └── dream-team
          ├── app
          │   ├── __init__.py
          │   ├── admin
          │   │   ├── __init__.py
          │   │   ├── forms.py
          │   │   └── views.py
          │   ├── auth
          │   │   ├── __init__.py
          │   │   ├── forms.py
          │   │   └── views.py
          │   ├── home
          │   │   ├── __init__.py
          │   │   └── views.py
          │   ├── models.py
          │   ├── static
          │   └── templates
          ├── config.py
          ├── instance
          │   └── config.py
          ├── migrations
          │   ├── README
          │   ├── alembic.ini
          │   ├── env.py
          │   ├── script.py.mako
          │   └── versions
          │       └── a1a1d8b30202_.py
          ├── requirements.txt
          └── run.py
      

      I chose not to have static and templates directories for each blueprint, because all the application templates will inherit from the same base template and use the same CSS file. Instead, the templates directory will have sub-directories for each blueprint so that blueprint templates can be grouped together.

      In each blueprint’s __init__.py file, we need to create a Blueprint object and initialize it with a name. We also need to import the views.

      # app/admin/__init__.py
      
      from flask import Blueprint
      
      admin = Blueprint('admin', __name__)
      
      from . import views
      
      # app/auth/__init__.py
      
      from flask import Blueprint
      
      auth = Blueprint('auth', __name__)
      
      from . import views
      
      # app/home/__init__.py
      
      from flask import Blueprint
      
      home = Blueprint('home', __name__)
      
      from . import views
      

      Then, we can register the blueprints on the app in the app/__init__.py file, like so:

      # app/__init__.py
      
      # existing code remains
      
      
      def create_app(config_name):
          # existing code remains
      
          from app import models
      
          from .admin import admin as admin_blueprint
          app.register_blueprint(admin_blueprint, url_prefix='/admin')
      
          from .auth import auth as auth_blueprint
          app.register_blueprint(auth_blueprint)
      
          from .home import home as home_blueprint
          app.register_blueprint(home_blueprint)
      
          return app
      

      We have imported each blueprint object and registered it. For the admin blueprint, we have added a url prefix, /admin. This means that all the views for this blueprint will be accessed in the browser with the url prefix admin.

      Home Blueprint

      Time to work on fleshing out the blueprints! We’ll start with the home blueprint, which will have the homepage as well as the dashboard.

      # app/home/views.py
      
      from flask import render_template
      from flask_login import login_required
      
      from . import home
      
      
      @home.route('/')
      def homepage():
          """
          Render the homepage template on the / route
          """
          return render_template('home/index.html', title="Welcome")
      
      
      @home.route('/dashboard')
      @login_required
      def dashboard():
          """
          Render the dashboard template on the /dashboard route
          """
          return render_template('home/dashboard.html', title="Dashboard")
      

      Each view function has a decorator, home.route, which has a URL route as a parameter (remember that home is the name of the blueprint as specified in the app/home/__init__.py file). Each view handles requests to the specified URL.

      The homepage view renders the home template, while the dashboard view renders the dashboard template. Note that the dashboard view has a login_required decorator, meaning that users must be logged in to access it.

      Now to work on the base template, which all other templates will inherit from. Create a base.html file in the app/templates directory and add the following code:

      <!-- app/templates/base.html -->
      
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <title>{{ title }} | Project Dream Team</title>
          <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
          <link href="https://www.digitalocean.com/community/tutorials/{{ url_for("static', filename="css/style.css") }}" rel="stylesheet">
          <link rel="shortcut icon" href="https://www.digitalocean.com/community/tutorials/{{ url_for("static', filename="img/favicon.ico") }}">
      </head>
      <body>
          <nav class="navbar navbar-default navbar-fixed-top topnav" role="navigation">
              <div class="container topnav">
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
                        <span class="sr-only">Toggle navigation</span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>
                    <a class="navbar-brand topnav" href="https://www.digitalocean.com/community/tutorials/{{ url_for("home.homepage') }}">Project Dream Team</a>
                </div>
                <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                    <ul class="nav navbar-nav navbar-right">
                        <li><a href="https://www.digitalocean.com/community/tutorials/{{ url_for("home.homepage') }}">Home</a></li>
                        <li><a href="#">Register</a></li>
                        <li><a href="#">Login</a></li>
                    </ul>
                </div>
              </div>
          </nav>
          <div class="wrapper">
            {% block body %}
            {% endblock %}
            <div class="push"></div>
          </div>
          <footer>
              <div class="container">
                  <div class="row">
                      <div class="col-lg-12">
                          <ul class="list-inline">
                              <li><a href="https://www.digitalocean.com/community/tutorials/{{ url_for("home.homepage') }}">Home</a></li>
                              <li class="footer-menu-divider">⋅</li>
                              <li><a href="#">Register</a></li>
                              <li class="footer-menu-divider">⋅</li>
                              <li><a href="#">Login</a></li>
                          </ul>
                          <p class="copyright text-muted small">Copyright © 2016. All Rights Reserved</p>
                      </div>
                  </div>
              </div>
          </footer>
      </body>
      </html>
      

      Note that we use # for the Register and Login links. We will update this when we are working on the auth blueprint.

      Next, create a home directory inside the app/templates directory. The homepage template, index.html, will go inside it:

      <!-- app/templates/home/index.html -->
      
      {% extends "base.html" %}
      {% block title %}Home{% endblock %}
      {% block body %}
      <div class="intro-header">
          <div class="container">
              <div class="row">
                  <div class="col-lg-12">
                      <div class="intro-message">
                          <h1>Project Dream Team</h1>
                          <h3>The best company in the world!</h3>
                          <hr class="intro-divider">
                          </ul>
                      </div>
                  </div>
              </div>
          </div>
      </div>
      {% endblock %}
      

      Inside the static directory, add css and img directories. Add the following CSS file, style.css, to your static/css directory (note that you will need a background image, intro-bg.jpg, as well as a favicon in your static/img directory):

      /* app/static/css/style.css */
      
      body, html {
          width: 100%;
          height: 100%;
      }
      
      body, h1, h2, h3 {
          font-family: "Lato", "Helvetica Neue", Helvetica, Arial, sans-serif;
          font-weight: 700;
      }
      
      a, .navbar-default .navbar-brand, .navbar-default .navbar-nav>li>a {
        color: #aec251;
      }
      
      a:hover, .navbar-default .navbar-brand:hover, .navbar-default .navbar-nav>li>a:hover {
        color: #687430;
      }
      
      footer {
          padding: 50px 0;
          background-color: #f8f8f8;
      }
      
      p.copyright {
          margin: 15px 0 0;
      }
      
      .alert-info {
          width: 50%;
          margin: auto;
          color: #687430;
          background-color: #e6ecca;
          border-color: #aec251;
      }
      
      .btn-default {
          border-color: #aec251;
          color: #aec251;
      }
      
      .btn-default:hover {
          background-color: #aec251;
      }
      
      .center {
          margin: auto;
          width: 50%;
          padding: 10px;
      }
      
      .content-section {
          padding: 50px 0;
          border-top: 1px solid #e7e7e7;
      }
      
      .footer, .push {
        clear: both;
        height: 4em;
      }
      
      .intro-divider {
          width: 400px;
          border-top: 1px solid #f8f8f8;
          border-bottom: 1px solid rgba(0,0,0,0.2);
      }
      
      .intro-header {
          padding-top: 50px;
          padding-bottom: 50px;
          text-align: center;
          color: #f8f8f8;
          background: url(../img/intro-bg.jpg) no-repeat center center;
          background-size: cover;
          height: 100%;
      }
      
      .intro-message {
          position: relative;
          padding-top: 20%;
          padding-bottom: 20%;
      }
      
      .intro-message > h1 {
          margin: 0;
          text-shadow: 2px 2px 3px rgba(0,0,0,0.6);
          font-size: 5em;
      }
      
      .intro-message > h3 {
          text-shadow: 2px 2px 3px rgba(0,0,0,0.6);
      }
      
      .lead {
          font-size: 18px;
          font-weight: 400;
      }
      
      .topnav {
          font-size: 14px;
      }
      
      .wrapper {
        min-height: 100%;
        height: auto !important;
        height: 100%;
        margin: 0 auto -4em;
      }
      

      Run the app; you should be able to see the homepage now.

      Auth Blueprint

      For the auth blueprint, we’ll begin by creating the registration and login forms. We’ll use Flask-WTF, which will allow us to create forms that are secure (thanks to CSRF protection and reCAPTCHA support).

      pip install Flask-WTF
      

      Now to write the code for the forms:

      # app/auth/forms.py
      
      from flask_wtf import FlaskForm
      from wtforms import PasswordField, StringField, SubmitField, ValidationError
      from wtforms.validators import DataRequired, Email, EqualTo
      
      from ..models import Employee
      
      
      class RegistrationForm(FlaskForm):
          """
          Form for users to create new account
          """
          email = StringField('Email', validators=[DataRequired(), Email()])
          username = StringField('Username', validators=[DataRequired()])
          first_name = StringField('First Name', validators=[DataRequired()])
          last_name = StringField('Last Name', validators=[DataRequired()])
          password = PasswordField('Password', validators=[
                                              DataRequired(),
                                              EqualTo('confirm_password')
                                              ])
          confirm_password = PasswordField('Confirm Password')
          submit = SubmitField('Register')
      
          def validate_email(self, field):
              if Employee.query.filter_by(email=field.data).first():
                  raise ValidationError('Email is already in use.')
      
          def validate_username(self, field):
              if Employee.query.filter_by(username=field.data).first():
                  raise ValidationError('Username is already in use.')
      
      
      class LoginForm(FlaskForm):
          """
          Form for users to login
          """
          email = StringField('Email', validators=[DataRequired(), Email()])
          password = PasswordField('Password', validators=[DataRequired()])
          submit = SubmitField('Login')
      

      Flask-WTF has a number of validators that make writing forms much easier. All the fields in the models have the DataRequired validator, which means that users will be required to fill all of them in order to register or login.

      For the registration form, we require users to fill in their email address, username, first name, last name, and their password twice. We use the Email validator to ensure valid email formats are used (e.g some-name@some-domain.com.) We use the EqualTo validator to confirm that the password and confirm_password fields in the RegistrationForm match. We also create methods (validate_email and validate_username) to ensure that the email and username entered have not been used before.

      The submit field in both forms will be represented as a button that users will be able to click to register and login respectively.

      With the forms in place, we can write the views:

      # app/auth/views.py
      
      from flask import flash, redirect, render_template, url_for
      from flask_login import login_required, login_user, logout_user
      
      from . import auth
      from forms import LoginForm, RegistrationForm
      from .. import db
      from ..models import Employee
      
      
      @auth.route('/register', methods=['GET', 'POST'])
      def register():
          """
          Handle requests to the /register route
          Add an employee to the database through the registration form
          """
          form = RegistrationForm()
          if form.validate_on_submit():
              employee = Employee(email=form.email.data,
                                  username=form.username.data,
                                  first_name=form.first_name.data,
                                  last_name=form.last_name.data,
                                  password=form.password.data)
      
              # add employee to the database
              db.session.add(employee)
              db.session.commit()
              flash('You have successfully registered! You may now login.')
      
              # redirect to the login page
              return redirect(url_for('auth.login'))
      
          # load registration template
          return render_template('auth/register.html', form=form, title="Register")
      
      
      @auth.route('/login', methods=['GET', 'POST'])
      def login():
          """
          Handle requests to the /login route
          Log an employee in through the login form
          """
          form = LoginForm()
          if form.validate_on_submit():
      
              # check whether employee exists in the database and whether
              # the password entered matches the password in the database
              employee = Employee.query.filter_by(email=form.email.data).first()
              if employee is not None and employee.verify_password(
                      form.password.data):
                  # log employee in
                  login_user(employee)
      
                  # redirect to the dashboard page after login
                  return redirect(url_for('home.dashboard'))
      
              # when login details are incorrect
              else:
                  flash('Invalid email or password.')
      
          # load login template
          return render_template('auth/login.html', form=form, title="Login")
      
      
      @auth.route('/logout')
      @login_required
      def logout():
          """
          Handle requests to the /logout route
          Log an employee out through the logout link
          """
          logout_user()
          flash('You have successfully been logged out.')
      
          # redirect to the login page
          return redirect(url_for('auth.login'))
      

      Just like in the home blueprint, each view here handles requests to the specified URL. The register view creates an instance of the Employee model class using the registration form data to populate the fields, and then adds it to the database. This esentially registers a new employee.

      The login view queries the database to check whether an employee exists with an email address that matches the email provided in the login form data. It then uses the verify_password method to check that the password in the database for the employee matches the password provided in the login form data. If both of these are true, it proceeds to log the user in using the login_user method provided by Flask-Login.

      The logout view has the login_required decorator, which means that a user must be logged in to access it. It calles the logout_user method provided by Flask-Login to log the user out.

      Note the use of flash method, which allows us to use Flask’s message flashing feature. This allows us to communicate feedback to the user, such as informing them of successful registration or unsuccessful login.

      Finally, let’s work on the templates. First, we’ll install Flask-Bootstrap so we can use its wtf and utils libraries. The wtf library will allow us to quickly generate forms in the templates based on the forms in the forms.py file. The utils library will allow us to display the flash messages we set earlier to give feedback to the user.

      pip install flask-bootstrap
      

      We need to edit the app/__init__.py file to use Flask-Bootstrap:

      # app/__init__.py
      
      # after existing third-party imports
      from flask_bootstrap import Bootstrap
      
      # existing code remains
      
      
      def create_app(config_name):
          # existing code remains
      
          Bootstrap(app)
      
          from app import models
      
          # blueprint registration remains here
      
          return app
      

      We’ve made quite a number of edits to the app/__init__.py file. This is the final version of the file and how it should look at this point (note that I have re-arranged the imports and variables in alphabetical order):

      # app/__init__.py
      
      # third-party imports
      from flask import Flask
      from flask_bootstrap import Bootstrap
      from flask_login import LoginManager
      from flask_migrate import Migrate
      from flask_sqlalchemy import SQLAlchemy
      
      # local imports
      from config import app_config
      
      db = SQLAlchemy()
      login_manager = LoginManager()
      
      
      def create_app(config_name):
          app = Flask(__name__, instance_relative_config=True)
          app.config.from_object(app_config[config_name])
          app.config.from_pyfile('config.py')
      
          Bootstrap(app)
          db.init_app(app)
          login_manager.init_app(app)
          login_manager.login_message = "You must be logged in to access this page."
          login_manager.login_view = "auth.login"
          migrate = Migrate(app, db)
      
          from app import models
      
          from .admin import admin as admin_blueprint
          app.register_blueprint(admin_blueprint, url_prefix='/admin')
      
          from .auth import auth as auth_blueprint
          app.register_blueprint(auth_blueprint)
      
          from .home import home as home_blueprint
          app.register_blueprint(home_blueprint)
      
          return app
      

      We need two templates for the auth blueprint: register.html and login.html, which we’ll create in an auth directory inside the templates directory.

      <!-- app/templates/auth/register.html -->
      
      {% import "bootstrap/wtf.html" as wtf %}
      {% extends "base.html" %}
      {% block title %}Register{% endblock %}
      {% block body %}
      <div class="content-section">
        <div class="center">
          <h1>Register for an account</h1>
          <br/>
          {{ wtf.quick_form(form) }}
        </div>
      </div>
      {% endblock %}
      
      <!-- app/templates/auth/login.html -->
      
      {% import "bootstrap/utils.html" as utils %}
      {% import "bootstrap/wtf.html" as wtf %}
      {% extends "base.html" %}
      {% block title %}Login{% endblock %}
      {% block body %}
      <div class="content-section">
        <br/>
        {{ utils.flashed_messages() }}
        <br/>
        <div class="center">
          <h1>Login to your account</h1>
          <br/>
          {{ wtf.quick_form(form) }}
        </div>
      </div>
      {% endblock %}
      

      The forms are loaded from the app/auth/views.py file, where we specified which template files to display for each view. Remember the Register and Login links in the base template? Let’s update them now so we can access the pages from the menus:

      <!-- app/templates/base.html -->
      
      <!-- Modify nav bar menu -->
      <ul class="nav navbar-nav navbar-right">
          <li><a href="https://www.digitalocean.com/community/tutorials/{{ url_for("home.homepage') }}">Home</a></li>
          <li><a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.register') }}">Register</a></li>
          <li><a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.login') }}">Login</a></li>
      </ul>
      
      <!-- Modify footer menu -->
      <ul class="list-inline">
          <li><a href="https://www.digitalocean.com/community/tutorials/{{ url_for("home.homepage') }}">Home</a></li>
          <li class="footer-menu-divider">⋅</li>
          <li><a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.register') }}">Register</a></li>
          <li class="footer-menu-divider">⋅</li>
          <li><a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.login') }}">Login</a></li>
      </ul>
      

      Run the app again and click on the Register and Login menu links. You should see the templates loaded with the appropriate form.

      Try to fill out the registration form; you should be able to register a new employee. After registration, you should be redirected to the login page, where you will see the flash message we configured in the app/auth/views.py file, inviting you to login.

      Logging in should be successful; however you should get a Template Not Found error after logging in, because the dashboard.html template has not been created yet. Let’s do that now:

      <!-- app/templates/home/dashboard.html -->
      
      {% extends "base.html" %}
      {% block title %}Dashboard{% endblock %}
      {% block body %}
      <div class="intro-header">
          <div class="container">
              <div class="row">
                  <div class="col-lg-12">
                      <div class="intro-message">
                          <h1>The Dashboard</h1>
                          <h3>We made it here!</h3>
                          <hr class="intro-divider">
                          </ul>
                      </div>
                  </div>
              </div>
          </div>
      </div>
      {% endblock %}
      

      Refresh the page. You’ll notice that the navigation menu still has the register and login links, even though we are already logged in. We’ll need to modify it to show a logout link when a user is already authenticated. We will also include a Hi, username! message in the nav bar:

      <!-- app/templates/base.html -->
      
      <!-- In the head tag, include link to Font Awesome CSS so we can use icons -->
      <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
      
      <!-- Modify nav bar menu -->
      <ul class="nav navbar-nav navbar-right">
          {% if current_user.is_authenticated %}
            <li><a href="https://www.digitalocean.com/community/tutorials/{{ url_for("home.dashboard') }}">Dashboard</a></li>
            <li><a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.logout') }}">Logout</a></li>
            <li><a><i class="fa fa-user"></i>  Hi, {{ current_user.username }}!</a></li>
          {% else %}
            <li><a href="https://www.digitalocean.com/community/tutorials/{{ url_for("home.homepage') }}">Home</a></li>
            <li><a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.register') }}">Register</a></li>
            <li><a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.login') }}">Login</a></li>
          {% endif %}
      </ul>
      
      <!-- Modify footer menu -->
      <ul class="list-inline">
          <li><a href="https://www.digitalocean.com/community/tutorials/{{ url_for("home.homepage') }}">Home</a></li>
          <li class="footer-menu-divider">⋅</li>
          {% if current_user.is_authenticated %}
            <li><a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.logout') }}">Logout</a></li>
          {% else %}
            <li><a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.register') }}">Register</a></li>
            <li class="footer-menu-divider">⋅</li>
            <li><a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.login') }}">Login</a></li>
          {% endif %}
      </ul>
      

      Note how we use if-else statements in the templates. Also, take note of the current_user proxy provided by Flask-Login, which allows us to check whether the user is authenticated and to get the user’s username.

      Logging out will take you back to the login page:

      Attempting to access the dashboard page without logging in will redirect you to the login page and display the message we set in the app/__init__.py file:

      Notice that the URL is configured such that once you log in, you will be redirected to the page you initially attempted to access, which in this case is the dashboard.

      Conclusion

      That’s it for Part One! We’ve covered quite a lot: setting up a MySQL database, creating models, migrating the database, and handling registration, login, and logout. Good job for making it this far!

      Watch this space for Part Two, which will cover the CRUD functionality of the app, allowing admin users to add, list, edit, and delete departments and roles, as well as assign them to employees.



      Source link

      Handling CRUD in Laravel With Eloquent ORM


      This Tech Talk is free and open to everyone. Register on Eventbrite here to receive a link to join on Thursday, September 17, 2020, 1:00–2:00 p.m. ET.

      About the Talk

      Laravel helps us to handle database operations. Eloquent ORM lets us avoid writing SQL statements out by hand.

      What You’ll Learn

      • How to use Eloquent ORM to perform CRUD (create, read, update, and delete)
      • How to generate relationships
      • How to manage your database with migrations

      This Talk is Designed For

      Laravel developers that want to build any applications or sites.

      Technical Level

      Beginner. You have basic knowledge of Laravel and want to make CRUD easy.

      About the Presenter

      Chris Sevilleja (@chrisoncode) is the founder of scotch.io and Senior Developer Advocate at DigitalOcean. He loves trying to figure out the most efficient and practical way to build apps that we can ship to our customers.

      How to Join

      To join the live Tech Talk, register here.



      Source link