<?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: k11o</title>
    <description>The latest articles on DEV Community by k11o (@k11o).</description>
    <link>https://dev.to/k11o</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%2Fuser%2Fprofile_image%2F717236%2Fee309aa9-098c-4dd8-8d96-5f3b9c3e698b.jpg</url>
      <title>DEV Community: k11o</title>
      <link>https://dev.to/k11o</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/k11o"/>
    <language>en</language>
    <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の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>
