Django REST FRAMEWORK Tutorial 3 -- Snippet アプリのモデルとシリアライザを作る
Tags: django, restframework
Published: 2022-01-22 20:11:25


# 環境構築

## 前回の記事と前提

https://dev.to/kaede_io/docker-compose-de-rest-tutorial-woyaru-22k0

前回の記事を参考にして再び Django プロジェクトを作成し
その中に Snippet アプリを作る。

前回は rest4 というプロジェクトだったから、今回は rest5 にする

```php
sudo docker-compose run web django-admin startproject rest5 .
```

設定ファイルを使い回してプロジェクトを作成

```php
Collecting Django<4.0,>=3.0
  Downloading Django-3.2.11-py3-none-any.whl (7.9 MB)
Collecting psycopg2-binary>=2.8
  Downloading psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_24_aarch64.whl (3.4 MB)
Collecting djangorestframework<3.12.0,>=3.11.0
  Downloading djangorestframework-3.11.2-py3-none-any.whl (911 kB)
Collecting pygments
  Downloading Pygments-2.11.2-py3-none-any.whl (1.1 MB)
Collecting pytz
  Downloading pytz-2021.3-py2.py3-none-any.whl (503 kB)
Collecting sqlparse>=0.2.2
  Downloading sqlparse-0.4.2-py3-none-any.whl (42 kB)
Collecting asgiref<4,>=3.3.2
  Downloading asgiref-3.4.1-py3-none-any.whl (25 kB)
```

sqlparse, pytz, asgiref, Django, pygments, psycopg2-binary, djangorestframework,

これらのライブラリを Docker で DL 完了

```php
Installing collected packages: sqlparse, pytz, asgiref, Django, pygments, psycopg2-binary, djangorestframework
Successfully installed Django-3.2.11 asgiref-3.4.1 djangorestframework-3.11.2 psycopg2-binary-2.9.3 pygments-2.11.2 pytz-2021.3 sqlparse-0.4.2
```

install 完了

```php
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
WARNING: You are using pip version 21.2.4; however, version 21.3.1 is available.
```

permission の警告。無視

```php
You should consider upgrading via the '/usr/local/bin/python -m pip install --upgrade pip' command.
Removing intermediate container 6e667f388344
 ---> 0fe6d68bab68
Step 7/7 : COPY . /code/
 ---> 447d0e8feb7b
```

Dockerfile の COPY まで完了

```php
Successfully built 447d0e8feb7b
Successfully tagged rest5_web:latest
WARNING: Image for service web was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating rest5_db_1 ... done
Creating rest5_web_run ... done
kaede0902@rooter rest5 % 
```

無事に pygments 込みで Django Project が作成できた。

---

# 作るものの説明

## 何をやるのか

https://www.django-rest-framework.org/tutorial/1-serialization/

Django REST Framework のチュートリアル snippets アプリを作る

serialization の章をやる。

言語解析のアプリを作るようだ。

```
The tutorial is fairly in-depth, 
so you should probably get 
a cookie and a cup of your favorite brew 
before getting started. 

If you just want a quick overview, 
you should head over 
to the quickstart documentation instead.
```

ちょっと深いことをやるので、軽くスタートしてみたいだけなら、
quickstart の章を先にやった方がいいと書いてある。

---

## シリアリイザとは？

https://twitter.com/builderio/status/1513955108614193155?t=VZd2FsYcSW434SBqZyILpQ&s=19

JS で JSON.stringify(object)

するののこと。

---

## startapp コマンドで snippets というアプリを作る

作成してある rest4 というプロジェクトに

```php
docker-compose run web \
python manage.py startapp snippets

Creating rest4_web_run ... done
```

startapp で snippets というアプリを作る


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cgh4qf4zwj4ve5eqiprl.png)

できたフォルダにはこれらが入っている

---

## rest5/settings に登録

```py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'snippets',
]

```

---

## snippets/models.py を更新

https://www.django-rest-framework.org/tutorial/1-serialization/#creating-a-model-to-work-with

```py
from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles

LEXERS = [item for item in get_all_lexers() if item[1]]
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
STYLE_CHOICES = sorted([(item, item) for item in get_all_styles()])


class Snippet(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=100, blank=True, default='')
    code = models.TextField()
    linenos = models.BooleanField(default=False)
    language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
    style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)

    class Meta:
        ordering = ['created']
```

lex は字句解析。
item の配列 2 つ目があれば
get_all_lexers() の中で item がある限り for で回して
それを LEXERS に入れる
それをソートして LANGUAGE_CHOICES に入れる
それを language でカラムとして使う。
STYLE_CHOICES には get_all_styles で入れる。

Snippet テーブルに
created, title, code, lineos, language, style,
を作成する。bool の lineos はなんなのか不明。

---

## makemigrations と migrate する

models を適用するために makemigrations と migrate する

```php
docker-compose run web \
python manage.py \
makemigrations snippets

Creating rest5_web_run ... done
Migrations for 'snippets':
  snippets/migrations/0001_initial.py
    - Create model Snippet
```

Snippet のモデルができた

```php
docker-compose run web \
python manage.py \
migrate

Creating rest5_web_run ... done

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, snippets
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK
  Applying snippets.0001_initial... OK
```

次はこのモデルを使った serializer を作る

---

## snippets/serializer.py を書く

https://www.django-rest-framework.org/tutorial/1-serialization/#creating-a-serializer-class

snippets/serializers.py を作成

```py
from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES


class SnippetSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(required=False, allow_blank=True, max_length=100)
    code = serializers.CharField(style={'base_template': 'textarea.html'})
    linenos = serializers.BooleanField(required=False)
    language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')
    style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')

    def create(self, validated_data):
        """
        Create and return a new `Snippet` instance, given the validated data.
        """
        return Snippet.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        Update and return an existing `Snippet` instance, given the validated data.
        """
        instance.title = validated_data.get('title', instance.title)
        instance.code = validated_data.get('code', instance.code)
        instance.linenos = validated_data.get('linenos', instance.linenos)
        instance.language = validated_data.get('language', instance.language)
        instance.style = validated_data.get('style', instance.style)
        instance.save()
        return instance
```

先ほど モデルで定義した

created, title, code, lineos, language, style,

から created を除いて id を追加したカラム定義する

* id に RO
* title, lineos は required = False で空でもいいように
* code は textarea.html を使う
* language, style, は選択肢に choices を使い、デフォルトの文字列を定義

そして 

* def create で 引数に Snippet.objects.create()
* def update で　instance の中身を get して save して return

する関数を作る。


---

## 今後

次の記事で作ったモデルとシリアライザの動作をターミナルで確認する