[Django3.0]メディアファイルの取り扱い

プロジェクトルートにmediaフォルダを作成します。

settings.pyでmediaルートを設定します。os.path.joinの第二引数はアプリケーションのフォルダ、第三引数はフォルダ名(media)です。

settings.py

MEDIA_ROOT = os.path.join(BASE_DIR, 'sales', 'media')
MEDIA_URL = '/media/'

STATIC_ROOT = os.path.join(BASE_DIR, 'sales', 'static')
STATIC_URL = '/static/'

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
]
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
	// 省略
]


urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

mediaルートに画像を置きます。

templateで呼び出してみましょう。

<img src="/media/img/qrcode.png" width=100 height=100>

うまく表示されています。

さて、次はどこでqrコードを生成するかです。
商品生成時にidで作りたいが、idはmysql側で付与されるから、
*save() とした後に以下のようにすれば良いのかな。
*.objects.order_by(“id”).last()

Pythonでqrコードを作りたい

QRコード画像生成ライブラリ「qrcode」をインストールします。

$ pip install qrcode
$ pip install pillow

### テキストを画像にする

import qrcode

qr = qrcode.QRCode()
qr.add_data('test text')
qr.make()
img = qr.make_image()
img.save('qrcode.png')

### URLを画像にする

import qrcode

qr = qrcode.QRCode()
qr.add_data('https://www.google.com/')
qr.make()
img = qr.make_image()
img.save('qrcode.png')

アラ? 凄い簡単に出来ますね。。。簡単過ぎてビックリした。

[Django3.0]親モデルからForeignKeyの子モデルを逆参照して各アイテムの関連レコード数をforループで表示したい

現在Djangoで、見積モデル(Estimates)と受注モデル(Orders)が、それぞれ顧客モデル(Clients)をForeignKeyで参照しています。
顧客一覧ページで、各顧客ごとの見積数および受注数を表示する為、顧客モデル(Clients)から見積モデル(Estimates)および受注モデル(Orders)を逆参照して、顧客ごとの見積件数、受注件数を表示したい
モデルの関係は以下の通りです。

models.py

class Clients(models.Model):
	name = models.CharField(max_length=255)
	name_kana = models.CharField(max_length=255, null=True, blank=True)
        // 省略

class Estimates(models.Model):
	client = models.ForeignKey(Clients, null=True, blank=True, on_delete=models.PROTECT)
	estimate_date = models.DateField()
        // 省略

class Orders(models.Model):
	client = models.ForeignKey(Clients, null=True, blank=True, on_delete=models.PROTECT)
	order_date = models.DateField()
        // 省略

顧客一覧ページのUIは以下の通りで、Clientsモデルからデータを取得しforループで表示させています。

views.py

data = Clients.objects.all()
page = Paginator(data, 3)
params = {
	'data' : page.get_page(num)
}
return render(request, 'sales/client.html', params)

template

{% for item in data %}
						<tr>
							<td>{{item.id}}</td>
							<td class="text-nowrap">{{item.name}}</td>
							<td class="text-nowrap">〒{{item.zipcode}} {{item.prefecture}}{{item.address|truncatechars:20}}</td>
							<td>5</td>
							<td>6</td>
							<td class="text-nowrap"><button class="btn btn-light" onclick="location.href='/client/detail/{{item.id}}'">詳細</button> <button class="btn btn-light" onclick="location.href='/client/edit/{{item.id}}'">編集</button> <a href="#modal"><button class="btn btn-light del" id="{{item.id}}" value="{{item.name}}">削除</button></a></td>
						</tr>
						{% endfor %}

やり方を色々調べたが、同じようなことをやってるトラブルシューティングがなく、大苦戦。。。。
だが、さらに調べていると、どうやら「1データを基準にするだけなら *.${modelName}_set という書き方で逆参照可能」らしい。
逆参照は、forループを回しているテンプレート側でやると上手くいかなかったので、views.py側で処理をする事にした。
具体的には、Clientsモデルから顧客データを取得し、forループで各顧客データごとの見積数、受注数を逆参照して、配列に追加し、テンプレート側では呼び出すだけにした。
client.estimates_set.count()、client.orders_set.count()で、レコード数を取得している。

		data = Clients.objects.all()
		i = 0
		for item in data:
			client = Clients.objects.get(id=item.id)
			data[i].estimate = client.estimates_set.count()
			data[i].order = client.orders_set.count()
			i += 1
		page = Paginator(data, 3)
		params = {
			'data' : page.get_page(num)
		}
	return render(request, 'sales/client.html', params)
{% for item in data %}
						<tr>
							<td>{{item.id}}</td>
							<td class="text-nowrap">{{item.name}}</td>
							<td class="text-nowrap">〒{{item.zipcode}} {{item.prefecture}}{{item.address|truncatechars:20}}</td>
							<td>{{item.estimate}}</td>
							<td>{{item.order}}</td>
							<td class="text-nowrap"><button class="btn btn-light" onclick="location.href='/client/detail/{{item.id}}'">詳細</button> <button class="btn btn-light" onclick="location.href='/client/edit/{{item.id}}'">編集</button> <a href="#modal"><button class="btn btn-light del" id="{{item.id}}" value="{{item.name}}">削除</button></a></td>
						</tr>
						{% endfor %}

おおおおおおおおお、上手くいきました。
疲れたわ。。😇😇😇

[Django3.0]widget_tweaksでradioボタンの使い方が解らない時

widget_tweaks Django公式サイトを見たが、textformやtextarea、select formなどはわかるが、radioボタンの使い方がイマイチよくわからない。
django-widget-tweaks 1.4.8

やりたい事としては、銀行口座編集画面で、A.銀行口座(value=1)or郵便貯金口座(value=2)、B.普通預金(value=1)or当座預金(value=2)の登録情報を編集できるようにしたい。
なお、フロント側では、Javascriptで銀行口座の場合は銀行口座入力のみ、郵便貯金口座の場合は郵便貯金口座入力のフォームのみ入力できるようにformのdisableで制御している。

結局色々調べたが不明のため、views.py側で口座タイプ(bank_type)の値を取得し、ビュー側でif endifで制御する事にした。
{% if bank_type == 1 %}checked{% endif %}

views.py

def bank_edit(request):
	data = Bank.objects.get(id=1)
	params = {
		'form': BankForm(instance=data),
		'bank_type': data.bank_type,
		'account_type': data.account_type,
	}
	return render(request, 'sales/bank_edit.html', params)

template

<div class="row col-md-12">
	          			<div class="form-check col-md-2">
						  <input class="form-check-input" type="radio" name="bank_type" id="bank" value="1" {% if bank_type == 1 %}checked{% endif %} onClick="flg0(this.checked);">
						  <label class="form-check-label" for="bank">銀行口座</label>
						</div>
						<div class="form-check col-md-2">
						  <input class="form-check-input" type="radio" name="bank_type" id="jpbank" value="2"  {% if bank_type == 2 %}checked{% endif %} onClick="flg1(this.checked);">
						  <label class="form-check-label" for="jpbank">郵便貯金口座</label>
						</div>
					</div>

formの初期値でdisabled=”disabled”を付けるかどうかも同様にif endifで制御した。

{% if bank_type == 1 %}
								{% render_field form.bank_name class="form-control" id="bank_name" placeholder="銀行名を入力してください" %}
							{% else %}
								{% render_field form.bank_name class="form-control" id="bank_name" placeholder="銀行名を入力してください" disabled="disabled" %}
							{% endif %}

想定通りの動きになっているが、何か気持ち悪いので、widget_tweaksでradioの使い方があれば教えて欲しい。

### PDFで表示
上記を請求書に実装する

if bank.bank_type == 1:
			bank_name = bank.bank_name + ' ' + bank.bank_branch
			if bank.account_type == 1:
				account = '普通預金 ' + bank.account_number
			else:
				account = '当座預金 ' + bank.account_number
			holder = '法人口座名義 ' + bank.account_holder
		elif bank.bank_type == 2:
			bank_name = '郵貯銀行'
			account = '記号 ' + bank.jp_number + '  番号' + bank.jp_holder
			holder = '法人口座名義 ' + bank.account_holder
		else:
			bank_name = '別途ご連絡'
			account = ''
			holder = ''

		delivery_date = data.delivery_date.strftime("%Y年%m月%d日") + '納品予定' if data.delivery_date != None else ''

		# 振込先
		pdf_canvas.drawString(60, 155, 'お手数でございますが、お支払いは下記銀行口座へ振込くださいますようお願い申し上げます。')
		pdf_canvas.drawString(60, 135, '振込先: ' + bank_name)
		pdf_canvas.drawString(60, 125, account)
		pdf_canvas.drawString(60, 115, holder)
		pdf_canvas.drawString(60, 105, '恐れ入りますが、振込手数料は貴社にてご負担ください。')
		pdf_canvas.drawString(60, 85, delivery_date)

Let’s Gooooooo

ラジオボタンで分岐する銀行口座or郵貯口座登録のUIを実装しよう

販売管理システムで、請求書に振込先を表示させる為、口座登録の画面を作ります。

amazon seller accountの口座登録画面
-> 銀行口座のみの登録

どちらかというと、郵便貯金口座も表示できるようにしたい。
チェックボックスで銀行口座、郵便貯金口座を振り分けられるようなUIになるようWireframeを書きます。

### フロント実装
– チェックボックスで銀行口座を選択した場合、郵貯口座の入力ができないようにする
– 同様にチェックボックスで郵貯口座を選択した場合、銀行口座の入力ができないようにする
– 入力途中で口座区分を変更した場合は、document.getElementById(“*”).value = ”として、valueを空に変更する

<form>
          			<br>
					<div class="row col-md-12">
	          			<div class="form-check col-md-2">
						  <input class="form-check-input" type="radio" name="bank_type" id="bank" checked onClick="flg0(this.checked);">
						  <label class="form-check-label" for="bank">銀行口座</label>
						</div>
						<div class="form-check col-md-2">
						  <input class="form-check-input" type="radio" name="bank_type" id="jpbank" onClick="flg1(this.checked);">
						  <label class="form-check-label" for="jpbank">郵便貯金口座</label>
						</div>
					</div>

          			<br><br>

					<div class="form-group mb-0">
						<label for="title">金融機関</label>
						<input type="text" class="form-control col-md-4" name="bank_name" id="bank_name" placeholder="銀行名を入力してください">
					</div>
					<br>
					<div class="row col-md-12">
	          			<div class="form-check col-md-2">
						  <input class="form-check-input" type="radio" name="account_type" id="account_type" checked>
						  <label class="form-check-label" for="account_type">普通預金</label>
						</div>
						<div class="form-check col-md-2">
						  <input class="form-check-input" type="radio" name="account_type" id="account_type">
						  <label class="form-check-label" for="account_type">当座預金</label>
						</div>
					</div>
					<br>
					<div class="row">
						<div class="col-md-3">
							<label for="account_number">口座番号</label>
							<input type="text" id="account_number" class="form-control" placeholder="口座番号を入力してください">
						</div>
						<div class="col-md-3">
							<label for="account_holder">口座名義人(カナ)</label>
							<input type="text" id="account_holder" class="form-control" placeholder="口座名義人(カナ)を入力してください">
						</div>
					</div>
					<br><br>

					<div class="row">
						<div class="col-md-3">
							<label for="jp_name">記号(郵貯)</label>
							<input type="text" id="jp_name" class="form-control" name="jp_name" placeholder="見積日を入力してください" disabled="disabled">
						</div>
						<div class="col-md-3">
							<label for="jp_number">番号(郵貯)</label>
							<input type="text" class="form-control" id="jp_number" name="jp_number" placeholder="部署名を入力してください"  disabled="disabled">
						</div>
						<div class="col-md-3">
							<label for="jp_holder">口座名義カナ(郵貯)</label>
							<input type="text" class="form-control" id="jp_holder" name="jp_holder" placeholder="見積担当者の名前を入力してください"  disabled="disabled">
						</div>
					</div>
					<br><br><br>

					<div class="col text-center">
						<button class="btn" type="submit">更新</button>
					</div>
					
				</form>

<script>
		function flg0(ischecked){
			if(ischecked == true){
				document.getElementById("jp_name").value = '';
				document.getElementById("jp_number").value = '';
				document.getElementById("jp_holder").value = '';

				document.getElementById("jp_name").disabled = true;
				document.getElementById("jp_number").disabled = true;
				document.getElementById("jp_holder").disabled = true;

				document.getElementById("bank_name").disabled = false;
				document.getElementById("account_number").disabled = false;
				document.getElementById("account_holder").disabled = false;
			} else {
			}
		}
		function flg1(ischecked){
			if(ischecked == true){
				document.getElementById("jp_name").disabled = false;
				document.getElementById("jp_number").disabled = false;
				document.getElementById("jp_holder").disabled = false;

				document.getElementById("bank_name").value = '';
				document.getElementById("account_number").value = '';
				document.getElementById("account_holder").value = '';

				document.getElementById("bank_name").disabled = true;
				document.getElementById("account_number").disabled = true;
				document.getElementById("account_holder").disabled = true;
			} else {
			}
		}
	</script>

OK、 これをDjangoに実装します。