How to do it...

Let's say we want to have product categories where each category can have multiple products, but each product should have only one category. Let's do this by modifying some files from the preceding application. We will make modifications to both models and views. In models, we will add a Category model, and, in views, we will add new methods to handle category-related calls and also modify the existing methods to accommodate the newly added feature.

First, modify the models.py file to add the Category model and make some modifications to the Product model:

from my_app import db 
 
class Product(db.Model): 
    id = db.Column(db.Integer, primary_key=True) 
    name = db.Column(db.String(255)) 
    price = db.Column(db.Float) 
    category_id = db.Column(db.Integer, 
db.ForeignKey('category.id')) category = db.relationship( 'Category', backref=db.backref('products', lazy='dynamic') ) def __init__(self, name, price, category): self.name = name self.price = price self.category = category def __repr__(self): return '<Product %d>' % self.id

In the preceding Product model, check the newly added fields for category_id and category. category_id is the foreign key to the Category model, and category represents the relationship table. As evident from the definitions themselves, one of them is a relationship, and the other uses this relationship to store the foreign key value in the database. This is a simple many-to-one relationship from product to category. Also, notice the backref argument in the category field; this argument allows us to access products from the Category model by writing something as simple as category.products in our views. This acts like the one-to-many relationship from the other end.

Just adding the field to the model would not get reflected in the database in the right away. You might need to drop the whole database and then run the application again or run migrations, which shall be covered in the next recipe, Migrating databases using Alembic and Flask-Migrate.

Create a Category model which has just one field called name, as shown below:

class Category(db.Model): 
    id = db.Column(db.Integer, primary_key=True) 
    name = db.Column(db.String(100)) 
 
    def __init__(self, name): 
        self.name = name 
 
    def __repr__(self): 
        return '<Category %d>' % self.id 

The preceding code is the Category model, which has just one field called name.

Now, modify the views.py to accommodate the change in the models.

Make the first change in the products() method.

from my_app.catalog.models import Product, Category 
 
@catalog.route('/products') 
def products(): 
    products = Product.query.all() 
    res = {} 
    for product in products: 
        res[product.id] = { 
            'name': product.name, 
            'price': product.price, 
            'category': product.category.name 
        } 
    return jsonify(res) 

Here, we have just one change where we are sending the category name in the product's JSON data, which is being generated to be returned as a response when a request is made to the preceding endpoint.

Change the create_products() method to look for category before creating the product:

@catalog.route('/product-create', methods=['POST',]) 
def create_product(): 
    name = request.form.get('name') 
    price = request.form.get('price') 
    categ_name = request.form.get('category') 
    category = Category.query.filter_by(name=categ_name).first() 
    if not category: 
        category = Category(categ_name) 
    product = Product(name, price, category) 
    db.session.add(product) 
    db.session.commit() 
    return 'Product created.' 

Here, we will first search for an existing category with the category name in the request. If an existing category is found, we will use the same in the product creation; otherwise, we will create a new category.

Create a new method create_category() to handle creation of category.

@catalog.route('/category-create', methods=['POST',]) 
def create_category(): 
    name = request.form.get('name') 
    category = Category(name) 
    db.session.add(category) 
    db.session.commit() 
    return 'Category created.' 

The preceding code is a relatively simple method for creating a category using the name provided in the request.

Create a new method categories() to handle listing of all categories and corresponding products.

@catalog.route('/categories') 
def categories(): 
    categories = Category.query.all() 
    res = {} 
    for category in categories: 
        res[category.id] = { 
            'name': category.name 
        } 
        for product in category.products: 
            res[category.id]['products'] = { 
                'id': product.id, 
                'name': product.name, 
                'price': product.price 
            } 
    return jsonify(res) 

The preceding method does a bit of tricky stuff. Here, we fetched all the categories from the database, and then for each category, we fetched all the products and then returned all the data as a JSON dump.