Building a CRUD Application with Flask and Angular
In this tutorial, we will walk you through creating a **CRUD (Create, Read, Update, Delete)** application with **Flask** as the backend and **Angular** for the frontend. The app will perform CRUD operations on user data (such as first name, last name, email, password, and image) using a **MySQL database**.
🔧 Setting Up Flask with MySQL
First, let’s set up **Flask** to communicate with a **MySQL database**.
Install Required Packages
# Create a virtual environment
python -m venv venv
source venv/bin/activate # For Windows, use venv\Scripts\activate
# Install Flask and Flask MySQL packages
pip install flask flask-sqlalchemy flask-cors flask-uploads
Configure MySQL Database
Make sure MySQL is installed on your machine and create a new database to store the user information. Here are the steps:
# Log in to MySQL
mysql -u root -p
# Create database and user
CREATE DATABASE flask_crud;
CREATE USER 'flaskuser'@'localhost' IDENTIFIED BY 'yourpassword';
GRANT ALL PRIVILEGES ON userdb.* TO 'flaskuser'@'localhost';
FLUSH PRIVILEGES;
# Create the 'users' table
USE flask_crud;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
first_name VARCHAR(50),
last_name VARCHAR(50),
email VARCHAR(100) UNIQUE,
password VARCHAR(100),
image VARCHAR(200)
);
Flask API Setup
Now, let’s set up the **Flask API** to handle CRUD operations for user data.
create app.py file
from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS
from werkzeug.utils import secure_filename
import os
app = Flask(__name__, static_url_path='/uploads', static_folder='uploads')
# Allow CORS for all origins
CORS(app)
# MySQL Configuration using Flapp = Flask(__name__, static_url_path='/uploads', static_folder='uploads')ask-SQLAlchemy
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://myadmin:Angular13@localhost:3308/flask_crud' # Update with your credentials
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
# Secret key for CSRF protection
app.config['SECRET_KEY'] = 'yofdgusdfsdffdsfdsdfddsfsdfrsecretkey'
# Create a folder to store uploaded images
app.config['UPLOAD_FOLDER'] = 'uploads/photos'
if not os.path.exists(app.config['UPLOAD_FOLDER']):
os.makedirs(app.config['UPLOAD_FOLDER'])
# Allowed file extensions for the images
ALLOWED_EXTENSIONS = {'jpg', 'jpeg', 'png'}
# User Model for the database
class Users(db.Model):
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(100), nullable=False)
last_name = db.Column(db.String(100), nullable=False)
email = db.Column(db.String(100), unique=True, nullable=False)
password = db.Column(db.String(100), nullable=False)
image = db.Column(db.String(200)) # Only store the filename, not the full path
def __repr__(self):
return f''
# Function to check allowed file extension
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/api/users', methods=['POST'])
def create_user():
# Check if the image file is in the form data
if 'image' not in request.files:
return jsonify({'message': 'No image file found'}), 400
image = request.files['image']
# Validate the image file extension
if image and allowed_file(image.filename):
filename = secure_filename(image.filename)
image_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
image.save(image_path)
else:
return jsonify({'message': 'Invalid image format. Only jpg, jpeg, and png allowed.'}), 400
# Get other form data (text fields)
data = request.form
# Print the data to inspect which field is missing
print("Received data:", data)
# Validate required fields
required_fields = ['first_name', 'last_name', 'email', 'password']
missing_fields = [field for field in required_fields if not data.get(field)]
if missing_fields:
print(f"Missing fields: {missing_fields}") # This will show the missing fields
return jsonify({'message': f'Missing required fields: {", ".join(missing_fields)}'}), 400
# Retrieve form data
first_name = data['first_name']
last_name = data['last_name']
email = data['email']
password = data['password']
# Create a new user in the database
new_user = Users(
first_name=first_name,
last_name=last_name,
email=email,
password=password,
image=filename # Store only the filename here
)
db.session.add(new_user)
db.session.commit()
return jsonify({'message': 'User created'}), 201
# Route for viewing all users
@app.route('/api/users', methods=['GET'])
def get_all_users():
users = Users.query.all()
users_list = []
for user in users:
users_list.append({
'id': user.id,
'first_name': user.first_name,
'last_name': user.last_name,
'email': user.email,
'image': user.image # Only send the filename, not the full path
})
return jsonify(users_list), 200
# Route for viewing a single user
@app.route('/api/users/', methods=['GET'])
def get_user(id):
user = Users.query.get(id)
if not user:
return jsonify({'message': 'User not found'}), 404
return jsonify({
'id': user.id,
'first_name': user.first_name,
'last_name': user.last_name,
'email': user.email,
'image': user.image
}), 200
@app.route('/api/users/', methods=['PUT'])
def update_user(id):
user = Users.query.get(id)
if not user:
return jsonify({'message': 'User not found'}), 404
data = request.form
user.first_name = data.get('first_name', user.first_name)
user.last_name = data.get('last_name', user.last_name)
user.email = data.get('email', user.email)
user.password = data.get('password', user.password)
# Handle image if provided
if 'image' in request.files:
image = request.files['image']
if image and allowed_file(image.filename):
filename = secure_filename(image.filename)
image_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
image.save(image_path)
user.image = filename
else:
return jsonify({'message': 'Invalid image format'}), 400
db.session.commit()
return jsonify({'message': 'User updated successfully'}), 200
@app.route('/api/users/', methods=['DELETE'])
def delete_user(id):
user = Users.query.get(id)
if not user:
return jsonify({'message': 'User not found'}), 404
# Delete image file if exists
if user.image:
image_path = os.path.join(app.config['UPLOAD_FOLDER'], user.image)
if os.path.exists(image_path):
os.remove(image_path)
db.session.delete(user)
db.session.commit()
return jsonify({'message': 'User deleted successfully'}), 200
if __name__ == '__main__':
with app.app_context():
db.create_all() # Create tables if they don't exist
app.run(debug=True)
Angular User Service and CRUD Components with Image Upload
In this section, we’ll walk through the implementation of the **User Service** and the **Add**, **Update**, and **List** components for CRUD operations in Angular. These components will interact with the **Flask backend** API to manage user data, including the ability to upload an image.
🔧 User Service
The **User Service** is responsible for making HTTP requests to the Flask API to perform CRUD operations on the user data.
# Install Angular CLI if not already installed
npm install -g @angular/cli
# Create a new Angular project
ng new angular-crud-app
# Navigate into the project directory
cd angular-crud-app
# Install HttpClientModule
npm install @angular/common/http
Components
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
interface User {
id?: number;
first_name: string;
last_name: string;
email: string;
password: string;
image: string;
}
@Injectable({
providedIn: 'root'
})
export class UserService {
private apiUrl = 'http://127.0.0.1:5000/api/users';
constructor(private http: HttpClient) { }
getUsers(): Observable<User[]> {
return this.http.get<User[]>(this.apiUrl);
}
createUser(user: User): Observable {
return this.http.post(this.apiUrl, user);
}
updateUser(id: number, user: User): Observable {
return this.http.put(`${this.apiUrl}/${id}`, user);
}
deleteUser(id: number): Observable {
return this.http.delete(`${this.apiUrl}/${id}`);
}
}
🏗 Add User Component
The **Add User** component allows the user to input data and create a new user. This will call the **UserService** to send a request to the backend to create a new user in the database, including an image.
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { UserService } from '../user.service';
@Component({
selector: 'app-user-create',
templateUrl: './user-create.component.html',
styleUrls: ['./user-create.component.css']
})
export class UserCreateComponent {
user = {
first_name: '',
last_name: '',
email: '',
password: '',
image: null // Initialize image as null
};
constructor(private userService: UserService, private router: Router) {}
onFileSelected(event: any) {
this.user.image = event.target.files[0]; // Store the selected file
}
createUser() {
const formData: FormData = new FormData();
formData.append('first_name', this.user.first_name);
formData.append('last_name', this.user.last_name);
formData.append('email', this.user.email);
formData.append('password', this.user.password);
if (this.user.image) {
formData.append('image', this.user.image, this.user.image.name);
}
this.userService.createUser(formData).subscribe(() => {
this.router.navigate(['/users']);
});
}
}
🛠 Update User Component
The **Update User** component allows the user to edit their details. It retrieves the user by ID, allows for updates, and sends the updated data to the backend, including the option to upload an image.
import { Component, OnInit } from '@angular/core';
import { UserService } from '../user.service';
import { ActivatedRoute, Router } from '@angular/router';
@Component({
selector: 'app-user-update',
templateUrl: './user-update.component.html',
styleUrls: ['./user-update.component.css']
})
export class UserUpdateComponent implements OnInit {
user: any = {};
constructor(
private userService: UserService,
private route: ActivatedRoute,
private router: Router
) {}
ngOnInit(): void {
const id = this.route.snapshot.paramMap.get('id');
this.userService.getUsers().subscribe((data: any[]) => {
this.user = data.find(u => u.id === +id);
});
}
onFileSelected(event: any) {
this.user.image = event.target.files[0]; // Store the selected file
}
updateUser() {
const formData: FormData = new FormData();
formData.append('first_name', this.user.first_name);
formData.append('last_name', this.user.last_name);
formData.append('email', this.user.email);
formData.append('password', this.user.password);
if (this.user.image) {
formData.append('image', this.user.image, this.user.image.name);
}
this.userService.updateUser(this.user.id, formData).subscribe(() => {
this.router.navigate(['/users']);
});
}
}
📋 User List Component
The **User List** component fetches all users from the backend and displays them. It also allows for deleting and updating users, as well as viewing their profile image.
import { Component, OnInit } from '@angular/core';
import { UserService } from '../user.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html',
styleUrls: ['./user-list.component.css']
})
export class UserListComponent implements OnInit {
users: any[] = [];
constructor(private userService: UserService, private router: Router) {}
ngOnInit(): void {
this.getUsers();
}
getUsers() {
this.userService.getUsers().subscribe((data: any) => {
this.users = data;
});
}
deleteUser(id: number) {
this.userService.deleteUser(id).subscribe(() => {
this.getUsers();
});
}
updateUser(id: number) {
this.router.navigate([`/update/${id}`]);
}
}
Running the Application
Run Flask Backend
python app.py
Run Angular Frontend
ng serve
The Angular frontend will be available at `http://localhost:4200`, and the Flask backend will run at `http://127.0.0.1:5000`.
Conclusion
You’ve now created a **full-stack CRUD application** using **Flask** for the backend and **Angular** for the frontend, connected to a **MySQL** database. You can extend this system by adding authentication, form validation, or additional user features.