<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Takenoko Tech LLC.</title>
    <description>The latest articles on DEV Community by Takenoko Tech LLC. (@takenoko-tech).</description>
    <link>https://dev.to/takenoko-tech</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F4783%2Fa70bf0bb-45a8-422c-9afa-7b5274989618.png</url>
      <title>DEV Community: Takenoko Tech LLC.</title>
      <link>https://dev.to/takenoko-tech</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/takenoko-tech"/>
    <language>en</language>
    <item>
      <title>Next.js（TypeScript）のプロジェクト作成 ESLint+Prettier（＠2022-06-29）</title>
      <dc:creator>niwoshi</dc:creator>
      <pubDate>Thu, 14 Jul 2022 01:24:46 +0000</pubDate>
      <link>https://dev.to/takenoko-tech/nextjstypescriptnopuroziekutozuo-cheng-eslintprettier2022-06-29-5e72</link>
      <guid>https://dev.to/takenoko-tech/nextjstypescriptnopuroziekutozuo-cheng-eslintprettier2022-06-29-5e72</guid>
      <description>&lt;p&gt;ちょっと色々変わってないか確認がてらプロジェクトの作成をまとめなおします。&lt;br&gt;&lt;br&gt;
この辺は気がついたら推奨設定が変わってたりするのでちょいちょい見直したいところです。&lt;/p&gt;
&lt;h2&gt;
  
  
  プロジェクトの作成
&lt;/h2&gt;

&lt;p&gt;nodeがインストールされた環境を想定しています。まずはプロジェクトを作成しておきます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-next-app [プロジェクト名] --typescript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;必要最低限は上記のコマンドで準備されます。&lt;br&gt;
個人的に頻繁に触りそうなファイル群は&lt;code&gt;src&lt;/code&gt;というディレクトリにいれときたいので、&lt;code&gt;pages&lt;/code&gt;、&lt;code&gt;styles&lt;/code&gt;は&lt;code&gt;src&lt;/code&gt;というディレクトリを作成して入れておきます。&lt;br&gt;
Next.jsではデフォルトで&lt;code&gt;src&lt;/code&gt;というディレクトリが存在すればそちらを&lt;code&gt;pages&lt;/code&gt;が含まれているディレクトリとして認識してくれるので、これで動作します。&lt;br&gt;
とはいえ、絶対パスに"@"を利用したい（いちいちパスを記載するのが面倒）ので、&lt;code&gt;tsconfig.json&lt;/code&gt;に&lt;code&gt;baseUrl&lt;/code&gt;の設定を追加します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;"incremental"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"baseUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"."&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"paths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@/*"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"src/*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;これで相対パスを省略して記載できます。&lt;br&gt;&lt;br&gt;
一応動作を確認しておきたいので、&lt;code&gt;src/pages/index.tsx&lt;/code&gt;の4行目を下記のように変更してみます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/styles/Home.module.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;動作チェックです。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;問題無く表示されればOKです。&lt;/p&gt;

&lt;h3&gt;
  
  
  Voltaでnodeとyarnのバージョン固定
&lt;/h3&gt;

&lt;p&gt;自分はnodeのバージョン管理にVoltaを利用しています。&lt;br&gt;&lt;br&gt;
ある程度よしなにしてくれるのでほっといてもいい気はしますが、トラブルに巻き込まれた際に面倒になるのでnodeとyarnのバージョンをプロジェクトで固定しておきます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;volta pin node@16
volta pin yarn@1.22
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ESLint+Prettier
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ESLint
&lt;/h3&gt;

&lt;p&gt;create-next-appで作成していれば必要最低限の準備は既に出来ています。&lt;br&gt;
試しに、&lt;code&gt;.eslintrc.json&lt;/code&gt;を次のように書き換えて、セミコロンの警告を出します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "extends": "next/core-web-vitals",
  "rules": {
    "semi": ["error", "never", {"beforeStatementContinuationChars": "never"}],
    "semi-spacing": ["error", {"after": true, "before": false}],
    "semi-style": ["error", "first"],
    "no-extra-semi": "error",
    "no-unexpected-multiline": "error",
    "no-unreachable": "error"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;index.tsxなどのどこかにセミコロンを記入して&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn lint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;すると警告が表示されると思います。&lt;br&gt;
この時点で&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn lint --fix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;で修正まで入るようになります。&lt;/p&gt;

&lt;h3&gt;
  
  
  Prettier
&lt;/h3&gt;

&lt;p&gt;整形は例にならってPrettierで行います。よって、先ほど追加したセミコロンのルールは削除してしまって構いません。（ESLint -&amp;gt; Prettierの順番で実行しようと考えているのでどうせ上書きされる）&lt;br&gt;
PrettierとESLintのプラグインをインストールしておきます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add -D -E prettier
yarn add -D eslint-config-prettier
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;eslint-config-prettier&lt;/code&gt;の設定を適応させるために、&lt;code&gt;.eslintrc.json&lt;/code&gt;を書き換えておきます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "extends": ["next/core-web-vitals", "prettier"],
  "rules": {
    "semi": ["error", "never", {"beforeStatementContinuationChars": "never"}],
    "semi-spacing": ["error", {"after": true, "before": false}],
    "semi-style": ["error", "first"],
    "no-extra-semi": "error",
    "no-unexpected-multiline": "error",
    "no-unreachable": "error"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;package.json&lt;/code&gt;に書いてもいいんですが、既に&lt;code&gt;.eslintrc.json&lt;/code&gt;も生成されてますので、&lt;code&gt;.prettierrc.json&lt;/code&gt;を作成してそこにPrettierの設定を書いておきます。&lt;br&gt;
とりあえず自分の必要最低限のモノだけ書いています。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "trailingComma": "all",
  "tabWidth": 2,
  "semi": false,
  "singleQuote": true,
  "jsxSingleQuote": true
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prettierでプロジェクト全体を整形したい場合用に、&lt;code&gt;package.json&lt;/code&gt;の&lt;code&gt;scripts&lt;/code&gt;に&lt;code&gt;format&lt;/code&gt;というコマンドを追加しておきます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    "format": "prettier --write ./**/*.{js,jsx,ts,tsx,json,css} --ignore-path .gitignore"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;とりあえず上記までで&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn lint --fix
yarn format
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;で全体を解析＋フォーマットできるようになりました。&lt;br&gt;
ここから先はお好みで、ローカルで解析やテストを回すなら「husky+lint-staged」、コミット先でやるなら「Github ActionsでESLint+Prettier+テスト」という構成などが考えられると思います。&lt;/p&gt;

&lt;h2&gt;
  
  
  参考
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://qiita.com/mysticatea/items/9da94240f29ea516ae87"&gt;https://qiita.com/mysticatea/items/9da94240f29ea516ae87&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>eslint</category>
      <category>prettier</category>
    </item>
    <item>
      <title>btrfsのRAID構成をオンラインで変更する</title>
      <dc:creator>k11o</dc:creator>
      <pubDate>Wed, 06 Jul 2022 12:23:05 +0000</pubDate>
      <link>https://dev.to/takenoko-tech/btrfsnoraidgou-cheng-woonraindebian-geng-suru-4121</link>
      <guid>https://dev.to/takenoko-tech/btrfsnoraidgou-cheng-woonraindebian-geng-suru-4121</guid>
      <description>&lt;p&gt;今まで btrfs の RAID 5 で3台の HDD を束ねて使っていましたが、容量の不足もあったので3台の HDD を追加し RAID 6 運用に変更した際の忘備録。これがオンラインで出来るのはありがたいです。&lt;/p&gt;

&lt;p&gt;もとの状態&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;konota@storage:/data$ sudo btrfs filesystem show
Label: none  uuid: 71cdbe11-5102-448d-9aac-2dda635edf30
        Total devices 1 FS bytes used 8.57GiB
        devid    1 size 58.09GiB used 11.06GiB path /dev/mapper/ubuntu--vg-ubuntu--lv

Label: '/data'  uuid: 27c4fe5d-89ec-4edd-853b-82a13ce4febe
        Total devices 3 FS bytes used 3.01TiB
        devid    1 size 2.73TiB used 1.54TiB path /dev/sde
        devid    2 size 2.73TiB used 1.54TiB path /dev/sdd
        devid    3 size 2.73TiB used 1.54TiB path /dev/sdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;新しいHDDを追加して、balance実行時にオプションでdataとmetadataを変換します。追加前に mkfs.btrfs でフォーマットを忘れずに。本来なら直前にbtrfs filesystem show コマンド等で追加状況を確認したほうがいいです。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;konota@storage:~$ sudo btrfs device add /dev/sda /data
konota@storage:~$ sudo btrfs device add /dev/sdb /data
konota@storage:~$ sudo btrfs device add /dev/sdc /data
konota@storage:~$ sudo btrfs balance start -dconvert=raid6 -mconvert=raid6 /data
WARNING:

        RAID5/6 support has known problems and is strongly discouraged
        to be used besides testing or evaluation. It is recommended that
        you use one of the other RAID profiles.
        The operation will continue in 10 seconds.
        Use Ctrl-C to stop.
10 9 8 7 6 5 4 3 2 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;balance処理中は追加されたdeviceのusedが増加するのがわかります。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;konota@storage:~$ sudo btrfs filesystem  show
[sudo] password for konota:
Label: none  uuid: 71cdbe11-5102-448d-9aac-2dda635edf30
        Total devices 1 FS bytes used 8.57GiB
        devid    1 size 58.09GiB used 11.06GiB path /dev/mapper/ubuntu--vg-ubuntu--lv

Label: '/data'  uuid: 27c4fe5d-89ec-4edd-853b-82a13ce4febe
        Total devices 6 FS bytes used 3.01TiB
        devid    1 size 2.73TiB used 1.54TiB path /dev/sde
        devid    2 size 2.73TiB used 1.54TiB path /dev/sdd
        devid    3 size 2.73TiB used 1.54TiB path /dev/sdf
        devid    4 size 2.73TiB used 17.27GiB path /dev/sda
        devid    5 size 2.73TiB used 17.27GiB path /dev/sdb
        devid    6 size 2.73TiB used 17.27GiB path /dev/sdc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;balance処理中、Celeron G6900ですがtopはこのような状態です。この量の変換で一晩ほどかかりそうです。(現在進行系)&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PgE3yg55--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7njwuyqlktsktgidlcal.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PgE3yg55--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7njwuyqlktsktgidlcal.png" alt="balance中の様子" width="880" height="488"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;その後無事に変換が完了していることを確認しました。今回の場合、3台追加してRAID6にしたので各デバイスごとのusedは減少しました。&lt;/p&gt;

</description>
      <category>btrfs</category>
    </item>
    <item>
      <title>Djangoで既存モデルにForeignKeyなフィールドを追加する場合のMigrationについて</title>
      <dc:creator>k11o</dc:creator>
      <pubDate>Tue, 03 May 2022 14:34:12 +0000</pubDate>
      <link>https://dev.to/takenoko-tech/djangodeji-cun-moderuniforeignkeynahuirudowozhui-jia-suruchang-he-nomigrationnituite-1b55</link>
      <guid>https://dev.to/takenoko-tech/djangodeji-cun-moderuniforeignkeynahuirudowozhui-jia-suruchang-he-nomigrationnituite-1b55</guid>
      <description>&lt;p&gt;Djangoでは割と使い物になるMigration生成機能がありますが、既存モデルにForeignKeyでフィールドを追加する場合、当該フィールドがNOT NULL制約付きなため、Migration時に適当な初期値を渡す必要があります。&lt;/p&gt;

&lt;p&gt;具体的にコードで書くと、下記のような変更を行った場面です。(Postモデルのデータをいくつか作成したものの、TagでPostをまとめたくなった、という想定)&lt;/p&gt;

&lt;p&gt;既存コード&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Post(models.Model):
    text = models.TextField("本文",blank=False)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;追加後&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Tag(models.Model):
    name = models.CharField("名前",max_length=10,blank=True)

class Post(models.Model):
    text = models.TextField("本文",blank=False)
    tag = models.ForeignKey(Tag,on_delete=CASCADE)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;この場合既存データが存在するため、普通にMigrationでフィールドを追加、というのが難しくなります。&lt;br&gt;
そこでTagモデルのデータを作りつつ、既存のPostについては全て一旦生成したTagに紐づけるようなMigrationを、一回のMigration生成で行う方法を考えてみます。&lt;/p&gt;

&lt;p&gt;先程のコードの変更を行ったあとに &lt;code&gt;manage.py makemigrations&lt;/code&gt; を実行すると、既存データにNOT NULLな列が追加されるからデフォルト値が必要だよ!どうする?みたいなプロンプトが出るので、one-off default値を渡すことにして適当な値を突っ込みます(本来ならここでPythonコードが書けるのでTagモデルのデータを作りつつその値を渡す…ということが出来るのかも知れませんが、私が試した限りでは実現出来ませんでした。)&lt;/p&gt;

&lt;p&gt;そうすると下記のようなMigrationコードが生成されます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Migration(migrations.Migration):

    dependencies = [
        ('application', '0011_auto'),
    ]

    operations = [
        migrations.AddField(
            model_name='post',
            name='tag',
            field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='application.tag'),
            preserve_default=False,
        ),
    ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;当然、このまま実行するとあまり良くない結果になるので、migrateコマンドは実行せずmigrationファイルを修正します。&lt;/p&gt;

&lt;p&gt;処理の方針としては&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;NOT NULL制約無しで列を追加&lt;/li&gt;
&lt;li&gt;Tagモデルのデータをとりあえず作り、既存データを紐づけるように更新&lt;/li&gt;
&lt;li&gt;NOT NULL制約を付ける&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;という順序でやってます。&lt;/p&gt;

&lt;p&gt;migrationsには&lt;a href="https://docs.djangoproject.com/en/4.0/ref/migration-operations/#runpython"&gt;RunPython&lt;/a&gt;というMigration中に関数を実行する機能があるのでこれを利用します。因みにこの機能はRollback用関数も定義出来るので中々強力です。&lt;/p&gt;

&lt;p&gt;またRunPythonで呼び出す関数については、ドキュメントにある通り引数の apps及びschema_editorを利用してDBへの接続やModelの取得を行います。&lt;/p&gt;

&lt;p&gt;これらを踏まえ、前述した処理の通りに、既存のPostを全てTag「日常日記」に紐づけるようなMigrationに修正したものが下記です。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def create_tag(apps, schema_editor):
    db_alias = schema_editor.connection.alias
    Tag = apps.get_model("application", "Tag")
    tag = Tag.objects.using(db_alias).create(name="日常日記")
    Post = apps.get_model("application", "Post")
    Post.objects.using(db_alias).update(tag=tag)

class Migration(migrations.Migration):

    dependencies = [
        ('application', '0011_auto'),
    ]

    operations = [
        migrations.AddField(
            model_name='post',
            name='tag',
            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='application.tag'),
            preserve_default=False,
        ),
        migrations.RunPython(create_tag),
        migrations.AlterField(
            model_name='post',
            name='tag',
            field=models.ForeignKey(null=False, on_delete=django.db.models.deletion.CASCADE, to='application.tag'),
            preserve_default=False,
        ),
    ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;これで一つのmigrationで既存データを破壊せずに外部キーを追加する事が出来ました。&lt;br&gt;
ただもっとスマートな方法がある気がしていますので、ご存知の方は是非コメント欄で教えていただけるとありがたいです。&lt;/p&gt;

</description>
      <category>django</category>
    </item>
    <item>
      <title>Django REST frameworkのユニットテスト</title>
      <dc:creator>niwoshi</dc:creator>
      <pubDate>Thu, 20 Jan 2022 10:37:02 +0000</pubDate>
      <link>https://dev.to/takenoko-tech/django-rest-frameworknoyunitutotesuto-2342</link>
      <guid>https://dev.to/takenoko-tech/django-rest-frameworknoyunitutotesuto-2342</guid>
      <description>&lt;p&gt;Django REST framework（以下DRF）のテストについて軽くまとめておきます。&lt;br&gt;
一応前回の続きという事で進めて行きます。&lt;br&gt;
正直Djangoのテストは慣れていないのでなにからやれば良いか試行錯誤している段階ではあります。&lt;/p&gt;
&lt;h2&gt;
  
  
  テストフォルダの作成
&lt;/h2&gt;

&lt;p&gt;DRFの場合、テスト対象としてはアプリケーション毎にモデル、シリアライザ、ビューの3種類が考えられます。&lt;br&gt;
&lt;code&gt;startapp&lt;/code&gt;で自動生成される&lt;code&gt;apiv1/tests.py&lt;/code&gt;にテストを書いていってもいい気もしますが、上記3種のテストを1ファイルに書いてしまうと肥大化していくのは目に見えているので、上記を分離しておきます。&lt;br&gt;
&lt;code&gt;apiv1/tests&lt;/code&gt;フォルダを作成して&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;apiv1/tests/__init__.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;apiv1/tests/test_models.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;apiv1/tests/test_serializers.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;apiv1/tests/test_views.py&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;の用にテストファイルを作成します。（必要なものだけで大丈夫かと思います。）&lt;br&gt;
&lt;code&gt;__init__.py&lt;/code&gt;は空ファイルで大丈夫です。ついでに&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;apiv1/tests.py&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;は削除しておきます。&lt;/p&gt;
&lt;h2&gt;
  
  
  ビューのテスト
&lt;/h2&gt;

&lt;p&gt;今回はDRFのviewsetsをそのまま継承して利用しているだけなので、おそらくテストは不要だと思います。&lt;br&gt;
とはいえ一応チュートリアル的な観点で試したいのでTodoの生成だけテストを書いておきます。&lt;code&gt;apiv1/tests/test_views.py&lt;/code&gt;を作成します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.urls&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;rest_framework.test&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;APITestCase&lt;/span&gt;


&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;todos.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Todo&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodoCreateTests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;APITestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_create_success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"test_create_success"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"apiv1:todos-list"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;expected_json_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"checked"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertJSONEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected_json_dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_create_bad_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"apiv1:todos-list"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;下記コマンドで全テストが捜索され実行されます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight postscript"&gt;&lt;code&gt;&lt;span class="nf"&gt;python&lt;/span&gt; &lt;span class="nf"&gt;manage.py&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;アプリケーションやクラスを指定して実行もできますが、ここでは割愛します。&lt;/p&gt;

&lt;h2&gt;
  
  
  シリアライザのテスト
&lt;/h2&gt;

&lt;p&gt;シリアライザのテストは、実際のリクエストを受け入れる際のバリデーションと、レスポンスを返す時の出力内容を検証すれば大丈夫だと思います。引き続き、&lt;code&gt;apiv1/tests/test_serializers.py&lt;/code&gt;を作成します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.test&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestCase&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;todos.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Todo&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;..serializers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TodoSerializer&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodoSerializerTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_input_valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;input_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"test_input_valid"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;serializer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TodoSerializer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;input_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_valid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_input_invalid_if_title_is_blank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;input_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;serializer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TodoSerializer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;input_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_valid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertCountEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertCountEqual&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;serializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"blank"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_output_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"test_output_data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;serializer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TodoSerializer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;expected_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"checked"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertDictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;とはいえ、先ほどのビューのテストで作成を検証していて、&lt;code&gt;title&lt;/code&gt;がブランクの時にエラーがでる処理を検証しているので少し被っている気もします。&lt;br&gt;
上記の例で言えばシリアライザのテストのみでいいと思いますが、そもそもviewsetsを利用している場合は開発段階で十分テストされていると思うので、省略してしまっても大丈夫だと思います。&lt;/p&gt;

&lt;h2&gt;
  
  
  モデルのテスト
&lt;/h2&gt;

&lt;p&gt;こちらは通常のDjangoでのテストと変わらないので割愛します。&lt;/p&gt;

&lt;h2&gt;
  
  
  参考
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3FgLziJ"&gt;現場で使える Django REST Framework の教科書&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.django-rest-framework.org/"&gt;Django REST framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.djangoproject.com/ja/4.0/"&gt;Django ドキュメント&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>django</category>
      <category>python</category>
    </item>
    <item>
      <title>Django REST frameworkの初期セットアップ</title>
      <dc:creator>niwoshi</dc:creator>
      <pubDate>Thu, 20 Jan 2022 10:36:45 +0000</pubDate>
      <link>https://dev.to/takenoko-tech/django-rest-frameworknochu-qi-setutoatupu-5aoh</link>
      <guid>https://dev.to/takenoko-tech/django-rest-frameworknochu-qi-setutoatupu-5aoh</guid>
      <description>&lt;p&gt;Django REST framework（以下DRF）でバックエンドを構築する際の初期設定をメモ代わりに書いておきます。&lt;br&gt;
（書かないと無限に忘れるので・・・）&lt;/p&gt;
&lt;h2&gt;
  
  
  プロジェクトの作成
&lt;/h2&gt;

&lt;p&gt;プロジェクト名は&lt;code&gt;tutorial&lt;/code&gt;としておきます。&lt;br&gt;
ディレクトリを作成して仮想環境(venv)が導入されている前提です。&lt;/p&gt;

&lt;p&gt;まずは&lt;code&gt;requirements.txt&lt;/code&gt;に必要最低限のライブラリのみ書いておきます。&lt;br&gt;
よく使うライブラリもこのタイミングで入れていいと思います。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;django
djangorestframework
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;必要パッケージのインストール&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight postscript"&gt;&lt;code&gt;&lt;span class="nf"&gt;pip&lt;/span&gt; &lt;span class="nf"&gt;install&lt;/span&gt; &lt;span class="nf"&gt;-r&lt;/span&gt; &lt;span class="nf"&gt;requirements.txt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Djangoプロジェクトの作成をコマンドで行います。&lt;br&gt;
この辺は完全に好みだと思うのですが、&lt;code&gt;プロジェクト名/プロジェクト名/settings.py&lt;/code&gt;みたいにプロジェクト名が二重にネストするのがちょっと気持ち悪いので、この設定ファイルなどがあるディレクトリ名を&lt;code&gt;config&lt;/code&gt;にするために、下記のようにDjangoプロジェクトを作成します。&lt;br&gt;
仮想環境を入れたプロジェクトファイル下で下記のコマンドを実行します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight postscript"&gt;&lt;code&gt;&lt;span class="nf"&gt;django-admin&lt;/span&gt; &lt;span class="nf"&gt;startproject&lt;/span&gt; &lt;span class="nf"&gt;config&lt;/span&gt; &lt;span class="nf"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;プロジェクト全体のディレクトリ構成は下記の様になります。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tutorial
├─ venv
├─ requirements.txt
├─ manage.py
└─ config
   ├─ __init__.py
   ├─ asgi.py
   ├─ settings.py
   ├─ urls.py
   └─ wsgi.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;機能的には&lt;code&gt;tutorial/tutorial&lt;/code&gt;になってもなんら問題無いのでこの辺は完全に好みです。むしろこっちの方が面倒。&lt;br&gt;
動作チェックはDRFとして動いて欲しいので、適当なモデルを作成してから行います。&lt;/p&gt;
&lt;h2&gt;
  
  
  アプリケーションの追加
&lt;/h2&gt;

&lt;p&gt;試しにTodoを管理するアプリケーションを想定して構築してみます。&lt;br&gt;
DRFはAPIエンドポイントとして運用される想定なので、一応バージョニングを意識して&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;APIエンドポイント用のアプリケーション（ここでは&lt;code&gt;apiv1&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;モデル用のアプリケーション（ここでは&lt;code&gt;todos&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;を分離しておきます。&lt;br&gt;
&lt;code&gt;manage.py&lt;/code&gt;でアプリケーションを作成しておきます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight postscript"&gt;&lt;code&gt;&lt;span class="nf"&gt;python&lt;/span&gt; &lt;span class="nf"&gt;manage.py&lt;/span&gt; &lt;span class="nf"&gt;startapp&lt;/span&gt; &lt;span class="nf"&gt;apiv1&lt;/span&gt;
&lt;span class="nf"&gt;python&lt;/span&gt; &lt;span class="nf"&gt;manage.py&lt;/span&gt; &lt;span class="nf"&gt;startapp&lt;/span&gt; &lt;span class="nf"&gt;todos&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ここで初期設定もかねて&lt;code&gt;config/settings.py&lt;/code&gt;を編集します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

from pathlib import Path
from django.conf.locale.ja import formats as ja_formats

...

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "rest_framework",
    "apiv1.apps.Apiv1Config",
    "todos.apps.TodosConfig",
]

...

# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/

LANGUAGE_CODE = "ja"

TIME_ZONE = "Asia/Tokyo"

USE_I18N = True

USE_L10N = True

USE_TZ = True

ja_formats.DATE_FORMAT = "Y/m/d"
ja_formats.DATE_INPUT_FORMATS = ("%Y/%m/%d",)
ja_formats.DATETIME_FORMAT = "Y/m/d H:i:s"
ja_formats.DATETIME_INPUT_FORMATS = ("%Y-%m-%d %H:%M:%S",)

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;INSTALLED_APPS&lt;/code&gt;の追記は、Django 3.2からAutomatic AppConfig discoveryで不要になったと聞いたのですが、自分の環境では何故か管理画面にモデルが表示されないので追記しています。&lt;/p&gt;

&lt;h2&gt;
  
  
  モデルの作成
&lt;/h2&gt;

&lt;h3&gt;
  
  
  models.pyの編集
&lt;/h3&gt;

&lt;p&gt;何はともあれモデルがないと始まりません。&lt;br&gt;
Todoモデル&lt;code&gt;todos/models.py&lt;/code&gt;を作成していきます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;db_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;todo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;UUIDField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;editable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;checked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BooleanField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DateTimeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auto_now_add&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DateTimeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auto_now&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__str__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;idをUUIDにしていますが、ここは好みです。（とはいえ、UUIDの方が推奨されそうですが・・・）&lt;/p&gt;

&lt;h3&gt;
  
  
  admin.pyの編集
&lt;/h3&gt;

&lt;p&gt;モデルを作ったら管理画面で編集できる様にしておきたいです。&lt;br&gt;
&lt;code&gt;todos/admin.py&lt;/code&gt;を編集します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.contrib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Todo&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodoAdmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelAdmin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;checked&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;list_display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;checked&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;created_at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;updated_at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TodoAdmin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;一応管理画面を確認しておきたいので念のためDBを更新してから&lt;code&gt;createsuperuser&lt;/code&gt;で管理ユーザーを作成しておきます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight postscript"&gt;&lt;code&gt;&lt;span class="nf"&gt;python&lt;/span&gt; &lt;span class="nf"&gt;manage.py&lt;/span&gt; &lt;span class="nf"&gt;makemigrations&lt;/span&gt;
&lt;span class="nf"&gt;python&lt;/span&gt; &lt;span class="nf"&gt;manage.py&lt;/span&gt; &lt;span class="nf"&gt;migrate&lt;/span&gt;
&lt;span class="nf"&gt;python&lt;/span&gt; &lt;span class="nf"&gt;manage.py&lt;/span&gt; &lt;span class="nf"&gt;createsuperuser&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ここまでやれば管理画面からモデルを操作できるので、動作に不安のある場合はこの辺で新規作成などしてみても良いと思うます。&lt;/p&gt;

&lt;h2&gt;
  
  
  シリアライザの作成
&lt;/h2&gt;

&lt;p&gt;DRF特有の存在であるシリアライザを作成します。&lt;br&gt;
これはAPIの機能なので&lt;code&gt;apiv1/serializers.py&lt;/code&gt;に作成します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rest_framework&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;serializers&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;todos.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Todo&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodoSerializer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelSerializer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Todo&lt;/span&gt;
        &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;checked&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ビューの作成
&lt;/h2&gt;

&lt;p&gt;API用のビュー&lt;code&gt;apiv1/views.py&lt;/code&gt;を編集します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rest_framework&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;viewsets&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;todos.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Todo&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.serializers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TodoSerializer&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodoViewSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewsets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelViewSet&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;queryset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;serializer_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TodoSerializer&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ルーティングの設定
&lt;/h2&gt;

&lt;p&gt;大体の機能は実装できたので、ルーティングの設定をしておきます。&lt;br&gt;
まずは&lt;code&gt;apiv1/urls.py&lt;/code&gt;を作成します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.urls&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rest_framework&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;routers&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt;

&lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;routers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DefaultRouter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;todos&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TodoViewSet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;basename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;todos&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;apiv1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;routerに&lt;code&gt;todos&lt;/code&gt;を登録する際に&lt;code&gt;basename="todos"&lt;/code&gt;と指定しているのは、&lt;code&gt;django.urls&lt;/code&gt;の&lt;code&gt;reverse&lt;/code&gt;関数で逆引きする際に&lt;code&gt;apiv1:todos-list&lt;/code&gt;としたい為です。コレを指定していない場合はビューの&lt;code&gt;queryset&lt;/code&gt;に登録したモデルの名称が採用されて&lt;code&gt;apiv1:todo-list&lt;/code&gt;と参照されてしまう様です。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;今回はビューをModelViewSetで実装したのでルーティングが楽です。&lt;br&gt;
あとは&lt;code&gt;config/urls.py&lt;/code&gt;を編集します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.contrib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.urls&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt;

&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api/v1/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;apiv1.urls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ここまでやれば、APIとしては実装完了です。&lt;/p&gt;

&lt;h2&gt;
  
  
  動作チェック
&lt;/h2&gt;

&lt;p&gt;DRFはデバッグモードではブラウザ上で色々確認出来ます。&lt;br&gt;
試しにアクセスしてみると、何もないリストが返却されているのが確認出来ると思います。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftj21gjvl0sb0cwhaprfz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftj21gjvl0sb0cwhaprfz.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;POST用のフォームもあるので、そちらから追加して見るもヨシ、cURLなどで追加して見るもヨシです。&lt;/p&gt;

&lt;h2&gt;
  
  
  おわり
&lt;/h2&gt;

&lt;p&gt;上記だけでCRUD操作ができるAPIが構築でき、管理画面も使いやすいしカスタマイズしやすいのがDRFの良さだと思います。&lt;br&gt;
本来であれば認証まわりも実装すると思いますが、今回は簡略化のため省きました。&lt;br&gt;
実際に実装する場合はDRFの設定（settings.py）とビューでの設定などが必要になると思います。&lt;/p&gt;

&lt;h2&gt;
  
  
  参考
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3FgLziJ" rel="noopener noreferrer"&gt;現場で使える Django REST Framework の教科書&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.django-rest-framework.org/" rel="noopener noreferrer"&gt;Django REST framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.djangoproject.com/ja/4.0/" rel="noopener noreferrer"&gt;Django ドキュメント&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>django</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>DjangoのClass-Based Viewで権限確認を行う</title>
      <dc:creator>k11o</dc:creator>
      <pubDate>Fri, 12 Nov 2021 14:55:22 +0000</pubDate>
      <link>https://dev.to/takenoko-tech/djangonoclass-based-viewdequan-xian-que-ren-woxing-u-hlh</link>
      <guid>https://dev.to/takenoko-tech/djangonoclass-based-viewdequan-xian-que-ren-woxing-u-hlh</guid>
      <description>&lt;p&gt;DjangoでClass-Based ViewによるDetailView等を実装している際に、そのViewへのアクセスの可否をカスタムで確認したい場合があると思います。&lt;br&gt;
例えば下記の様なモデルのDetailViewにおいて、各ユーザー自分のデータしか見れないようにする、などと言った場面です。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Memo(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;path('memo/&amp;lt;int:pk&amp;gt;', views.MemoDetailView.as_view(),
         name='memo_detail'),
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class MemoDetailView(DetailView):
    model = models.Memo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;このままだと memo/2 のような適当な URL を入れることで誰でも閲覧可能になってしまいます。これを自分の Memo しか見れないようにします。&lt;/p&gt;

&lt;p&gt;このような場面のためにテスト用のclassが用意されています。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;class UserPassesTestMixin
&lt;a href="https://docs.djangoproject.com/ja/3.2/topics/auth/default/#django.contrib.auth.mixins.UserPassesTestMixin"&gt;https://docs.djangoproject.com/ja/3.2/topics/auth/default/#django.contrib.auth.mixins.UserPassesTestMixin&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;test_func で OK/NG を Bool で返せば良いので、今回の場合だと下記のようなclassを作成し&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ObjectOwnerCheckMixin(UserPassesTestMixin):
    def test_func(self):
        login_user = self.request.user
        object_owner = self.get_object().user
        return login_user == object_owner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;View側ではそれを継承すればOKです。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class MemoDetailView(ObjectOwnerCheckMixin,DetailView):
    model = models.Memo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>django</category>
      <category>python</category>
    </item>
  </channel>
</rss>
