目次

Djangoのメモ。

Django

導入

ラップトップでやったら簡単に同じ環境を作れた。すごい。

mkdir djangoApp
python3 -m venv djangoApp  # 仮想環境作成
cd djangoApp
source bin/activate  # 有効化
pip3 install django  # 仮想環境にインストールされる

プロジェクト作成

django-admin startproject djangoApp

サーバー起動

manage.pyの配置されたディレクトリで、

python3 manage.py runserver

localhost:8000にアクセスする。

タイムスタンプ

from django.utils import timezone
class Log(models.Model):
regist_date = models.DateTimeField(default=timezone.now)
from django.utils import timezone

をインポートして

default=timezone.now

これでレコード登録時の日本時間が保存されます。

日付

datetime → 書式化文字列

>>> import datetime
>>> now = datetime.datetime.now()
>>> now.strftime("%Y/%m/%d %H:%M:%S")
'2012/01/01 20:29:39'

フィールドの初期値

Class Post(models.Model):
  title = models.CharField(default='タイトル')

DBの初期化

python manage.py flush

初期設定

python manage.py makemigrations django_test
python manage.py migrate
python manage.py createsuperuser

モデルのテンプレ

class Cooking(models.Model):
  id = models.AutoField(primary_key=True)
  register_time = models.DateTimeField(default=timezone.now)  # 登録ボタンが押された時刻
  cooking_time = models.DateTimeField()  # 調理した時刻(フォーム入力)
  discard_time = models.DateTimeField(null=True, blank=True)  # 廃棄ボタンが押された時刻
  num = models.PositiveIntegerField()
  product = models.ForeignKey(
      'Product',
      on_delete=models.CASCADE)

データに沿ったselectを出す方法

例えば商品テーブルに登録されているものすべてを名前フィールドで出して、idで値を調理テーブルに入れるようなときだ。

class FF_Form(ModelForm):
  class Meta:
      model = Cooking
      fields = ["product"]
  num = IntegerField()
  cooking_time = DateTimeField()

などとする。 ポイントは、外部キーになっているフィールドを参照すること。

ここでは、商品テーブルではなく、調理テーブルのproductを参照する。

さらに、このままではobjectのコード?が出てきて何がなにやらわからないので、model.pyの

class Job(models.Model):
  id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
  job = models.CharField(max_length=64, blank=True)
  def __str__(self):
      return self.job

defstrを追加して、名前を返すようにすると、わかりやすくなる。

格納時の注意

  cooking = Cooking.objects.create(
      product_id=request.POST['product'],
             num=request.POST['num'],
    cooking_time=request.POST['cooking_time'])
  cooking.save()

product_idに注目。POSTされる値はproductだが、格納されるのはproductになる。

これは、フォームがモデルからデータを取ってくるときに外部キーのフィールドがproductであるため。 モデルでは、外部キーでも_idがつかない。

フォームの初期値の設定

views.pyにて、

initial_value = {
  'cooking_time': now
}

として、

form = ff_form(request.POST, initial=initial_value)

とする。

自作フィルタの追加方法

アプリケーション直下(models.pyとかあるところ)に、templatetagsディレクトリを作成する。 init.pyを作成し、test_tag.pyなどを作成し、中身を

from django import template
register = template.Library()
@register.filter(name="multiplie")
def multiplie(value, args):
  return value * args

のようにする。あとはテンプレート内で、

{% load app_tag %} // 作成したファイル名を記述
{{ review.score | mulitiplie:20}}

初期データ投入

アプリケーション直下にfixturesディレクトリを作成する。 中にinitial.json(なんでもいい)を作成し、以下のように記述。

[
  {
    "model": "django_test.Product",
    "pk": 1,
    "fields": {
      "title": "おはよう"
    }
  },
  {
    "model": "app名.モデル名",
    "pk": 2,
    "fields": {
      "title": "こんにちは"
    }
  }
]

そして、

python manage.py loaddata initial.json

time.time()でタイムゾーンが変わる問題

datetimeではちゃんと表示できているのに、time()をするとタイムゾーンが変わってしまう。 djangoのテンプレートでは勝手に変換してくれるが、time()では変換がなくなる。

仕方ないので9時間足して対応した。

editの例

get・postによって処理を分ける。editとupdateは同じメソッドに書く。

def discard_post(request):
  if request.method == 'POST':
      cooking = get_object_or_404(Cooking, id=request.POST['id'])
      form = DiscardForm(request.POST)
      if form.is_valid():
          cooking.discard_num = form.cleaned_data['discard_num']
          cooking.save()
          return redirect(to='/ff_form')
  else:
      cooking = get_object_or_404(Cooking, id=request.GET['id'])
      form = DiscardForm({
          'discard_num': cooking.discard_num,
          'id': cooking.id
      })
  d = {
      'form': form,
  }

一部だけhiddenにする例

class DiscardForm(ModelForm):
  def __init__(self, *args, **kwargs):
      super().__init__(*args, **kwargs)
      self.fields['id'].widget = forms.HiddenInput()
  class Meta:
      model = Cooking
      fields = ['id']
  discard_num = IntegerField(label='廃棄個数')
  id = CharField()

クエリ条件生成の例

cookings = Cooking.objects.exclude(cooking_time__gt=today).filter(discard_num__exact=None)

ダミーカラムを非表示にする

dataTable.addRows([
	 /* 全カラム表示用。*/
	 {% for product in products %}
	 [ '{{product.name}}',
	   '',
	   'opacity: 0',
	   new Date({{now | conv_dummytime | conv_graph}}),
	   new Date({{now | conv_dummytime | conv_graph}}),
	 ],
	 {% endfor %}

このようにすれば、非表示になる。線は見えないものの一応あって、時間が重なると2列になる。 ダミーを現在時にすると重なり、離れすぎるとグラフがおかしくなるので、12時間後にした。

現在時ライン

コピペで動作せず苦労したが、結局divのidをtimelineとして、packages名で揃えたことでできるようになった。よくわからない。

最初はコンテナ名がexample5.1,package名がtimelineだった。 これをどちらもtimelineにした。

コンテナとpackage名なので、たぶん指し示す位置が異なるのだろう。