ラベル Django の投稿を表示しています。 すべての投稿を表示
ラベル Django の投稿を表示しています。 すべての投稿を表示

2024年7月31日水曜日

Django: プロジェクト全体でテンプレートタグを使いたい

以下のような構成とします。

/myproject
/myproject/myproject
/myproject/myapp
/myproject/templates
/myproject/templatetags/__init__.py
/myproject/templatetags/custom_tags.py

settings.py

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')], #/myproject/templates
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
            'libraries': {
                'custom_tags': 'templatetags.custom_tags', #myproject/myproject以下にしたい場合はmyproject.templatetags.custom_tags とすればよい
            }
        },
    },
]

テンプレートの方で {% load custom_tags %} とすれば、自作テンプレートタグがプロジェクトで共用できるようになる。

2024年7月4日木曜日

Django: 今日の日付 datetime.date.today() が1日前になってしまう

settings.py で、タイムゾーン設定 USE_TZ が True になっている場合、DjangoはデフォルトでUTCを使用します。このため、表示時に適切なタイムゾーンに変換する必要があります。

Djangoのタイムゾーン設定を確認する:

# settings.py
USE_TZ = True  # タイムゾーン対応を有効にする
TIME_ZONE = 'Asia/Tokyo'  # サーバーのデフォルトタイムゾーンを設定する


ビューでのタイムゾーン変換:
Djangoのビューで、datetime.date.today()を使う場合は、タイムゾーンを考慮して今日の日付を取得します。

from django.utils import timezone
from datetime import date

def get_today_date():
    # 現在のタイムゾーンを取得
    current_tz = timezone.get_current_timezone()
    # 現在の日付と時刻を取得し、タイムゾーンを設定
    now = timezone.now().astimezone(current_tz)
    # 現在の日付を返す
    return now.date()

テンプレートでの表示:
Djangoのテンプレートで日付を表示する際には、タイムゾーンを考慮して表示します。

<!-- テンプレートファイル (例: my_template.html) -->
{% load tz %}

{% get_current_timezone as TIME_ZONE %}
{% localtime on %}
    <p>今日の日付: {{ today }}</p>
{% localtime off %}

2024年7月1日月曜日

Django: フォームのウィジエットテンプレート上書き

フォームのウィジェットテンプレートをDjangoアプリのテンプレートで上書きしたいとき
settings.py に以下の追加1、追加2部分が必要

INSTALLED_APPS = [
    'app.apps.AppConfig',  # マイアプリケーション
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.forms',  # 追加1
]

FORM_RENDERER = 'django.forms.renderers.TemplatesSetting' # 追加2
例: 以下のようにアプリ下のtemplatesにデフォルトのウィジェットテンプレートを保存する場合
/myproject/app_name/templates/django/forms/widgets/select_option.html

2024年6月25日火曜日

Django: django-debug-toolbarのパネルが表示されない

django-debug-toolbarを設定したところ右サイドバーは表示されたが、右サイドバー項目をクリックしたときメインの画面が表示されず困りました。

Django管理画面では表示されるのですが、公開ページでは表示されないという現象。


調べたところ、公開ページでページ内スクロールするJavaScriptコードがdjango-debug-toolbarとかぶっていたのが原因でした。

$('a[href^="#"]').click(function () { //・・・ }


一例ですが、以下のように公開ページのイベントの動作範囲を制限して表示できるようになりました。

$('.anker a[href^="#"]').click(function ()  { //・・・ }


参考:django-debug-toolbarが動かない - エンジニアもどきの技術メモ
https://chantsune.github.io/articles/122/


2024年4月11日木曜日

Django モデルフォームではModelでblank=Falseにするとrequired=Trueになる

Djangoのモデルからフォームを作りました。

forms.pyのほうでこんな感じで required=Trueにしました。

def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control'
            field.widget.attrs['required'] = True

こうすると、inputにrequiredが追加されます。

このrequiredをテンプレートで判定するため以下のように

{% for field in form %}
    {% if field.required %}必須{% endif %}
{% endfor %}

とすればOKかと思ったら、field.required がどうしてもTrueになりません。

以下を参照すると {% if field.field.required %} にしろと書いてある。

Tell if a Django Field is required from template

それでも field.field.required はFalseのままでだめでした。

さらに以下を参照すると

Django: Make certain fields in a ModelForm required=False

you ought to add blank=True to the corresponding model

The documentation says

If the model field has blank=True, then required is set to False on the form field. Otherwise, required=True.

とありました。モデルのフィールドを blank=False にしないとテンプレートでは required=True 判定しないようです。

結論としては以下となります。

  • 必須にしたいモデルのフィールドを blank=False にする
  • テンプレートでは
    {% for field in form %}
        {% if field.field.required %}必須{% endif %}
    {% endfor %}
    のようにする。


2023年9月22日金曜日

django-CMSでのチェック方法

django-CMS (https://www.django-cms.org)

$ python manage.py cms check

を実行すると保存されておらずorphanedになったものが発見される場合がある。
以下を実行して削除する。

$ python manage.py cms list plugins

$ python manage.py cms delete-orphaned-plugins


2023年7月25日火曜日

Django: SQLiteが動作しない場合

Amazon Linux2 または CentOS7上でDjangoをインストールして、「$ python manage.py migrate」すると以下のようなエラーが出ました。

django.db.utils.NotSupportedError: deterministic=True requires SQLite 3.8.3 or higher.

SQLiteのバージョンが古いということで、アップグレードしても同じようなエラーが出ます。

こちらのサイト https://stackoverflow.com/questions/66380006/django-deterministic-true-requires-sqlite-3-8-3-or-higher-upon-running-python によれば、以下のようにすればよいとのことでした。

まず、pysqlite3 と pysqlite3-binary をインストールします。(仮想環境です)

(venv)$ pip install pysqlite3

(venv)$ pip install pysqlite3-binary

そして、以下のbase.pyの一部を書き換えます。

venv/python3.9/site-packages/django/db/backends/sqlite3/base.py

# from sqlite3 import dbapi2 as Database # annotation

 ↓ 以下のようにします。

from pysqlite3 import dbapi2 as Database # import pysqlite3

再度 migrateし、ウェブサーバーを再起動したところ動作するようになりました。

<追記>上記でもまだ動かない(500エラー)場合(特にmod_wsgiを使う場合)
Apacheのエラーログを確認すると.../django/apps/registry.py の以下の部分でエラーになっている。
raise RuntimeError("populate() isn't reentrant")

このままだと原因が分からないので raise RuntimeError~をコメントアウトして代わりに
self.app_configs = {} を追加して実際のエラーを表示させます。

以下のエラーが確認できました。
.../django/db/backends/sqlite3/base.py", line 70, in check_sqlite_version

check_sqlite_version() が実行されて sqliteバージョンが3.9.0以降でないというエラーが出て止まっていました。

とりあえず対応として check_sqlite_version() をコメントアウトして実行しないようにするとDjangoが起動できました。

2023年6月28日水曜日

Django: テンプレートでクエリパラメータを取得する

Djangoのテンプレートでクエリパラメータを表示したい場合です。

クエリ例1:https://example.com/?preview=ok&update=true&id=123

上記の例ようなクエリパラメータの場合、一つずつ取得するときは以下のようにします。

{{ request.GET.preview }} ⇒ ok
{{ request.GET.update }} ⇒ true
{{ request.GET.id }} ⇒ 123

一覧を表示したいときは以下のようにします。

{% for key, value in request.GET.items %}
    <p>{{ key }}: {{ value }}</p>
{% endfor %}

表示結果
preview: ok
update: false
id: 123

注意点としては、以下のようにパラメータに重複がある場合です。

クエリ例2:https://example.com/?preview=ok&update=true&id=123&id=xyz

この場合、{{ request.GET }} は

<QueryDict: {'preview': ['ok'], 'update': ['true'], 'id': ['123', 'xyz']}></QueryDict:>

のようなQueryDictなのですが、

{{ request.GET.id }} は リスト ['123', 'xyz'] になるわけではなく、後ろの'xyz'しか取得できません。

{{ request.GET.id }} ⇒ xyz


2023年6月23日金曜日

Django: テンプレートでuuidを文字列に変換

 DjangoでuserにUUIDフィールドを作ったので以下のようにテンプレートで条件判定しようとしたところ、同じUUID文字列のはずなのにどうしても一致しませんでした。

{% if "あるUUID文字列" == user.uuid %}

 ・・・・

{% endif %}


以下のように、uuidを文字列に変換しないといけませんでした。

{% if "あるUUID文字列" == user.uuid|stringformat:"s" %}

 ・・・・

{% endif %}


2023年5月15日月曜日

Django: shellでのモデルデータの取得方法

 $python manage.py shell

>>> from products.models import Product
>>> Product.objects.get(id=9)
<Product: レーザマシン>


>>> Product.objects.get(id=9).image
<FieldFile: products/product/9/photo1.jpg>


>>> Product.objects.get(id=9).image.name
'products/product/9/photo1.jpg'


>>> Product.objects.get(id=9).image.path
'/var/www/html/products/myproject/media/products/product/9/photo1.jpg'


参考
モデル (Model) - データアクセスの基礎 - Django 入門 - Python 入門
https://python.keicode.com/django/model-data-access-basics.php

2022年10月12日水曜日

DjangoでBasic認証を設定する

 まず、以下のサイトを参照して wsgi-basic-auth をインストールしました。(環境はAmazon Linux2、Apache)

【Django】DjangoアプリにBasic認証をかける
https://qiita.com/mizu4my/items/e8fb51d65f872f302537

まず、ライブラリをインストールします。

$ pip install wsgi-basic-auth

次に、.envファイルに、Basic認証のIDとパスワードを追加します、とありました。

.envファイルとは環境変数を保存するものというのは分かりますが、いったいどこに保存するものなのか書いてありません。
何回か検索して調べると、どうやら django-environ というライブラリを使うらしいことが分かりました。以下のサイトです。

Djangoで環境変数?ならdjango-environを使おうか。
https://qiita.com/moonwalkerpoday/items/9da47dda80d3ddac9a62

最終的には以下のようにしてできました。

wsgi.py(例)

import os
from wsgi_basic_auth import BasicAuth #追加
import environ #追加
env = environ.Env() #追加
env.read_env('/var/www/html/myproject/myproject/.env') #追加.envへのパスの例です。
application = get_wsgi_application()
application = BasicAuth(application) #追加

そして、Apacheの設定ファイル(httpd.conf等)には、WSGIPassAuthorization On を追加することも必要でした。

httpd.conf

WSGIPassAuthorization On #追加


以下を参考にさせていただきました。

WEBアプリケーション開発メモ
http://drunk.chips.jp/web/2021/04/django_basic.html

2022年10月6日木曜日

Django: urls.pyのnameをViewで取得する

urls.py で以下のようにnameを設定した場合、

urlpatterns = [
    path('search/', views.Search.as_view(), name="search"),
]

views.pyで以下のようにnameを取得できる。

from django.urls import resolve

URL_NAME = resolve(request.path_info).url_name


2022年10月5日水曜日

Django: QuerySetに対するvalues()とvalues_list()の使い方と結果例

 メモです。

# filter(id__in=[1266, 1267]) は all() に置き換えても同様

# モデルオブジェクト取得の基本形
Product.objects.filter(id__in=[1266, 1267])
    #結果: <QuerySet [<Product: PRD-0001A>, <Product: PRD-0002/0002M>]>


# values() の使い方例
Product.objects.filter(id__in=[1266, 1267]).values('id', 'name')
    #結果: <QuerySet [{'id': 1266, 'name': 'PRD-0001A'}, {'id': 1267, 'name': 'PRD-0002/0002M'}]>
    #ValuesQuerySetと呼ばれるデータ型

# get()で一つのレコードだけ扱う場合
Product.objects.filter(id__in=[1266, 1267]).get(id=1266)
    #結果: <Product: PRD-0001A>

Product.objects.filter(id__in=[1266, 1267]).values('id', 'name').get(id=1266)
    #結果: {'id': 1266, 'name': 'PRD-0001A'}


# values_list() の使い方 *タプルを返す
Product.objects.filter(id__in=[1266, 1267]).values_list('id', 'name')
    #結果: <QuerySet [(1266, 'PRD-0001A'), (1267, 'PRD-0002/0002M')]>

Product.objects.filter(id__in=[1266, 1267]).values_list('id', 'name').get(id=1266)
    #結果: (1266, 'PRD-0001A')

list(Product.objects.filter(id__in=[1266, 1267]).values_list('id', 'name'))
    #結果: [(1266, 'PRD-0001A'), (1267, 'PRD-0002/0002M')]


# get()で一つのレコードだけ扱う場合
Product.objects.filter(id__in=[1266, 1267]).values_list('name').get(id=1266)
    #結果: ('PRD-0001A',)

Product.objects.filter(id__in=[1266, 1267]).values_list('name', flat=True).get(id=1266)
    #結果: 'PRD-0001A'

# flat=True の例 *フィールドは一つだけ指定します。複数の場合エラーになります。
Product.objects.filter(id__in=[1266, 1267]).values_list('name') #flat=True なし
    #結果: <QuerySet [('PRD-0001A',), ('PRD-0002/0002M',)]>

Product.objects.filter(id__in=[1266, 1267]).values_list('name', flat=True) #flat=Ture あり
    #結果: <QuerySet ['PRD-0001A', 'PRD-0002/0002M']>

list(Product.objects.filter(id__in=[1266, 1267]).values_list('name')) #flat=True なし
    #結果: [('PRD-0001A',), ('PRD-0002/0002M',)]

list(Product.objects.filter(id__in=[1266, 1267]).values_list('name', flat=True)) #flat=Ture あり
    #結果: ['PRD-0001A', 'PRD-0002/0002M']


# 外部キーも参照できる
Product.objects.filter(id__in=[1266, 1267]).values('id', 'name', 'category1__name')
    #結果: <QuerySet [{'id': 1266, 'name': 'PRD-0001A', 'category1__name': 'Wheel and axle'}, {'id': 1267, 'name': 'PRD-0002/0002M', 'category1__name': 'Pulley'}]>

2022年8月2日火曜日

Django: Apache (WSGIモジュール) を再起動しなくてもアプリケーション変更を反映させる方法

DjangoをApacheのWSGIで本番運用している場合、アプリケーションプログラムを変更した場合、いちいちApacheを再起動していました。面倒なので簡単に変更が反映される方法がないか調べたところ、

「wsgi.py を微修正して保存する」と変更が反映されることが分かりました。

これで少し楽になりました。

参考
Is it possible to reload the view without restarting Django? - Stack Overflow

https://stackoverflow.com/questions/9183114/is-it-possible-to-reload-the-view-without-restarting-django

If you are running a production server (nginx, apache, etc) and you want code-reload, then you need to add something to your wsgi module to detect code changes.

Code reloading with apache: http://code.google.com/p/modwsgi/wiki/ReloadingSourceCode

Code reloading with uwsgi: http://projects.unbit.it/uwsgi/wiki/TipsAndTricks


If you are running Django as a WSGI application in daemon mode you just need to touch the wsgi.py for your site and it will reload the application next time there is a request. (So no need for any special options).

Django: Admin モデル一覧画面でリストのリンクをすべて無効にする

list_display_links = [] としても、モデルのIDだけが勝手にリンクにされてしまいますが、以下のように None にするとすべて無効化できました。

class TestAdmin(admin.ModelAdmin):

    list_display_links = None #リストのリンクをすべて無効化

2022年6月17日金曜日

Django: request.FILESの構造

通常のフィールドは、request.POST によりデータ受け渡しされるが、ファイルフィールドは、request.FILES で受け渡しされる。

フィールドが以下のような形だとすると、

<input type="file" name="file_1"> (選択したファイル名: filename_1.pdf)
<input type="file" name="file_2"> (選択したファイル名: filename_2.jpg)
<input type="file" name="file_3"> (選択したファイル名: filename_3.png)


request.FILES (dict型オブジェクト)は、

object = { 'file_1': file_1, 'file_2': file_3, 'file_1': file_3, }

という構造になっている。

ファイル名を取得するには、

ファイル名取得 object['file1'].name => filename_1.pdf

とする。

その他、以下が取得できる。

ファイルサイズ取得 object['file1'].size

ファイルMIMEタイプ取得 object['file1'].content_type

ファイル文字コード取得 object['file1'].charset


2022年6月16日木曜日

Django: shellでsettings.pyの設定値を取得する



$ python manage.py shell

>>> from django.conf import settings

>>> settings.BASE_DIR

>>> '/var/www/html/myproject/'


2022年6月15日水曜日

Django: urls.pyで単純にリダイレクトする方法

例:

/abc/任意文字列 ⇒ /xyz/任意文字列


from django.views.generic.base import RedirectView

urlpatterns = [

    url(r'^abc/(?P<path>.*)$', RedirectView.as_view(url='/xyz/%(path)s', query_string=True)),

]

2021年2月17日水曜日

Django 文字数制限した要約文の作成 Truncator()

from django.db import models

from django.utils.text import Truncator


class Board(models.Model):

    # ...

    def __str__(self):

        return self.name


class Topic(models.Model):

    # ...

    def __str__(self):

        return self.subject


class Post(models.Model):

    # ...

    def __str__(self):

        truncated_message = Truncator(self.message)

        return truncated_message.chars(30)

2020年10月10日土曜日

Django Admin FileFieldからアップロードしたZIPファイルを自動解凍する

Admin画面のFileFieldからアップロードした直後のファイルの処理がなかなか分からなかったが以下のようにmodels.pyにdef save(self, *args, **kwargs):を追加して解決しました。

例 models.py:

class DataFile(models.Model):
    data = models.FileField()
    def save(self, *args, **kwargs):
        super(DataFile, self).save(*args, **kwargs)
        # Do anything you'd like with the data in filename
        filename = str(self.data)
        filepath = os.path.join(settings.MEDIA_ROOT, filename)
        with zipfile.ZipFile(filepath) as existing_zip:
            existing_zip.extractall(os.path.dirname(filepath))


参考

Django admin file upload handling · Eviatar Bach
http://eviatarbach.com/2014/12/12/django-admin-upload-handling/