見出し画像

PYTHONではじめてのWEBサイトを作成 10

Python DjangoではじめてWEBサイトを作る。
はや1ヶ月が経過しました。

頑張っていきましょう

今日は全国バインミーのストアリストを最新に更新

そのためにmodels.pyを修正してfecthファイルで新しくデータを取得しなおします。

Let's Start


全国バインミーのストアリストを最新する準備

model.py を更新
registered_date = models.DateField() # 登録月
を挿入

class Store(models.Model):
    name = models.CharField(max_length=255)  # 店名
    prefecture = models.CharField(max_length=100)  # 都道府県
    city = models.CharField(max_length=100)  # 市区町村
    website = models.URLField(blank=True, null=True)
    url = models.URLField()  # URL
    rating = models.FloatField()  # レーティング
    review_count = models.IntegerField()  # レビュー数
    latitude = models.FloatField()  # 緯度
    longitude = models.FloatField()  # 経度
    place_id = models.CharField(max_length=255, unique=True)  # Google Place ID
    weighted_score = models.FloatField(null=True, blank=True)  # 加重スコア
     registered_date = models.DateField()  # 取得日
    registered_month = models.CharField(max_length=7, default='2024-01')  # 取得月 (YYYY-MM形式)
python manage.py makemigrations
python manage.py migrate

データを削除してから取得しなおし

banhmilove_app/management/commands/delete_all_stores.py

from django.core.management.base import BaseCommand
from banhmilove_app.models import Store

class Command(BaseCommand):
    help = 'Delete all existing store data'

    def handle(self, *args, **options):
        Store.objects.all().delete()
        self.stdout.write(self.style.SUCCESS('Successfully deleted all store data'))
python manage.py delete_all_stores

一旦データを削除しました。


データ取得のスクリプト修正

  • 取得月のデータ

  • 取得日のデータ

  • 都道府県名を日本語で表示する辞書を追加

import datetime
from django.core.management.base import BaseCommand
import requests
from banhmilove_app.models import Store, MonthlyData
from django.db import transaction

class Command(BaseCommand):
    help = 'Fetches Banh Mi shop data from Japan'

    PREFECTURE_MAPPING = {
        "Hokkaido": "北海道", "Aomori": "青森県", "Iwate": "岩手県", "Miyagi": "宮城県",
        "Akita": "秋田県", "Yamagata": "山形県", "Fukushima": "福島県", "Ibaraki": "茨城県",
        "Tochigi": "栃木県", "Gunma": "群馬県", "Saitama": "埼玉県", "Chiba": "千葉県",
        "Tokyo": "東京都", "Kanagawa": "神奈川県", "Niigata": "新潟県", "Toyama": "富山県",
        "Ishikawa": "石川県", "Fukui": "福井県", "Yamanashi": "山梨県", "Nagano": "長野県",
        "Gifu": "岐阜県", "Shizuoka": "静岡県", "Aichi": "愛知県", "Mie": "三重県",
        "Shiga": "滋賀県", "Kyoto": "京都府", "Osaka": "大阪府", "Hyogo": "兵庫県",
        "Nara": "奈良県", "Wakayama": "和歌山県", "Tottori": "鳥取県", "Shimane": "島根県",
        "Okayama": "岡山県", "Hiroshima": "広島県", "Yamaguchi": "山口県", "Tokushima": "徳島県",
        "Kagawa": "香川県", "Ehime": "愛媛県", "Kochi": "高知県", "Fukuoka": "福岡県",
        "Saga": "佐賀県", "Nagasaki": "長崎県", "Kumamoto": "熊本県", "Oita": "大分県",
        "Miyazaki": "宮崎県", "Kagoshima": "鹿児島県", "Okinawa": "沖縄県"
    }

    def handle(self, *args, **options):
        self.stdout.write("Fetching Banh Mi shops in Japan...")
        self.fetch_places()

    def fetch_places(self):
        locations = [
            "Hokkaido", "Aomori", "Iwate", "Miyagi", "Akita", "Yamagata", "Fukushima",
            "Ibaraki", "Tochigi", "Gunma", "Saitama", "Chiba", "Tokyo", "Kanagawa",
            "Niigata", "Toyama", "Ishikawa", "Fukui", "Yamanashi", "Nagano", "Gifu", 
            "Shizuoka", "Aichi", "Mie", "Shiga", "Kyoto", "Osaka", "Hyogo", "Nara", 
            "Wakayama", "Tottori", "Shimane", "Okayama", "Hiroshima", "Yamaguchi", 
            "Tokushima", "Kagawa", "Ehime", "Kochi", "Fukuoka", "Saga", "Nagasaki", 
            "Kumamoto", "Oita", "Miyazaki", "Kagoshima", "Okinawa"
        ]
        current_date = datetime.date.today()
        current_month = current_date.strftime("%Y-%m")
        
        for location in locations:
            url = "https://maps.googleapis.com/maps/api/place/textsearch/json"
            params = {
                "query": f"Banh Mi shop in {location} Japan",
                "key": "YOUR_GOOGLE_API_KEY",  # 本番キーが公開されないように注意
                "language": "ja"
            }
            response = requests.get(url, params=params)
            if response.status_code == 200:
                results = response.json().get('results', [])
                if not results:
                    self.stdout.write("No data received from API.")
                else:
                    self.save_data(results, location, current_date, current_month)
                    self.stdout.write(f"Stores in {location} fetched and saved successfully.")
            else:
                self.stdout.write(f"Failed to fetch data for {location}: {response.status_code} - {response.text}")

    def save_data(self, results, location, current_date, current_month):
        with transaction.atomic():
            for place in results:
                city = self.fetch_place_details(place['place_id'])
                prefecture_jp = self.PREFECTURE_MAPPING.get(location, location)
                store, created = Store.objects.get_or_create(
                    place_id=place.get('place_id'),
                    defaults={
                        'name': place.get('name'),
                        'prefecture': prefecture_jp,
                        'city': city if city else "N/A",
                        'url': place.get('website', ''),
                        'rating': place.get('rating', 0),
                        'review_count': place.get('user_ratings_total', 0),
                        'latitude': place.get('geometry', {}).get('location', {}).get('lat', 0),
                        'longitude': place.get('geometry', {}).get('location', {}).get('lng', 0),
                        'registered_date': current_date,
                        'registered_month': current_month
                    }
                )
                if not created:
                    store.name = place.get('name')
                    store.prefecture = prefecture_jp
                    store.city = city if city else "N/A"
                    store.url = place.get('website', '')
                    store.rating = place.get('rating', 0)
                    store.review_count = place.get('user_ratings_total', 0)
                    store.latitude = place.get('geometry', {}).get('location', {}).get('lat', 0)
                    store.longitude = place.get('geometry', {}).get('location', {}).get('lng', 0)
                    store.registered_date = current_date
                    store.registered_month = current_month
                    store.save()

                MonthlyData.objects.update_or_create(
                    date=current_date,
                    store=store,
                    defaults={
                        'ranking': 0,  # ランキングは後で更新する
                        'weighted_score': store.weighted_score
                    }
                )
                print(f"Saved store: {store.name} with registered date: {store.registered_date}")

    def fetch_place_details(self, place_id):
        detail_url = "https://maps.googleapis.com/maps/api/place/details/json"
        params = {
            "place_id": place_id,
            "fields": "address_component,website",
            "key": "YOUR_GOOGLE_API_KEY",  # 同様にAPIキーを安全に保管
            "language": "ja"
        }
        response = requests.get(detail_url, params=params)
        if response.status_code == 200:
            details = response.json().get('result', {})
            city = None
            for component in details.get('address_components', []):
                if 'locality' in component['types']:
                    city = component['long_name']
            return city
        return None

今月オープンしたストアリストの表示

def new_store(request):
    current_month = timezone.now().month
    current_year = timezone.now().year

    new_stores = Store.objects.filter(registered_date__year=current_year, registered_date__month=current_month)

    return render(request, 'banhmilove_app/new_store.html', {
        'new_stores': new_stores,
    })

ストアリストに登録月のカラムを追加し、5月にあって6月にない店舗を「閉鎖」として表示するための手順を示します。

新規オープンストアの表示


new_store.html

{% extends 'banhmilove_app/base.html' %}

{% block content %}
<div class="container">
    <h1>今月オープンしたバインミー店</h1>

    {% if new_stores %}
    <table class="table table-bordered ranking-table">
        <thead class="thead-light">
            <tr>
                <th>店名</th>
                <th>所在地</th>
                <th>登録月</th>
                <th>google<br>レーティング</th>
                <th>google<br>レビュー数</th>
            </tr>
        </thead>
        <tbody>
            {% for store in new_stores %}
            <tr>
                <td>{{ store.name }}</td>
                <td>{{ store.prefecture }}</td>
                <td>{{ store.registered_date }}</td>
                <td>{{ store.rating }}</td>
                <td>{{ store.review_count }}</td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
    {% else %}
    <p>今月オープンしたバインミー店はありません。</p>
    {% endif %}
</div>
{% endblock %}


ストアリストのビューも修正

ストアリストのビューを修正して、5月と6月のデータを適切に取得し、重複を排除し、閉鎖店舗を識別するようにします。

from django.core.paginator import Paginator
from django.shortcuts import render
from .models import Store

def store_list(request):
    prefecture = request.GET.get('prefecture')
    name_query = request.GET.get('name')
    
    # 5月のストアを取得
    may_stores = Store.objects.filter(registered_date__year=2024, registered_date__month=5)
    # 6月のストアを取得
    june_stores = Store.objects.filter(registered_date__year=2024, registered_date__month=6)

    # 6月のストアIDリスト
    june_store_ids = june_stores.values_list('id', flat=True)

    # 閉鎖した店舗リスト
    closed_stores = may_stores.exclude(id__in=june_store_ids)

    # 5月と6月のストアリストをマージ
    stores = june_stores.union(may_stores).order_by('registered_date')

    # 検索フィルタ
    if prefecture:
        stores = stores.filter(prefecture=prefecture)
    
    if name_query:
        stores = stores.filter(name__icontains=name_query)

    paginator = Paginator(stores, 50)  # 1ページに表示する店舗数を50に設定
    page_number = request.GET.get('page')
    page_obj = paginator.get_page(page_number)
    
    prefectures = Store.objects.values_list('prefecture', flat=True).distinct()

    return render(request, 'banhmilove_app/store_list.html', {
        'page_obj': page_obj,
        'prefecture': prefecture,
        'name_query': name_query,
        'prefectures': prefectures,
        'closed_stores': closed_stores,
    })

store_list.htmlの更新

{% extends 'banhmilove_app/base.html' %}

{% block content %}
<div class="container">
    <h1>Store List</h1>

    <!-- 検索フォーム -->
    <form method="get" action="{% url 'store_list' %}" class="form-inline mb-3">
        <div class="form-group mr-2">
            <label for="name" class="mr-2">店舗名:</label>
            <input type="text" name="name" id="name" value="{{ name_query }}" class="form-control" placeholder="店舗名を入力">
        </div>
        <div class="form-group mr-2">
            <label for="prefecture">都道府県:</label>
            <select name="prefecture" id="prefecture" class="form-control">
                <option value="">All</option>
                {% for pref in prefectures %}
                <option value="{{ pref }}" {% if pref == prefecture %}selected{% endif %}>{{ pref }}</option>
                {% endfor %}
            </select>
        </div>
        <button type="submit" class="btn btn-primary mt-2">Search</button>
    </form>

    <table class="ranking-table mt-3">
        <thead>
            <tr>
                <th>店名</th>
                <th>都道府県</th>
                <th>市区町村</th>
                <th>google<br>レーティング</th>
                <th>google<br>レビュー数</th>
                <th>Website</th>
                <th>登録月</th>
            </tr>
        </thead>
        <tbody>
            {% for store in page_obj %}
            <tr>
                <td>{{ store.name }}</td>
                <td>{{ store.prefecture }}</td>
                <td>{{ store.city }}</td>
                <td>{{ store.rating }}</td>
                <td>{{ store.review_count }}</td>
                <td><a href="{{ store.url }}" target="_blank">Visit Website</a></td>
                <td>{{ store.registered_date|date:"Y年m月" }}</td>
            </tr>
            {% empty %}
            <tr>
                <td colspan="7">No stores found.</td>
            </tr>
            {% endfor %}
            <!-- 閉鎖した店舗 -->
            {% for store in closed_stores %}
            <tr>
                <td>{{ store.name }}</td>
                <td>{{ store.prefecture }}</td>
                <td>{{ store.city }}</td>
                <td>閉鎖</td>
                <td>閉鎖</td>
                <td>閉鎖</td>
                <td>{{ store.registered_date|date:"Y年m月" }} (閉鎖)</td>
            </tr>
            {% endfor %}
        </tbody>
    </table>

    <!-- ページネーション -->
    <nav aria-label="Page navigation example" class="mt-4">
        <ul class="pagination">
            {% if page_obj.has_previous %}
            <li class="page-item">
                <a class="page-link" href="?page={{ page_obj.previous_page_number }}{% if prefecture %}&prefecture={{ prefecture }}{% endif %}" aria-label="Previous">
                    <span aria-hidden="true">&laquo;</span>
                </a>
            </li>
            {% endif %}
            {% for num in page_obj.paginator.page_range %}
            <li class="page-item {% if page_obj.number == num %}active{% endif %}">
                <a class="page-link" href="?page={{ num }}{% if prefecture %}&prefecture={{ prefecture }}{% endif %}">{{ num }}</a>
            </li>
            {% endfor %}
            {% if page_obj.has_next %}
            <li class="page-item">
                <a class="page-link" href="?page={{ page_obj.next_page_number }}{% if prefecture %}&prefecture={{ prefecture }}{% endif %}" aria-label="Next">
                    <span aria-hidden="true">&raquo;</span>
                </a>
            </li>
            {% endif %}
        </ul>
    </nav>
</div>
{% endblock %}

まとめ

だいぶ間が空いてしまいましたが、今回はデータを再取得してショップリストを表示しました!

続いて分析ページに入ります。


この記事が気に入ったらサポートをしてみませんか?