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

2018年1月6日土曜日

テーブル定義を変更せずに model を修正する

通常、model を定義し、その定義に基づき DB のテーブルを生成する。
しかし、何らかの理由により、テーブル定義を変えずに model を変更したい場合がある。

この場合、migrate コマンドに fake オプションを付けて実行すればよい。
> python ./manage.py makemigrations
> python ./manage.py migrate --fake

2018年1月3日水曜日

django-tables2 の SingleTableView を利用してみる

前回は TemplateView で django-tables2 を利用した。
同様の機能として django-tables2 の SingleTableMixin を利用することができる。

以下の例では、Actor model のデータを ActorTable table の構造で表示する。なお、table_pagination は RequestConfig の paginate パラメータへ与える辞書を与えることができる。
from django_tables2 import SingleTableView
class ActorSingleTableView(SingleTableView):
    model = Actor
    table_class = ActorTable
    table_pagination = {"per_page": 20}
    template_name = "actorlistview.html"
このテーブルを表示する際、デフォルトのテンプレート変数は table になる。
テンプレート引数を他の変数に変更したい場合は、context_table_name を設定する。
from django_tables2 import SingleTableView
class ActorSingleTableView(SingleTableView):
    model = Actor
    table_class = ActorTable
    context_table_name = "actors_table"     # テンプレート変数が actors_table に変更される。
    table_pagination = {"per_page": 20}
    template_name = "actorlistview.html"
上記例の場合、Actor に格納されたデータがそのまま ActorTable の形式で表示される。
クエリを変更したい場合、いくつかの方法がある。

まず、queryset を設定する方法である。以下の例では、表示順を last_name, first_name の順にしている。
from django_tables2 import SingleTableView
class ActorSingleTableView(SingleTableView):
    queryset = Actor.objects.order_by("last_name", "first_name")
    table_class = ActorTable
    table_pagination = {"per_page": 20}
    template_name = "actorlistview.html"
get_queryset() により設定することもできる。
from django_tables2 import SingleTableView
class ActorSingleTableView(SingleTableView):
    table_class = ActorTable
    table_pagination = {"per_page": 20}
    template_name = "actorlistview.html"

    def get_queryset(self):
        return Actor.objects.order_by("last_name", "first_name")
また、同様に get_table_data() により設定することができる。
from django_tables2 import SingleTableView
class ActorSingleTableView(SingleTableView):
    model = Actor
    table_class = ActorTable
    table_pagination = {"per_page": 20}
    template_name = "actorlistview.html"

    def get_table_data(self):
        return Actor.objects.order_by("last_name", "first_name")

2017年8月14日月曜日

Django で TemplateViewを利用する

Django のクラスベース汎用ビューは、よく記述するビューのパターンを簡易に利用できるようにしたものである。
その中の TemplateView は与えられたテンプレートをレンダリングする際に使用する。

TemplateView を利用する場合、レンダリングするためのデータ取得は get_context_data をオーバライドし、辞書型のコンテキストを返せばよい。
    def get_context_data(self, **kargs):
        # データ取得するための処理...
        return {取得したデータは辞書型で返す}
以下の例では、取得したデータを django-tables2 を利用して表示している。
[view.py]

class ActorTemplate(TemplateView):
    template_name = "sample.html"

    def get_context_data(self, **kargs):
        actors = Actor.objects.order_by("first_name", "last_name")
        actortable = ActorTable(actors)
        RequestConfig(self.request,
                      paginate={"per_page": 20}).configure(actortable)
        return {"title": "HOGE TITLE",
                "actortable": actortable}
[models.py]

class Actor(models.Model):
    actor_id = models.SmallIntegerField(primary_key=True)
    first_name = models.CharField(max_length=45)
    last_name = models.CharField(max_length=45)
    last_update = models.DateTimeField()
[tables.py]

class ActorTable(tables.Table):
    first_name = tables.Column(accessor="first_name",
                               verbose_name="First Name",
                               orderable=False,
                               attrs={"th": {"id": "first_name_id"}}
                               )
    last_name = tables.Column(accessor="last_name",
                              verbose_name="Last Name",
                              orderable=False,
                              attrs={"th": {"id": "last_name_id"}}
                              )
[urls.py]

urlpatterns = [
    url(r'^actors/',
        ActorTemplate.as_view()),
]
[sample.html]
{% load render_table from django_tables2 %}
{% load static %}

<html>
<head>
  <title>{{ title }}</title>
  <link rel="stylesheet" href="{% static 'django_tables2/themes/paleblue/css/screen.css' %}" />
  <style type="text/css">
    th#first_name_id {width: 150px}    
    th#last_name_id {width: 150px}    
  </style>
</head>

<body>
{% render_table actortable %}
</body>

</html>

2017年2月22日水曜日

django で既存DBから model を作成する

既存DBから model 情報を作成することができる。
なお、database name には、settings.py の DATABASES で設定したデータベース名を設定する。
python manage.py inspectdb --database=(database name) > (output file name)

2016年12月31日土曜日

django-table2 で選択された CheckBox を取得する

sakila をもとに生成した以下のモデルをもとに表を作成する。
class City(models.Model):
    city_id = models.SmallIntegerField(primary_key=True)
    city = models.CharField(max_length=50)
    country = models.ForeignKey('Country')
    last_update = models.DateTimeField()
    objects = CityManager()

    class Meta:
        managed = False
        db_table = 'city'


class Country(models.Model):
    country_id = models.SmallIntegerField(primary_key=True)
    country = models.CharField(max_length=50)
    last_update = models.DateTimeField()

    class Meta:
        managed = False
        db_table = 'country'
table として CityTable を以下のように作成する。各行の先頭にチェックボックスを配置する。
このとき、チェックボックスへの accessor は pk としておく。
import django_tables2 as tables


class CityTable(tables.Table):
    ck = tables.CheckBoxColumn(accessor="pk")
    city = tables.Column(accessor="city",
                         verbose_name="都市名",
                         orderable=True,
                         attrs={"th": {"id": "country"}}
                         )
    country = tables.Column(accessor="country.country",
                            verbose_name="国名",
                            orderable=True,
                            attrs={"th": {"id": "country"}}
                            )

    class Meta:
        attrs = {"class": "paleblue"}
チェックがついた行の情報を取得する場合は、対応する view 関数内で request.POST.getlist 関数にて取得する。
なお、取得されるデータは文字列リストの形式となるため、list と map を組み合わせて、数値リストに変換している。
def city_view(request):
    if request.method == "POST":
        pks = list(map(int, request.POST.getlist("ck")))
        ・・・

2016年11月20日日曜日

django で複数のデータベースを利用する

特定のアプリケーションの場合のみデータベースを変更したい場合、データベースルータを使用する。

まず、アクセス対象のデータベースを settings.py に記載する。
ここでは、通常(default)のデータベースと、特定のアプリケーションで利用するデータベース(nextdb)を定義した。
なお、以下の例では特定のアプリケーションを app としている。
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    },
    'nextdb': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'nextdb.sqlite3'),
    }
}
次に、データベースの切り替え条件を定義したルータの情報を記載する。なお、ここでは、project/router.py として作成する。
class Router(object):
    def db_for_read(self, model, **hints):
        if model._meta.app_label == "app":
            return "nextdb"
        return None

    def db_for_write(self, model, **hints):
        if model._meta.app_label == "app":
            return "nextdb"
        return None

    def allow_relation(self, obj1, obj2, **hints):
        if obj1._meta.app_label == "app" and obj2._meta.app_label == "app":
            return True
        elif "app" not in [obj1._meta.app_label, obj2._meta.app_label]:
            return True
        return None

    def allow_migrate(self, db, app_label, model=None, **hints):
        if app_label == "app":
            return db == "nextdb"
        else:
            return db == "default"
db_for_read および db_for_write は、与えられた model のアクセス先データベースを返す関数である。なお、戻り値が None の場合は、DATABASES の default で定義したデータベースが採用される。

allow_relation は、外部キーなどの設定の際、オブジェクト間でのリレーション可否を判定するための関数である。また、allow_migrate は、該当モデルとデータベースとの同期可否を定義する。

上記のすべての関数を定義する必要はなく、必要なものを定義することになる。

最後に settings.py にルータ情報を登録する。
DATABASE_ROUTERS = ['router.Router']
例として、appアプリケーションのモデル(app/model.py)と、その他のアプリケーションで共通させるモデル(core/model.py)を以下のように定義する。
[app/model.py]

from django.db import models

class Organization(models.Model):
    organization_cd = models.CharField(max_length=5)
    name = models.CharField(max_length=20)

    def __unicode__(self):
        return self.name

    class Meta:
        ordering = ["organization_cd"]


class User(models.Model):
    staff_cd = models.CharField(max_length=7)
    name = models.CharField(max_length=15)
    organization = models.ForeignKey(Organization)
[core/model.py]

from django.db import models

class User(models.Model):
    cd = models.CharField(max_length=7)
    name = models.CharField(max_length=15)

    def __str__(self):
        return "%s %s" % (self.cd, self.name)
双方とも管理者画面からアクセスすると、2つのデータベースが作成されていることが確認できる。

2016年9月26日月曜日

STATIC_URL が表示されない場合の対処法

Django 1.8 において、template に {{ STATIC_URL }} と記述しても該当の URL が展開されない場合、setting.py の TEMPLATES 変数に django.template.context_processors.static が追加されていることを確認する。
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [(テンプレートが格納されているディレクトリ)],
        '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',
                'django.template.context_processors.static',
            ],
        },
    },
]

2016年7月10日日曜日

ModelForm で ImproperlyConfigured 例外が送出された場合の対応

Django 1.8 より、ModelForm の仕様が変更になっている。
これまで、model で定義した全項目が form の項目になる場合、以下のように記述するだけでよかった。
class TestForm(ModelForm):
    class Meta:
        model = User
Django 1.8 以上は、form の項目を明示する必要があり、上記のような記述をした場合、ImproperlyConfigured 例外が送出される。
model の全項目を form の項目にする場合は、以下のように記述する。
class TestForm(ModelForm):
    class Meta:
        model = User
        fields = "__all__"

2016年2月20日土曜日

TypeError : Object is not JSON serializable. を回避する。

Django 1.6 よりシリアライザが pickle から json へ変更されており、それに伴い pickle で可能だったシリアライズができない事象が発生している。
これを回避するため、setting.py に SESSION_SERIALIZER を以下の通り追加し、シリアライザを pickle に戻す。
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'

2015年10月19日月曜日

Django で実行したSQLを表示する方法

例えば、User テーブルから条件抽出する SQL を表示する場合は、以下のとおりとする。
users = User.object.filter(name__startswith="John")
print users.query

select * from core_user where name like 'John%';

2015年9月27日日曜日

django で空のクエリセットオブジェクトを生成する

QuerySet を引数に渡す際、初期値など空データであることが予め自明な場合がある。
空リストと空QuerySetでは意味が異なるため、明確にQuerySetを渡す必要がある。
empty_list = []    # <type 'list'>
none_query_set = SomeModel.objects.none()    # <class 'django.db.models.query.QuerySet'>

2015年4月28日火曜日

django-table2 を利用して、ソート可能な表を作成する。

django-tables2 は、DB内に格納されたデータを、ページ分割/ソート可能な表形式で容易に出力してくれる。
まず、pip で django-tables2 をインストールする。
pip install django-tables2
model内に格納されたデータを出力するには、対応するtableクラスを作成する。
以下の User クラスのデータを出力する。
[model.py]

class Organization(models.Model):
    organization_id = models.CharField(max_length=5)
    name = models.CharField(max_length=20)

class User(models.Model):
    staff_id = models.CharField(max_length=7)
    name = models.CharField(max_length=15)
    organization = models.ForeignKey(Organization)
まず、出力項目を管理する django_tables2.Table より派生したクラスを作成する。
[tables.py]

import django_tables2 as tables

class UserTable(tables.Table):
    staff_id = tables.Column(accessor="staff_id",
                             verbose_name="STAFF ID",
                             order_by=("staff_id"))
    name = tables.Column(accessor="name",
                         verbose_name="USER NAME",
                         order_by=("name"))
    organization = tables.Column(accessor="organization.name",
                                 verbose_name="ORGANIZATION",
                                 order_by=("organization.organization_id"))

    class Meta:
        attrs = {"class": "paleblue"}
上に記載した Column の引数を説明する。

accessor は、該当項目のデータを管理している model 項目の名称となる。
テーブル内の項目名と一致している場合は省略可能である。
特に外部参照している場合、"参照先.項目名"とする必要がある。

verbose_name は、表上に出力される項目名称である。
また、order_by は、表上のソートボタンを押下された際、何に基づいてソートするかを定義するものである。
当該項目についても、外部参照している場合は、"参照先.項目名"とする必要がある。

表を表示する views.py の例を記載する。
表示対象となるデータを取得する。以下の例では、all によりすべてのデータを取得対象としているが、filter により選択することも可能である。
[views.py]

from django.shortcuts render
from models import User
from tables import UsetTable
from django_tables2.config import RequestConfig

def create_table(request):
    users = User.objects.all()
    table = UserTable(users)
    RequestConfig(request).configure(table)
    return render(request, "people.html", {"table": table})

適当な url により、create_table を呼び出せば、ソート可能な表が出力される。

2015年2月15日日曜日

モデルから作成したドロップダウンの表示順を変更する

django では容易にモデルから登録フォームを作成することができる。
その際、モデルの型が ForeignKey になっている項目は、ModelChoiceField が割り当てられるが、通常、ドロップダウンの表示順は参照先テーブルのID順となる。

以下のモデルを想定する。
class Organization(models.Model):
    organization_id = models.CharField(max_length = 5)  # 組織ID
    name = models.CharField(max_length = 20)            # 組織名

    def __unicode__(self):
        return self.name
    
class User(models.Model):
    staff_id = models.CharField(max_length = 7)         # スタッフID
    name = models.CharField(max_length = 15)            # スタッフ名
    organization = models.ForeignKey(Organization)      # 所属組織
本来、組織ID順にソートしたいところだが、実際は組織テーブルへの登録順に表示されてしまう。
組織ID順にソートするためには、組織テーブルに Meta クラスを追加し、デフォルトの取得順序を設定する。
class Organization(models.Model):
    organization_id = models.CharField(max_length = 5)  # 組織ID
    name = models.CharField(max_length = 20)            # 組織名

    def __unicode__(self):
        return self.name

    class Meta:
        ordering = ["organization_id"]

2014年5月25日日曜日

モデルからフォームを生成する

既に存在するモデルの構造からフォームを生成する際、以下のようにする。
なお、以下の例では、モデル Ticket に対するフォームを作成する。
from django.forms import ModelForm
class TicketRequestForm(ModelForm):
  class Meta:
    model = Ticket
モデルを利用する場合、フォームから入力する値とシステムが決定する値が混在する場合がある。

たとえば、例として挙げた Ticket が登録時分秒(entrytime)を持っており、システムが決定する値だとする。
このとき、フォームから値を取得する際、instance 変数へ初期値を与えたオブジェクトを与える。
entrytime = datetime.datetime.today()
ticket = Ticket(entrytime = entrytime)
ticketrequestform = TicketRequestForm(request.POST, instance = ticket)

2014年3月14日金曜日

Windows 8.1 における settings.py の編集上の注意点

Windows 8.1 において、localhost は ::1 といった IPV6 アドレスとなっている。
実際、ping localhost とコマンドプロンプトで入力した場合、ループバックアドレスとして、[::1] が表示されている。 

django から MySQL へ接続する場合、settings.py の DATABASES ディクショナリ内にある HOST 項目が空白もしくは localhost になっている場合、上記のループバックアドレスが利用され、_mysql_exceptions.OperationalError: (2003, "Can't connect to MySQL server on 'localhost' (10061)") といったエラーが発生する。

 これを回避するために、HOST 項目に 127.0.0.1 といった IPV4 形式のループバックアドレスを設定する。

2013年7月2日火曜日

CentOS 5.x に django 1.4 をインストールする

CentOS 5.x に python 2.6 および mod_wsgi がインストールされていることを前提とする。

まずは、django のソースをダウンロードし、インストールする。
なお、setup.py を実行する際は、必ず、python 2.6 を使用する。
wget http://www.djangoproject.com/download/1.4.5/tarball/
tar xzvf Django-1.4.5.tar.gz
cd Django-1.4.5
python26 setup.py install
次に、django プロジェクトを作成する。
mkdir /var/www/cgi-bin/django
cd /var/www/cgi-bin/django
django-admin.py startproject testproject
開発用サーバを起動し、別端末より、django が動作することを確認する。
cd /var/www/cgi-bin/django/testproject
python26 manage.py runserver
curl http://localhost:8000/

<!DOCTYPE html>
<html lang="en"><head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta name="robots" content="NONE,NOARCHIVE"><title>Welcome to Django</title>
・・・
<div id="explanation">
  <p>
    You're seeing this message because you have <code>DEBUG = True</code> in your
    Django settings file and you haven't configured any URLs. Get to work!
  </p>
</div>
</body></html>
apache 側の設定をpython26-mod_wsgi.conf へ記載する。
WSGIScriptAlias /django /var/www/cgi-bin/django/testproject/testproject/wsgi.py
WSGIPythonPath /var/www/cgi-bin/django/testproject

<IfModule !python_module>
    <IfModule !wsgi_module>
        LoadModule wsgi_module modules/python26-mod_wsgi.so
    </IfModule>
</IfModule>

<Directory /var/www/cgi-bin/django/testproject/testproject>
Order deny,allow
Allow from all
</Directory>
apache を再起動し、apache経由でdjangoの動作を確認する。
service httpd restart
curl http://localhost/django/

<!DOCTYPE html>
<html lang="en"><head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta name="robots" content="NONE,NOARCHIVE"><title>Welcome to Django</title>
・・・
<div id="explanation">
  <p>
    You're seeing this message because you have <code>DEBUG = True</code> in your
    Django settings file and you haven't configured any URLs. Get to work!
  </p>
</div>
</body></html>