作成: 2019年02月25日
更新: 2019年02月25日
DjangoのmodelのFileFieldはupload_to=で指定することで保存する際のディレクトリ、ファイル名を指定できる。このときにディレクトリ名、ファイル名にmodelのprimary keyを使用したい。
Djangoのprimary keyは通常自動生成されるが、これはmodelの保存時に自動生成されるためmodel生成時に呼ばれるupload_toで用いることができない(無理やり呼ぶとNoneになる)。
max(id)+1のようにしてもいいが生成されるidがmax(id)+1である保証がない。
以下を参考にして作成した
python - Django admin file upload with current model id - Stack Overflow
from django.db import models
from django.db.models import CharField, FileField, DateTimeField, IntegerField
from django.utils import timezone
from django.dispatch import receiver
from django.db.models.signals import post_save, pre_save
import os.path
def md_file_path(instance, filename):
fn, ext = os.path.splitext(filename)
return "article/{id}/{id}{ext}".format(id=instance.pk, ext=ext)
class Article(models.Model):
title = CharField(max_length=128)
article = models.FileField(upload_to=md_file_path)
post_date = DateTimeField(default=timezone.now)
_UNSAVED_FILEFIELD = 'unsaved_filefield'
@receiver(pre_save, sender=Article)
def skip_saving_file(sender, instance, **kwargs):
if not instance.pk and not hasattr(instance, _UNSAVED_FILEFIELD):
setattr(instance, _UNSAVED_FILEFIELD, instance.article)
instance.article = None
@receiver(post_save, sender=Article)
def save_file(sender, instance, created, **kwargs):
if created and hasattr(instance, _UNSAVED_FILEFIELD):
instance.article = getattr(instance, _UNSAVED_FILEFIELD)
instance.save()
instance.__dict__.pop(_UNSAVED_FILEFIELD)
上のコードはmdファイルをアップロードするためのコードなので必要に応じて適宜読み換えてほしい。
@receiver
というデコレータを用いてpre_save、post_saveシグナルと関数を紐づける。シグナルについては以下を参照。
Signals | Django documentation | Django
pre_saveはDjangoのsaveメソッドの最初に呼ばれて、post_saveはDjangoのsaveメソッドの最後に呼ばれる。sender=でモデルを指定する。
skip_saving_fileはinstanceに_UNSAVED_FILEFIELDという属性を付加して、その値をarticleにしている。そしてinstanceのarticle(FileField)を一時的にNoneにしている。
save_fileでは_UNSAVED_FILEFIELDに避難させていたarticleをinstance.articleに戻し、instance.save()を改めて実行している。_UNSAVED_FILEFIELDはもう不要なのでinstance.__dict__.pop(_UNSAVED_FILEFIELD)で属性ごと削除している。
まとめると_UNSAVED_FILEFIELDというフィールドにarticleを避難させた状態でsaveを行い、primary keyが生成された後で避難させたarticleを戻すという処理を行わせている。
これでmdファイルを保存するとmedia/article/1/1.md、media/article/2/2.md……のように保存される。