0
0

More than 3 years have passed since last update.

Django tutorial やってみた

Last updated at Posted at 2021-03-10

Writing your first Django app, part 1¶

まず django-admin が使えなくてハマった。pip install djangoすれば入るとか言うがはいらんのじゃ。
virtualenvしておくのが必須だった。

python3 -m venv venv
. venv/bin/activate
pip install django
django-admin version

準備できたので本家tutorialをやっていく。

$ python -m django --version
3.1.6

$ django-admin startproject mysite

$ ll
total 4.0K
drwxr-xr-x  4 user1 user1   32 2021-02-05 14:38:27 .
drwxrwxrwx 17 user1 user1 4.0K 2021-02-05 14:11:33 ..
drwxr-xr-x  3 user1 user1   37 2021-02-05 14:38:21 mysite
drwxr-xr-x  5 user1 user1  100 2021-02-05 14:34:16 venv

tutorialを真面目に呼んでいく。
ファイル構造を学ぶ。

manage.py
mysite/
    __init__.py
    settings.py
    urls.py
    asgi.py
    wsgi.py

気になるところは...プロジェクト形式。

今、mysiteというプロジェクトを作ったが、これがアプリひとつ、なのか?

mysite/settings.py: Settings/configuration for this Django project. Django settings will tell you all about how settings work.

settings.py ってこの mysite のsetting。 でも manage.py はrootに所属。
ちょっと思ってたのと違った。

asgi, wsgiはweb serverの話なのかしら。わからんので一旦パス

python manage.py runserver

死ぬほどエラーが出た。

django.core.exceptions.ImproperlyConfigured: SQLite 3.8.3 or later is required (found 3.7.17).

よしDBだな。

Writing your first Django app, part 2¶

DB

DB setupはここ
https://docs.djangoproject.com/en/3.1/ref/databases/#mysql-notes

CREATE DATABASE django CHARACTER SET utf8;
CREATE USER 'django'@'%' IDENTIFIED WITH mysql_native_password AS '***';
GRANT ALL PRIVILEGES ON *.* TO 'django'@'%' REQUIRE NONE WITH GRANT OPTION MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0;
CREATE DATABASE IF NOT EXISTS `django`;
GRANT ALL PRIVILEGES ON `django`.* TO 'django'@'%';GRANT ALL PRIVILEGES ON `django\_%`.* TO 'django'@'%';

  • settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'OPTIONS': {
            'read_default_file': '/kc/my.cnf',
        },
    }
}

my.cnf

[client]
database = db.example.com
user = django
password = django
default-character-set = utf8

pip install mysqlclient

全然my.cnf読んでくれないので直ガキした。


DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django',
        'USER': "django",
        'PASSWORD': "django",
        'HOST': "db.example.com",
        'PORT': 3306,
        'OPTIONS': {
            'sql_mode': 'STRICT_TRANS_TABLES',
            'charset': 'utf8mb4',
            'init_command': 'SET '
                            'character_set_connection=utf8mb4,'
                            'collation_connection=utf8_general_ci'
        }
    }
}

python manage.py migrate

mysql> show tables;
+----------------------------+
| Tables_in_django |
+----------------------------+
| auth_group |
| auth_group_permissions |
| auth_permission |
| auth_user |
| auth_user_groups |
| auth_user_user_permissions |
| django_admin_log |
| django_content_type |
| django_migrations |
| django_session |
+----------------------------+
10 rows in set (0.07 sec)

できた!!

runserver

python manage.py runserver

ブラウザで見れた!

The install worked successfully! Congratulations!

Now that your environment – a “project” – is set up, you’re set to start doing work.

ふむ。今のは "project" なのか。

mysiteの下のmysite何者。

python manage.py startapp polls

tutorila通りにファイル作る。 polls/urls.py がなかったので作った。

興味深いのは

Whenever Django encounters include(), it chops off whatever part of the URL matched up to that point and sends the remaining string to the included URLconf for further processing.

しかも path('') しか書いてないのに /polls で認識される。

こんなんdjango知ってないと無理だわ。直感的にできるってのは素晴らしい。

polls のmodelを書く

TypeError: init() missing 1 required positional argument: 'on_delete'
でコケたのでこうしたら通った


class Choise(models.Model):
    question = models.ForeignKey(
        Question,
        on_delete=models.CASCADE
    )
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

python manage.py makemigrations polls
するとこういうファイルができる。
polls/migrations/0001_initial.py

sqlかと思ったらpythonなんだな。


    operations = [
        migrations.CreateModel(
            name='Question',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('question_text', models.CharField(max_length=200)),
                ('pub_date', models.DateTimeField(verbose_name='date published')),
            ],
        ),

createModelってのが、初期作成ってことかなたぶん。

そしたらsqlの・・previewができる。。本番に直接migrateたたけない場合(PaaSとか)は、このSQLをDBに直接流せばいいんだろう!

$ python manage.py sqlmigrate polls 0001
]--
-- Create model Question
--
CREATE TABLE `polls_question` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `question_text` varchar(200) NOT NULL, `pub_date` datetime(6) NOT NULL);
--
-- Create model Choise
--
CREATE TABLE `polls_choise` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `choice_text` varchar(200) NOT NULL, `votes` integer NOT NULL, `question_id` integer NOT NULL);
ALTER TABLE `polls_choise` ADD CONSTRAINT `polls_choise_question_id_4bd8457d_fk_polls_question_id` FOREIGN KEY (`question_id`) REFERENCES `polls_question` (`id`);

これはまだDBに変更を加えない。

migrate だけを実行すると、tableができた!

$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
  Applying polls.0001_initial... OK

さっそく Choice を typo して Choise になってたので、直してみた。

models.pyを1文字直し

class Choice(models.Model):
    question = models.ForeignKey(
        Question,
        on_delete=models.CASCADE
    )
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

python manage.py makemigrations
python manage.py sqlmigrate polls 0002
python manage.py migrate

これだけで RENAME TABLE polls_choise TO polls_choice; が実行された!!! 便利だ!これは便利だ!!!

manage.py shell

debugに便利そう!!
SQLが通らないときはこれで試すと良さそう。最初のdjango.setup() までだるいけど仕方ないか

$ python manage.py shell
>>> import django
>>> django.setup()         <-----ここまで決り文句
>>>
>>> from polls.models import Question, Choice              <-----使うtable/model
>>> Question.objects.all()
<QuerySet []>

# insert
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())
>>> q.save
<bound method Model.save of <Question: Question object (None)>>
>>> q.save()

# insertされたPKがそのままのobjectで見れる!
>>> q.id
1
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2021, 3, 9, 4, 42, 28, 899072, tzinfo=<UTC>)

# updateしてみる
>>> q.question_text = "What's up"
>>> q.save()
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>

これだと pk=1 しかわからなくて不便なので !!! ここ知りたかった!!

modelに str を足す!!

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

    def __str__(self):
        return self.question_text

適用するには、shellを起動し直す必要あり。

$ python manage.py shell
Python 3.7.7 (default, Jul 16 2020, 18:28:35)
[Clang 10.0.1 (clang-1001.0.46.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> import django
>>> django.setup()
>>> from polls.models import *
>>> Question.objects.all()
<QuerySet [<Question: What's up>]>       <------- テキストが見えた!

queryには pk という文字が使える

table には id というカラムしかないが、id=1と同じ意味で、pkを使える。便利!

>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True

relationsのrecord作成は .save() がない

>>> q.choice_set.create(choice_text='Not much', votes=0)

これ実行した瞬間にcommitされます。違いはなんなのかわからないし
choice_set という言葉がどこから出てきたのか意味不明。relationしてる場合のルールなんだろう、きっと。

Writing your first Django app, part 2¶

admin を有効にする

tutorial通りで。model操作できるようにしたらレコードのhistory見えるのすごいな。

python manage.py createsuperuser

そしたら /login で中に入れる。

http://127.0.0.1:8000/login

どうやるのと → DBにchange logが入ってた。

SELECT * FROM `django_admin_log`

たぶんadmin操作でしか残らないlogだな。あまり用途がわからない

fieldsets はadmin panelの見た目をいじる。columnにhtmlのheaderをつけれる。

ここらへんは使わなそうだから読み飛ばしてよさそう・・。

Writing your first Django app, part 3¶

html viewを作っていく。api serverとして使うからあまり不要だけど、一応基礎知識としてやっておこう

urlpatterns = [
    url(r'^$', views.index, name='index'),
]

このregex動かない。なんだべか。と思ったら、いついれたんだっかの中身がぜんぜん違ってた。
path() じゃなくて url() を使うのね
ファイルまんま置き換えたら動いた。

jinjaは使いたくないけど一応やる。template directoryはdjagoがかってによんでくれる。
templateにわたす値は contextでtemplate.reander()に渡す。
そしたら勝手にlinkまでついた。
あー、jinjaでobjectの中使って作ってるのか。

なるほど

render shortcut

これとこれは同じ

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    template = loader.get_template('polls/index.html')
    context = {
        'latest_question_list': latest_question_list,
    }
    return HttpResponse(template.render(context, request))
def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'latest_question_list': latest_question_list}
    return render(request, 'polls/index.html', context)

こういう同じ書き方あるのが辛い。

404

404設定すると

DoesNotExist at /polls/13/

Page not found (404)

になって、

Question does not exist

を教えてくれる。

404 shortcut

この2つは同じ

def detail(request, question_id):
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    return render(request, 'polls/detail.html', {'question': question})


def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})


error messageは No Question matches the given query.

になった

viewはこんなもんでいいか

Writing your first Django app, part 4¶

jinjaの話なのでまるっとスキップ。

Writing your first Django app, part 5¶

polls/tests.py を置くと

python manage.py test polls

でテストができる。面白いのが Question(pub_date=time) だけで、仮想(tmp?)のDBレコードを作ってくれる。

viewのtestは

from django.core.urlresolvers import reverse

これがこけたのでskipすることにした

Writing your first Django app, part 6¶

css 追加方法

/polls/static/polls/styles.css を作って

{% load static %}

で読み込む。

ふむ。ちょっとdjangoわかった

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0