↓↓クリックして頂けると励みになります。
【48 | 配達人プロフィールページ】 << 【ホーム】 >> 【50 | 取引の更新】
「core/models.py」ファイルを編集します。
記述追加 【Desktop/crowdsource/core/models.py】22行目
import uuid from django.db import models from django.contrib.auth.models import User from django.utils import timezone # Create your models here. class Customer(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) avatar = models.ImageField(upload_to='customer/avatars/', blank=True, null=True) phone_number = models.CharField(max_length=50, blank=True) stripe_customer_id = models.CharField(max_length=255, blank=True) stripe_payment_method_id = models.CharField(max_length=255, blank=True) stripe_card_last4 = models.CharField(max_length=255, blank=True) def __str__(self): return self.user.get_full_name() class Courier(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) lat = models.FloatField(default=0) lng = models.FloatField(default=0) paypal_email = models.EmailField('PayPalメールアドレス', max_length=255, blank=True) def __str__(self): return self.user.get_full_name() class Category(models.Model): slug = models.CharField(max_length=255, unique=True) name = models.CharField(max_length=255) def __str__(self): return self.name class Job(models.Model): SMALL_SIZE = "small" MEDIUM_SIZE = "medium" LARGE_SIZE = "large" SIZES = ( (SMALL_SIZE, '小'), (MEDIUM_SIZE, '中'), (LARGE_SIZE, '大'), ) CREATING_STATUS = 'creating' PROCESSING_STATUS = 'processing' PICKING_STATUS = 'picking' DELIVERING_STATUS = 'delivering' COMPLETED_STATUS = 'completed' CANCELED_STATUS = 'canceled' STATUSES = ( (CREATING_STATUS, 'Creating'), (PROCESSING_STATUS, 'Processing'), (PICKING_STATUS, 'Picking'), (DELIVERING_STATUS, 'Delivering'), (COMPLETED_STATUS, 'Completed'), (CANCELED_STATUS, 'Canceled'), ) # Step 1 id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) customer = models.ForeignKey(Customer, on_delete=models.CASCADE, verbose_name='配達依頼人') courier = models.ForeignKey(Courier, on_delete=models.CASCADE, null=True, blank=True) name = models.CharField('配達依頼名', max_length=255) description = models.CharField('備考', max_length=255) category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True, verbose_name='カテゴリー') size = models.CharField('サイズ', max_length=20, choices=SIZES, default=MEDIUM_SIZE) quantity = models.IntegerField('数量', default=1) photo = models.ImageField('写真', upload_to='job/photos/') status = models.CharField('状態', max_length=20, choices=STATUSES, default=CREATING_STATUS) created_at = models.DateTimeField(default=timezone.now, verbose_name='登録日') # Step 2 pickup_address = models.CharField('荷物受取先住所', max_length=255, blank=True) pickup_lat = models.FloatField('荷物受取先緯度', default=0) pickup_lng = models.FloatField('荷物受取先経度', default=0) pickup_name = models.CharField('依頼人氏名', max_length=255, blank=True) pickup_phone = models.CharField('依頼人電話番号', max_length=50, blank=True) # Step 3 delivery_address = models.CharField('配達先住所', max_length=255, blank=True) delivery_lat = models.FloatField('配達先緯度', default=0) delivery_lng = models.FloatField('配達先経度',default=0) delivery_name = models.CharField('配達先氏名',max_length=255, blank=True) delivery_phone = models.CharField('配達先電話番号',max_length=50, blank=True) # Step 4 duration = models.IntegerField('移動時間' ,default=0) distance = models.FloatField('距離' ,default=0) price = models.IntegerField('料金', default=0) # その他 pickup_photo = models.ImageField(upload_to='job/pickup_photos/', null=True, blank=True) pickedup_at = models.DateTimeField(null=True, blank=True) delivery_photo = models.ImageField(upload_to='job/delivery_photos/', null=True, blank=True) delivered_at = models.DateTimeField(null=True, blank=True) def __str__(self): return self.description class Transaction(models.Model): stripe_payment_intent_id = models.CharField(max_length=255, unique=True) job = models.ForeignKey(Job, on_delete=models.CASCADE) amount = models.FloatField(default=0) created_at = models.DateTimeField(default=timezone.now) def __str__(self): return self.stripe_payment_intent_id
マイグレーションファイルを作成します。
コマンド
python manage.py makemigrations
マイグレーションを適用します。
コマンド
python manage.py migrate
「crowdsource/urls.py」ファイルを編集します。
記述追加 【Desktop/crowdsource/crowdsource/urls.py】32行目
from django.contrib import admin from django.urls import path, include from django.contrib.auth import views as auth_views from django.conf import settings from django.conf.urls.static import static from core import views from core.customer import views as customer_views from core.courier import views as courier_views, apis as courier_apis customer_urlpatters = [ path('', customer_views.home, name="home"), path('profile/', customer_views.profile_page, name="profile"), path('payment_method/', customer_views.payment_method_page, name="payment_method"), path('create_job/', customer_views.create_job_page, name="create_job"), path('jobs/current/', customer_views.current_jobs_page, name="current_jobs"), path('jobs/archived/', customer_views.archived_jobs_page, name="archived_jobs"), path('jobs/<job_id>/', customer_views.job_page, name="job"), ] courier_urlpatters = [ path('', courier_views.home, name="home"), path('jobs/available/', courier_views.available_jobs_page, name="available_jobs"), path('jobs/available/<id>/', courier_views.available_job_page, name="available_job"), path('jobs/current/', courier_views.current_job_page, name="current_job"), path('jobs/current/<id>/take_photo/', courier_views.current_job_take_photo_page, name="current_job_take_photo"), path('jobs/complete/', courier_views.job_complete_page, name="job_complete"), path('jobs/archived/', courier_views.archived_jobs_page, name="archived_jobs"), path('profile/', courier_views.profile_page, name="profile"), path('payout_method/', courier_views.payout_method_page, name="payout_method"), path('api/jobs/available/', courier_apis.available_jobs_api, name="available_jobs_api"), path('api/jobs/current/<id>/update/', courier_apis.current_job_update_api, name="current_job_update_api"), ] urlpatterns = [ path('admin/', admin.site.urls), path('oauth/', include('social_django.urls', namespace='social')), path('', views.home), path('sign-in/', auth_views.LoginView.as_view(template_name="sign_in.html")), path('sign-out/', auth_views.LogoutView.as_view(next_page="/")), path('sign-up/', views.sign_up), path('customer/', include((customer_urlpatters, 'customer'))), path('courier/', include((courier_urlpatters, 'courier'))), ] if settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
「core/courier」フォルダに「forms.py」ファイルを新規作成します。
新規作成 【Desktop/crowdsource/core/courier/forms.py】
from django import forms from core.models import Courier class PayoutForm(forms.ModelForm): class Meta: model = Courier fields = ('paypal_email',)
「core/courier/views.py」ファイルを編集します。
記述編集 【Desktop/crowdsource/core/courier/views.py】
from django.shortcuts import render, redirect from django.contrib.auth.decorators import login_required from django.urls import reverse from django.conf import settings from django.contrib import messages from core.models import * from core.courier import forms @login_required(login_url="/sign-in/?next=/courier/") def home(request): return redirect(reverse('courier:available_jobs')) @login_required(login_url="/sign-in/?next=/courier/") def available_jobs_page(request): return render(request, 'courier/available_jobs.html', { "GOOGLE_MAP_API_KEY": settings.GOOGLE_MAP_API_KEY }) @login_required(login_url="/sign-in/?next=/courier/") def available_job_page(request, id): job = Job.objects.filter(id=id, status=Job.PROCESSING_STATUS).last() if not job: return redirect(reverse('courier:available_jobs')) if request.method == 'POST': job.courier = request.user.courier job.status = Job.PICKING_STATUS job.save() return redirect(reverse('courier:available_jobs')) return render(request, 'courier/available_job.html', { "job": job }) @login_required(login_url="/sign-in/?next=/courier/") def current_job_page(request): job = Job.objects.filter( courier=request.user.courier, status__in = [ Job.PICKING_STATUS, Job.DELIVERING_STATUS ] ).last() return render(request, 'courier/current_job.html', { "job": job, "GOOGLE_MAP_API_KEY": settings.GOOGLE_MAP_API_KEY }) @login_required(login_url="/sign-in/?next=/courier/") def current_job_take_photo_page(request, id): job = Job.objects.filter( id=id, courier=request.user.courier, status__in=[ Job.PICKING_STATUS, Job.DELIVERING_STATUS ] ).last() if not job: return redirect(reverse('courier:current_job')) return render(request, 'courier/current_job_take_photo.html', { "job": job }) @login_required(login_url="/sign-in/?next=/courier/") def job_complete_page(request): return render(request, 'courier/job_complete.html') @login_required(login_url="/sign-in/?next=/courier/") def archived_jobs_page(request): jobs = Job.objects.filter( courier=request.user.courier, status=Job.COMPLETED_STATUS ) return render(request, 'courier/archived_jobs.html', { "jobs": jobs }) @login_required(login_url="/sign-in/?next=/courier/") def profile_page(request): jobs = Job.objects.filter( courier=request.user.courier, status=Job.COMPLETED_STATUS ) total_earnings = round(sum(job.price for job in jobs) * 0.8) #配送業者の収入は総額の8割 total_jobs = len(jobs) total_km = sum(job.distance for job in jobs) return render(request, 'courier/profile.html', { "total_earnings": total_earnings, "total_jobs": total_jobs, "total_km": total_km }) @login_required(login_url="/sign-in/?next=/courier/") def payout_method_page(request): payout_form = forms.PayoutForm(instance=request.user.courier) if request.method == 'POST': payout_form = forms.PayoutForm(request.POST, instance=request.user.courier) if payout_form.is_valid(): payout_form.save() messages.success(request, "支払い用 Eメールアドレスを更新しました。") return redirect(reverse('courier:profile')) return render(request, 'courier/payout_method.html', { 'payout_form': payout_form })
「core/templates/courier」フォルダに「payout_method.html」ファイルを新規作成します。
新規作成 【Desktop/crowdsource/core/templates/courier/payout_method.html】
{% extends 'courier/base.html' %} {% load bootstrap4 %} {% block head %} <style> .header { position: fixed; top: 0; left: 0; right: 0; height: 60px; display: flex; align-items: center; padding: 0 20px; background-color: #6f00be; box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2); } </style> {% endblock %} {% block content %} <div class="header"> <a href="{% url 'courier:profile' %}" class="mr-2"> <i class="fas fa-chevron-left text-light"></i> </a> <h5 class="mt-1 mb-0 text-light">プロフィール</h5> </div> <div class="container-fluid" style="padding-top: 80px"> <form method="POST"> {% csrf_token %} {% bootstrap_form payout_form %} <button type="submit" class="btn btn-block btn-danger">保存する</button> </form> </div> {% endblock %}
「core/templates/courier/profile.html」ファイルを編集します。
記述編集 【Desktop/crowdsource/core/templates/courier/profile.html】
{% extends 'courier/base.html' %} {% load static %} {% block content %} <div class="media align-items-center bg-info text-light p-3"> <img src="{% static 'img/avatar.png' %}" class="rounded-circle" width="60" height="60"> <div class="media-body ml-4"> <h4 class="mb-0">{{ request.user.get_full_name }}</h4> </div> </div> <div class="mt-4 p-2 mb-5"> <b class="text-secondary">お支払い</b> <a href="{% url 'courier:payout_method' %}" class="btn btn-outline-secondary btn-block btn-md mt-2"> 登録する </a> </div> <div class="mt-2 p-2"> <b class="text-secondary">配達記録</b> <hr /> <div class="d-flex text-center"> <div class="flex-grow-1"> <h4 class="text-success">{{ total_earnings }}円</h4> <span class="text-secondary">総収入</span> </div> <div class="flex-grow-1"> <h4 class="text-success">{{ total_jobs }} 件</h4> <span class="text-secondary">配達完了</span> </div> <div class="flex-grow-1"> <h4 class="text-danger">{{ total_km }} Km</h4> <span class="text-secondary">総配達距離</span> </div> </div> <hr /> </div> <div class="p-2"> <a href="/sign-out/" class="btn btn-block btn-danger btn-md mt-2"> <i class="fas fa-sign-out-alt mr-1"></i> ログアウト </a> </div> {% include 'courier/bottom_tabs.html' %} {% endblock %}
「core/templates/courier/base.html」ファイルを編集します。
記述編集 【Desktop/crowdsource/core/templates/courier/base.html】
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1" /> <title>配達人 | クラウドソーシングアプリ</title> {% load bootstrap4 %} {% bootstrap_css %} {% bootstrap_javascript jquery='full' %} <script> let vh = window.innerHeight * 0.01; document.documentElement.style.setProperty('--vh', `${vh}px`); </script> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.9.0/css/all.css"> <style> #content { height: calc(var(--vh, 1vh) * 100); } </style> {% block head %}{% endblock %} </head> <body> <div id="content"> {% block content %}{% endblock %} </div> <script src="https://unpkg.com/bootoast@1.0.1/dist/bootoast.min.js"></script> <link rel="stylesheet" href="https://unpkg.com/bootoast@1.0.1/dist/bootoast.min.css"> <script> function toast(message, type) { bootoast.toast({ position: 'centerBottom', message, type, }); } {% if messages %} {% for message in messages %} toast('{{ message }}', '{{ message.tags }}'); {% endfor %} {% endif %} </script> </body> </html>
ブラウザを確認します。
支払い用メールアドレスを登録できるようになりました。
↓↓クリックして頂けると励みになります。
【48 | 配達人プロフィールページ】 << 【ホーム】 >> 【50 | 取引の更新】