Day13-3 sessionについてだぜい

# urls.py
urlpatterns = [
    ...
    ...
    path('change_item_amount', views.change_amount, name='change_amount'),
    ...
]

"""
何かボタンをhtmlにおきたい時はそれ専用のurlを作ったりもする。
でもリダイレクト先はviews.pyのどこかの関数に送ったりする場合もある。
今回で言えば、change_amount.htmlというのは存在しない。
っていうか作らない。なんせそんなの必要ないから。当然か。。。。
"""


# views.py

@login_required
@require_POST
def change_amount(request):
    product_id = request.POST["product_id"]
    bag_session = request.session['bag']
    if product_id in bag_session:
        if 'remove_amount' in request.POST:
            bag_session[product_id] -= 1
        if 'addon_amount' in request.POST:
            bag_session[product_id] += 1
        if bag_session[product_id] <= 0:
            del bag_session[product_id]
    return redirect('app:bag')


# xxx.html

{% block content %}

<form action={%  url 'app:change_amount' %} method='post'>{% csrf_token %}
    <input type="hidden" name="product_id" value="{product.id}">
    <input type="submit" name="remove_amount" value="一つ減らす">
    <input type="submit" name="addon_amount" value="一つ増やす">
</form>

{% endblock %}

・product_id = request.POST["product_id"].......product_idに送られて来たproduct_idを代入
・bag_session = request.session['bag'] ........'bag'というsessionを取り出して、bag_sessionに代入
・if product_id in bag_sessionの文から.......送られて来たproduct_idがbag_sessionの中に存在したら、
   ・'remove_amount'(htmlのinputのnameでつけたところ。formかinputはnameとvalueをペアにして情報を送る。)の情報が送られて来たら、bag_session[product_id]を一つ減らす。その下は逆のパターン
   ・で0個以下だったら、bag_session[product_id]を削除する。



っちゅう感じやな。よし。おけーーーーーい。次いいいいいいいいい!

Day13-2 Djangoおおおう!

# views.py

@login_required
def bag(request):
    bag = request.session.get('bag', {})
    bag_products = dict()
    total_price = 0
    for product_id, num in bag.items():
        product = Product.objects.get(id=product_id)
        bag_products[product] = num
        total_price += product.price * num

    context = {
        'bag_products': bag_products,
        'total_price': total_price,
    }
    return render(request, 'app/bag.html', context)


# bag.html
{% block content %}

{% for product, num in bag_products.items %}

        <a href="{% url 'app:detail' product.id %}">
          <img src="{{ product.image.url }}" class="product-img">
        </a>

        <h2>{{product.name}}</h2>

        <div>価格:{{ product.price | intcomma }}</div>
        <div>個数:{{ num | intcomma }}</div>

        <div>小計:{{ product.price | multiply:num | intcomma }}</div>

 {% endfor %}

{% endblock %}


# sessionのイメージ
session = {
    'bag' = {
        '1':99
    }
}

# views.py
cart = request.session.get('bag', {}) ........これで、bagセッションを辞書としてとってくる
・bag_products = dict() ........これで、bag_productsという辞書を作成

~~~~~~~~for文の中身~~~~~~~~~~

・ for product_id, num in bag.items() .........これで、bagという辞書に入っているkeyとvalueをproduct_idとnumとしてとってくる。item()は辞書のkeyとvalue両方をとってくる書き方。
  ・product = Product.objects.get(id=product_id) ......モデルのProductクラスからidをとって、productに入れる
  ・bag_products[product] = num .....これは辞書に要素を追加している。例えばbag["id"] = valueとすると、表示されるのはbag = {"id":value}というのが追加される。つまりここで表示されるのはbag_products = {"product(つまりproductのid)": numということになる}

【Python入門】dictionary(辞書)の使い方。基本と応用 | 侍エンジニアブログ (ありがとうございます.....)





#xxx.html
ここではviews.pyのcontextからhtmlに渡されたものをもとに、{%%}文の中に中に書く事ができる。

・{% for product, num in bag_products.items %} ..........bag_products.itemsで辞書のペアを取り出す。キーとバリューをproductとnumで置き換えるというか入れるというか。
・a href="{% url 'app:detail' product.id %}"........... product.idでリンクを書ける。
・img src="{{ product.image.url }}" .......... product.image.urlでimageのpathを書く事ができる。

そんな感じっすかね。以上!今日はもっとあげちゃうぜ

Day13 session 辞書

"""bag sessionのイメージ"""
session = {
    'bag':{
        '1': 5,
        '4': 3,
        '10': 6,
    }
}

# forms.py
class AddToBagForm(forms.Form)
    num = forms.IntegerField(
        label = '数量',
        min_value = 1,
        required=True,
    )

# views.py
"""セッションのところだけ取り出す。"""
def detail(request, product_id):
    product = get_objects_or_404(Product, pk=product_id)
    add_to_bag_form = AddToBagForm(request.POST or None)
    if add_to_bag_form.is_valid():
        num = add_to_bag_form.cleaned_data['num']

        if 'bag' in request.session:
            if str(product_id) in request.session['bag']:
                request.session['bag'][str(product_id)] += num
            else:
                request.session['bag'][str(product_id)] = num
        else:
            request.session['bag'] = {str(product_id): num}

        messages.success(request, f"{product.name}を{num}個買い物カゴにはいってるよ")
        return redirect('app:detail', product_id=product_id)

    context = {
        'product':product,
        'add_to_bag_form':add_to_bag_form,
    }
    return render(request, 'app/detail.html', context)

構造:
フォームが有効なら
 numに数量というデータを入れる。
  
 1.バッグがセッションに存在するなら
  2.product_idというキーがbagというセッションの中に既に存在するなら
    bagというセッションの中にあるproduct_idのキーの数量をnum分増加
  2.product_idというキーがbagというセッションの中に既に存在しないなら
    bagというセッションの中に新しくproduct_idのキーを追加する

 1.バッグがセッションに存在しないなら
  新しく'bag'というセッションのキーを追加する
 
 3.views.pyのdetailにproduct_idとともにリダイレクトする。


こんな感じかな!また新しくやることがあればこの次のブログでやったるぜ!

day11

・名前は狭い範囲のワードを使うこと
・動詞を必ず前に置くこと

def fetch_latest_news():
    ...

def calc_tax_including(price):
    ...

def aggregate_sum_price(items):
    ...


is_ , has_ ,で始まる変数名はBoolを返すようにする

def is_valid(sth)
    return not name.startswith("tttsssss")

意味のまとまりで関数を作ること。

import csv

def read_csv():
    """ some sentences """
    with open(...) as f :
        reader = csv.reader(f)
        for row in reader:
            name = row[0]
            price = int(row[0])
            yield name, price

def is_add_price(price):
    return price < 90000


def main():
    products = read.products()
    for name, price in products:
        if is_add_price(price)
            print(name)

・yield とは..........関数を一時的に実行停止させることが出来るもの。下に書いてあるからわからなくなったら読む
【Python入門】yield文の基本的な使い方を解説 | 侍エンジニアブログ

Day11 Django基礎 今日は写経するけどブログにはあまり載せないぜ!記録しておくだけだぜ。

・カスタムユーザーモデル
DjangoではAbstractUser, AbstractBaseUser の2種類のクラスを使う方法が用意されている。
様々なWebサービス要件に対応できるようにするためにはAbstaractBaseUserを使えるようにしておく。

以上!

Day10 Django 基礎

#html
{% load static %}


#base.html
<body>
    {% block content %}{% endblock %}
</body>

・{% load static %} でcssjavascript、画像などを読み込むことができるよん

・この {% block content %}{% endblock %}があるところに他のhtmlを入れていくことができるよん

# html
{% extends 'app/base.html' %}

{% block content %}
{% endblock %}

・{% extends 'app/base.html' %}......これで、base.htmlの延長で他のhtmlを書くことができる。あと{% block content %}{% endblock %}を使うよん

# settings.py

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, "static")
)

・これでプロジェクトディレクトリのstaticを読み込むことができる。


# Interactive console
>>>User.objects.all()
>>><QuerySet [<User: username>]>

・これはクエリセットだけ表示される。

>>>user1 = User.objects.get(username='username')

>>>user1.username
shijimi

>>>user1.date_joined
datetime.datetime(10101, 9, 8, 8, 19, 83, 846275, tzin...)

>>>user1.is_superuser
False

・getで取得するとインスタンス?を表示できる。インスタンスであってるのかわからないが、綺麗なものが取得できる。

# urls.py

path('users/<int:pk>', views.users_detail, name='users_detail')

・例えば、views.users_detailというviewの名前であったとしても、クライアントがurlを入力するときの名前もusers_detailとする必要はない。今回のように、users/のようにしても良い。

・Userモデルの属性はドキュメントにも載っている。
django.contrib.auth | Django documentation | Django

# 画像をアップロードするために。

# models.py
class Photo(models.Model):
    .......
    .......
    image = models.ImageField(upload_to='photos')
    .......


# settings.py
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

・models.ImageFieldを使うにはPillowをインストールする必要がある
・MEDIA_ROOT......画像の保存先を表す。この記述により、アップロードされた画像はBASE_DIR(ルートディレクトリ)直下のmediaというディレクトリに保存される。
・image = models.ImageField(upload_to='photos')でアップロード先をphotosにしているため、/media/photos/に画像が保存されることになる。


#admin.py

from .models import Photo

class PhotoAdmin(admin.ModelAdmin)
    list_display = ('id', 'title')
    list_display_links = ('id', 'title')

admin.site.register(Photo, PhotoAdmin)

・管理者画面で作成したモデルを使うにはadmin.pyに登録する必要がある。このadmin.ModelAdminが何を表すか、継承だろうけど、それがわからない。まあ使っとけということではある。

# console

>>> from app.models import Photo
>>> photo = Photo.objects.all()[0]
<ImageFieldFile: ...>

>>> photo.image.url
'MEDIA_URL/photos/........jpeg'

>>> photo.image.path
~User/..../...../...../...../...../MEDIA_URL/photos/.........jpeg

・.urlで指定するとその画像のアドレスを指定できるのだが、それらのアドレスの頭には、
MEDIA_URLに指定した文字列がつく。今回はMEDIA_URL = '/media/'としているので画像のアドレスは/media/photos/....jpeg となる。

・MEDIA_URL......ユーザー視点のURL
・STATIC_URL......開発者視点のURL

# プロジェクトのurls.py
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
...
...
]

# MEDIA_ROOTをアクセス可能にする
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 

これにより、一般ユーザーがmediaディレクトリにアクセスできるようになり、写真が公開することができる。

pip install django-cleanup


#settings.py

INSTALLED_APPS=[

...

'django_cleanup']


・これにより写真を消した時に、mediaのなかの写真も一緒に消すことができる。これを入れないと写真が消されてもディレクトリの中に残っていく。

#application urls.py
# このauth_viewsにはLoginViewとLogoutViewがあるからそれを今回は使う
from django.contrib.auth import views as auth_views

urlspattern = [
    .....
    path('login/', auth_views.LoginView.as_view(template_name='app/login.html'), name='login'),
    path('logout/', auth_views.LogoutView.as_view(), name='logout')
]

# settings.py

LOGIN_URL = 'app:login'
LOGIN_REDIRECT_URL = 'app:index'
LOGOUT_REDIRECT_URL = 'app:index'

・LOGIN_URL.........ユーザーがログインする時に使用するページ
・LOGIN_REDIRECT_URL...........ユーザーがログインした時に最初にリダイレクトさせるURL
・LOGOUT_REDIRECT_URL..........ログアウトしたユーザーをリダイレクトさせるURL


今日はここまで!