💨

中間テーブルとは #2

2023/08/28に公開

概要

本記事では、中間テーブルのデータ表示方法を解説する。「中間テーブルとは #1」に続く記事なので、より詳細を知りたい方はそちらを参考いただきたい。
https://zenn.dev/oreilly_ota/articles/a9a3b4d1dabec0

中間テーブルのおさらい

中間テーブルのコードを以下に記載する。

order_items = db.Table('order_items',
    db.Column('order_id', db.Integer, db.ForeignKey('order.id'), primary_key=True),
    db.Column('product_id', db.Integer, db.ForeignKey('product.id'), primary_key=True)
)

class Order(db.Model):
  id = db.Column(db.Integer, primary_key=True)
  status = db.Column(db.String(), nullable=False)
  products = db.relationship('Product', secondary=order_items,
      backref=db.backref('orders', lazy=True))

class Product(db.Model):
  id = db.Column(db.Integer, primary_key=True)
  name = db.Column(db.String(), nullable=False)

order_items / Order / Productの三つのテーブルが存在し、order_itemsが中間テーブルである。

  • Orderテーブル
    • 顧客の注文を管理するテーブル。注文idと注文状態(準備中、提供ずみ等)がカラムにある。
  • Productテーブル
    • 商品情報を管理するテーブル。商品idと商品名がカラムにある。
  • order_itemsテーブル
    • order_idとproduct_idがカラムにある。それぞれ、Orderテーブルのid、そしてProductテーブルのidと紐づくように管理されている。

Order -> Product, Product -> Orderの参照

Order -> Productの参照

各注文について、「どんな商品があったっけ?」とか、もしくは各商品に対して、「どの注文に含まれてたっけ?」と参照したい場合がある。そういうときに、Orderテーブルの以下コードが重要となる。

  products = db.relationship('Product', secondary=order_items,
      backref=db.backref('orders', lazy=True))

Orderテーブルの中でProductテーブルの定義がされているため、そちらへの情報参照が可能となる。

@app.route('/order/<int:order_id>/products', methods=['GET'])
def get_products_by_order(order_id):
    order = Order.query.get(order_id)
    if not order:
        return jsonify({'error': 'Order not found'}), 404
    products = [{'id': product.id, 'name': product.name} for product in order.products]
    return jsonify(products)

例えば、上記のように情報参照できる。まず最初に、order = Order.query.get(order_id)によって、特定のorder情報を取得できる。そのorder情報を取得後、order.productsによって、その商品情報を取得できる。なぜなら、先ほども説明したように、すでにOrderテーブルで、productsの定義がされているからである。

つまり下記コードは、特定のorderのidに対し商品情報を取得して、それをリスト化した情報をproductsに格納するコードである。

products = [{'id': product.id, 'name': product.name} for product in order.products]

Product -> Orderの参照

逆にProductテーブルからOrderテーブルの参照をしたい場合、backrefでordersが定義されているので、それを利用する。

  products = db.relationship('Product', secondary=order_items,
      backref=db.backref('orders', lazy=True))

例えば、コードは以下のイメージ。

  • まず、product = Product.query.get(product_id)で、特定のproductのid情報を取得する。例えば、product id = 3のproduct情報を取得する。
  • 次に、そのproduct id = 3のorders情報を取得する。id = 3であるorders情報を全て取得しリスト化して、ordersに格納する。
@app.route('/product/<int:product_id>/orders', methods=['GET'])
def get_orders_by_product(product_id):
    product = Product.query.get(product_id)
    if not product:
        return jsonify({'error': 'Product not found'}), 404
    orders = [{'id': order.id, 'status': order.status} for order in product.orders]
    return jsonify(orders)

Frontendでの情報表示

上記内容により、Backend側のエンドポイントは開発できた。次に、Frontendでのデータ表示を説明する。全体のコードは、以下の通り。

<template>
  <div>
    <h2>Products for Order ID: {{ orderId }}</h2>
    <ul>
      <li v-for="product in products" :key="product.id">
        {{ product.name }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      orderId: null,
      products: []
    };
  },
  created() {
    this.orderId = this.$route.params.orderId;  // Assuming you're using vue-router
    this.fetchProducts();
  },
  methods: {
    async fetchProducts() {
      try {
        let response = await fetch(`/order/${this.orderId}/products`);
        if (response.ok) {
          this.products = await response.json();
        } else {
          console.error("Error fetching products:", response.statusText);
        }
      } catch (error) {
        console.error("There was an error fetching the products:", error);
      }
    }
  }
};
</script>

まず最初に、methodsにてfetchProducts関数を作る。ここでは、先ほど作ったエンドポイントからlet response = await fetch(/order/${this.orderId}/products);という形でresponseに情報格納して、それをthis.products = await response.json();とjson化しproductsに格納する。

    async fetchProducts() {
      try {
        let response = await fetch(`/order/${this.orderId}/products`);
        if (response.ok) {
          this.products = await response.json();
        } else {
          console.error("Error fetching products:", response.statusText);
        }
      } catch (error) {
        console.error("There was an error fetching the products:", error);
      }

そして下記コードにて、for-loopでproductsリストの各情報を表示する。

<li v-for="product in products" :key="product.id">
{{ product.name }}
</li>

まとめ

中間テーブルの情報表示って、どうやってやるんだっけ?と気になって、備忘録的に記事を作成した。疑問点、不明点、質問があれば、コメントお願いします。

Discussion