質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

Webサイト

一つのドメイン上に存在するWebページの集合体をWebサイトと呼びます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

EC-CUBE

EC-CUBEは、主に日本国内で開発されているECコンテンツ管理システムです。ロックオン社のECKitを元にしてオープンソース化され、商品管理・受注管理・顧客管理・売上集計などECに特化した様々な機能を備えています。

Q&A

解決済

1回答

760閲覧

Djangoにおいて、ユーザーが選択した値に対応する別の値を用いて、計算を実行したりif文を組む方法

Yu-Yokochi

総合スコア7

Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

Webサイト

一つのドメイン上に存在するWebページの集合体をWebサイトと呼びます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

EC-CUBE

EC-CUBEは、主に日本国内で開発されているECコンテンツ管理システムです。ロックオン社のECKitを元にしてオープンソース化され、商品管理・受注管理・顧客管理・売上集計などECに特化した様々な機能を備えています。

0グッド

1クリップ

投稿2020/09/20 17:27

前提・実現したいこと

Djangoを用いた料理注文アプリを作成しております。

その機能の中で、

エリア別に送料・配達予想時間・注文に必要な最低金額(料理にかかった金額のみ)の3つのパラメータを予めadminからデーターベースへ情報を格納し、
後からユーザーがアプリでエリアを選択し、現在の注文合計金額が注文に必要な最低金額未満ならば、「あなたのエリアでは△△円の注文金額が必要なので、あと○○円分の注文が必要です」と表示し、それ以上ならば、決済へ進めるボタンが出現する

という仕様を実現しようとしています。

実際のサイトの構成は以下の通りです。


ホーム:アプリ名homepage/テンプレート名top.html--------------

お店トップページ

注文するボタンで住所選択ページへ


住所選択ページ:アプリ名ec/テンプレート名location_register.html-------

エリアA、Bから選択。
仮に
エリアA:送料400円、配達予想時間20分、必要注文最低額1000円
エリアB:送料800円、配達予想時間40分、必要注文最低額2000円
の2つのデータを格納している。

決定ボタンを押して、料理一覧ページへリダイレクト


料理一覧ページ:アプリ名ec/テンプレート名category_list.html---------

カテゴリー別に料理の画像、料理名、価格が並び、各料理に「カートへ追加する」ボタンがある。

カートへ料理を追加するとカート内ページへ。
料理の画像をクリックすると料理詳細ページへ。


料理詳細ページ:アプリ名ec/テンプレート名menu_detail.html------------

その料理の画像、料理名、価格、特記事項が表示され
料理一覧ページへ戻るボタンと、カートへ追加するボタンがある。


カート内ページ:アプリ名cart/テンプレート名cart.html-----------------

追加された料理の一覧(料理名、個数、価格)を表示し、
合計金額を表示。

その下に現在の合計金額が必要注文最低額未満ならば、「あなたのエリアでは△△円の注文金額が必要なので、あと○○円分の注文が必要です」と表示し、
それ以上ならば、決済へ進めるボタンが出現する
(例:ユーザーがエリアAを選択していて、合計金額が800円の時。必要注文最低額の1000円に対して200円足りないので「あなたのエリアでは1000円の注文金額が必要なので、あと200円分の注文が必要です」と表示されているところ、300円の商品を追加したので決済へ進めるボタンが出現した)

なお、カート内の料理の個数は+-ボタンで加減できる。


発生している問題・エラーメッセージ

・エリアに応じた必要注文最低額が画面に表示されない。
(あとのソースコード内の{{ location.mustorderprice }}および、Location.mustorderpriceが反映されない)
・上記の問題のせいで、追加で注文するべき金額がうまく計算できていない。
・上記の問題のせいで、テンプレート内で決済へ進めるボタンを出現させるかしないかを決定するif文がうまく実行できていない。

Internal Server Error: /cart/ Traceback (most recent call last): File "/Users/myname/qsqs/cart/views.py", line 41, in cart_detail rest = Location.mustorderprice - total TypeError: unsupported operand type(s) for -: 'DeferredAttribute' and 'int'

該当のソースコード

アプリ名ec/models.py

Python

1from django.db import models 2 3# Create your models here. 4class Category(models.Model): 5 name = models.CharField('カテゴリ名', max_length=12, unique=True) 6 7 def __str__(self): 8 return self.name 9 10class Menu(models.Model): 11 name = models.CharField('料理名', max_length=36) 12 category = models.ForeignKey(Category, on_delete=models.PROTECT, verbose_name='料理カテゴリ',related_name='menus') 13 price = models.IntegerField('価格') 14 text = models.TextField('特記事項やアレルギー等', blank=True, null=True) 15 image = models.ImageField(upload_to='images', blank=True, null=True) 16 17 def __str__(self): 18 return self.name 19 20class Location(models.Model): 21 name = models.CharField('住所', max_length=64) 22 shippingtax = models.IntegerField('送料') 23 shippingtime = models.IntegerField('配達所要時間(分)') 24 mustorderprice = models.IntegerField('必要注文最低額') 25 26 def __str__(self): 27 return self.name 28 29class ChoiceLocation(models.Model): 30 location = models.ForeignKey(Location, on_delete=models.PROTECT, verbose_name='住所') 31 32 def __str__(self): 33 return self.location 34 35

アプリ名cart/models.py

Python

1from django.db import models 2from ec.models import Menu 3# Create your models here. 4 5class Cart(models.Model): 6 cart_id = models.CharField(max_length=250, blank=True) 7 date_added = models.DateField(auto_now_add=True) 8 9 class Meta: 10 db_table = 'Cart' 11 ordering = ['date_added'] 12 13 def __str__(self): 14 return self.cart_id 15 16class CartItem(models.Model): 17 product = models.ForeignKey(Menu, on_delete=models.CASCADE) 18 cart = models.ForeignKey(Cart, on_delete=models.CASCADE) 19 quantity = models.IntegerField() 20 active = models.BooleanField(default=True) 21 22 class Meta: 23 db_table = 'CartItem' 24 25 def sub_total(self): 26 return self.product.price * self.quantity 27 28 def __str__(self): 29 return self.product

アプリ名ec/views.py

Python

1from django.shortcuts import render, redirect 2from django.http import HttpResponse 3from .models import Menu, Category 4from django.views import generic 5from .forms import LocationRegisterForm 6 7 8class MenuList(generic.ListView): 9 model = Category 10 11class MenuDetail(generic.DetailView): 12 model = Menu 13 14def location_register(request): 15 form = LocationRegisterForm(request.POST or None) 16 if request.method == 'POST' and form.is_valid(): 17 form.save() 18 return redirect('ec:menu_list') 19 20 context = { 21 'form': form 22 } 23 return render(request, 'ec/location_register.html', context)

アプリ名cart/views.py

Python

1from django.shortcuts import render, redirect, get_object_or_404 2from ec.models import Menu, Location 3from .models import Cart, CartItem 4from django.core.exceptions import ObjectDoesNotExist 5 6def _cart_id(request): 7 cart = request.session.session_key 8 if not cart: 9 cart = request.session.create() 10 return cart 11 12def add_cart(request, pk): 13 product = Menu.objects.get(id=pk) 14 try: 15 cart = Cart.objects.get(cart_id=_cart_id(request)) 16 except Cart.DoesNotExist: 17 cart = Cart.objects.create( 18 cart_id = _cart_id(request) 19 ) 20 cart.save() 21 try: 22 cart_item = CartItem.objects.get(product=product, cart=cart) 23 cart_item.quantity += 1 24 cart_item.save() 25 except CartItem.DoesNotExist: 26 cart_item = CartItem.objects.create( 27 product = product, 28 quantity = 1, 29 cart = cart 30 ) 31 cart_item.save() 32 return redirect('cart:cart_detail') 33 34def cart_detail(request, total=0, counter=0, cart_items = None): 35 try: 36 cart = Cart.objects.get(cart_id=_cart_id(request)) 37 cart_items = CartItem.objects.filter(cart=cart, active=True) 38 for cart_item in cart_items: 39 total += (cart_item.product.price * cart_item.quantity) 40 counter += cart_item.quantity 41 rest = Location.mustorderprice - total 42 except ObjectDoesNotExist: 43 pass 44 45 return render(request, 'cart/cart.html', dict(cart_items = cart_items, total = total, counter = counter)) 46 47def cart_remove(request, pk): 48 cart = Cart.objects.get(cart_id=_cart_id(request)) 49 product = get_object_or_404(Menu, id=pk) 50 cart_item = CartItem.objects.get(product=product, cart=cart) 51 if cart_item.quantity > 1: 52 cart_item.quantity -= 1 53 cart_item.save() 54 else: 55 cart_item.delete() 56 return redirect('cart:cart_detail')

アプリ名ec/location_register.html(住所選択ページ)

HTML

1{% extends 'ec/base.html' %} 2{% block content %} 3<p>住所を選択してください</p> 4<p>住所により必要注文最低金額が変化します。</p> 5<form action="" method="Post"> 6 {{ form.as_p }} 7 {% csrf_token %} 8 <button type="submit">決定</button> 9</form> 10{% endblock %}

アプリ名ec/category_list.html(料理一覧ページ)

HTML

1{% extends 'ec/base.html' %} 2{% block content %} 3<a class="nav-link" href="{% url 'cart:cart_detail' %}">カートを見る:({{item_count}})</a> 4 {% for category in object_list %} 5 <div class="each-category-content"> 6 <p>{{category.name}}</p> 7 <div class="article-flex"> 8 {% for menu in category.menus.all %} 9 <p>料理名:{{ menu.name }}</p> 10 <p>価格:{{ menu.price }}</p> 11 <a href="{% url 'ec:menu_detail' menu.pk %}"> 12 <img src='/{{ IMAGE_URL }}{{menu.image}}' height=100 width=200> 13 </a> 14 <a href="{% url 'cart:add_cart' menu.pk %}">Add to Cart</a> 15 {% endfor %} 16 </div> 17 </div> 18 {% endfor %} 19{% endblock %}

アプリ名cart/cart.html(カート内ページ)

HTML

1{% extends 'cart/base.html' %} 2{% block content %} 3{% if not cart_items %} 4 <h1>カートは空です。</h1> 5 <p><a href="{% url 'ec:menu_list' %}">こちら</a>からカートへ料理を追加してください。</p> 6{% else %} 7 <h1>カートの中の料理</h1> 8 <div> 9 <table> 10 <thead> 11 <tr> 12 <th>カート内料理</th> 13 </tr> 14 </thead> 15 <tbody> 16 {% for cart_item in cart_items %} 17 <tr> 18 <td> 19 {{cart_item.product.name}} 20 <br> 21 単価: ¥{{cart_item.product.price}} 22 <br> 23 個数: {{cart_item.quantity}} x ¥{{cart_item.product.price}} 24 </td> 25 <td> 26 ¥{{cart_item.sub_total}} 27 </td> 28 <td> 29 <a href="{% url 'cart:add_cart' cart_item.product.id %}">+</a> 30 &nbsp; 31 <p>{{cart_item.quantity}}</p> 32 &nbsp; 33 <a href="{% url 'cart:cart_remove' cart_item.product.id %}">-</a> 34 &nbsp; 35 </td> 36 </tr> 37 {% endfor %} 38 </tbody> 39 </table> 40 <a href="{% url 'ec:menu_list' %}">料理を追加する</a> 41 <h2>CheckOut</h2> 42 <p>合計金額は: <strong>¥{{ total }}</strong>です。</p> 43 {% if total >= Location.mustorderprice %} 44 <a href="#">決済へすすむ</a> 45 {% else %} 46 <p>あなたのエリアでは{{ location.mustorderprice }}円の注文金額が必要なので、あと{{ rest }}円分の注文が必要です </p> 47 {% endif %} 48 </div> 49 {% endif %} 50{% endblock %}

アプリ名ec/forms.py

Python

1from django import forms 2from .models import ChoiceLocation 3 4class LocationRegisterForm(forms.ModelForm): 5 6 class Meta: 7 model = ChoiceLocation 8 fields = '__all__'

試したこと

昨晩から一日格闘して、最終的にこのような状況になっております。どのように変更すれば、エリアに対応した最低注文金額が表示されるのか、全く正解への検討がつきませんでした。

この度は非常に経験が浅く、周りに頼れる人もいないため、質問させていただくこととしました。

お手数おかけしますが、知恵をお貸しいただけないでしょうか。

どうかよろしくお願いします。

補足情報(FW/ツールのバージョンなど)

MacOS
Django2.2
Python3

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sfdust

2020/09/21 11:22 編集

cart/views.pyでテンプレートに渡すパラメータが一部欠如しているのは何か意図があるのですか?
Yu-Yokochi

2020/09/21 13:48

パラメータの欠如や漏れについては一切意図はありません。ご指摘いただいて初めて自覚しました。 以下の記事を参考にしたのですが、その際に発生した可能性があります。 https://qiita.com/__init__/items/37375d60256014fc93f0
guest

回答1

0

ベストアンサー

可能な限り元コードを活かした形になりますが、下記のように
選択したエリアのインデックスをセッションに保存して、リダイレクト先に渡すやり方を考えてみました。

ec/views.py

def location_register(request): form = LocationRegisterForm(request.POST or None) if request.method == 'POST' and form.is_valid(): form.save() request.session['location'] = request.POST['location'][0] return redirect('ec:menu_list') ~後略~

cart/views.py

def cart_detail(request, total=0, counter=0, cart_items = None): try: if "location" in request.session: location_index = request.session.get("location") location = Location.objects.get(id=location_index) cart = Cart.objects.get(cart_id=_cart_id(request)) cart_items = CartItem.objects.filter(cart=cart, active=True) for cart_item in cart_items: total += (cart_item.product.price * cart_item.quantity) counter += cart_item.quantity rest = location.mustorderprice - total except ObjectDoesNotExist: pass return render(request, 'cart/cart.html', dict(cart_items = cart_items, total = total, counter = counter, rest=rest, location=location))

(当初元質問のコードがすべて示されているわけではないため、
検証のために示されていないコードは手元で自作・補完しました。(特にurls.py、あとMenuとProductの独自置換が混合されていてqiitaの元コードもほとんど参考にならず再構築に苦労しました)
そのうえでキモとなる部分を回答として記載しています。
したがって、実際に質問者様の環境で直される場合は、上記の部分だけではなく、
質問者様自身が握っている現状のsetting.pyやurls.py等他に直すべきところもあるかもしれません。
ただし私は超能力者ではないので、仮に上記だけを修正してうまく動かないとしても、すべてのコードが示されていない限り、そこまではサポートできないことはご了承ください。)

投稿2020/09/21 16:49

編集2020/09/22 05:22
sfdust

総合スコア1137

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Yu-Yokochi

2020/09/22 08:21

頂戴したコードによる変更で無事解決することができました。 自分の力不足により回答に苦労させてしまい申し訳ございません。 この度は多大なるご協力をしていただき誠にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問