如何使用 upload_to 动态路径将文件保存到模型

How to save a file to a model using upload_to for dynamic paths

我正在尝试为每个用户创建一个文件夹来放入他们的项目。因此他们的文件将具有路径 ..\project\id\filenameid 是用户 idfilename 是文件名。现在在 Filefield 中使用 upload_toinstancefilename)允许的参数,我意识到 instance.id 将是 None 和路径到文件将是 ..\project\None\filename 而不是 ..\project\id\filename.

正在阅读 Django documentation upload_to 我看到了这个:

In most cases, this object will not have been saved to the database yet, so if it uses the default AutoField, it might not yet have a value for its primary key field.

我的解释是creating a new record和user_directory_path不是同时实例化的,也就是说,当我在Project模型上调用create时,instance.id 将是 None。我现在的问题是,有没有办法解决这个问题?虽然我看到 upload_to 方便,但对于像我正在做的那样的动态路径来说并不一定方便。我正在考虑创建记录,然后在更新中添加文件路径,但我正在寻找一种可以一步保存所有内容的方法。

models.py
def user_directory_path(instance, filename):
    # file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
    return 'project/{0}/{1}'.format(instance.user.id, filename)


class Project(models.Model):

     email = models.ForeignKey(User,
                          to_field="email",
                          max_length=50
    )
    title = models.CharField(max_length=100)
    date_created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    file = models.FileField(upload_to=user_directory_path, validators=[validate_file_type], null=True)

当表单通过验证时,这是 views.py。注意 user_directory_pathcreate.

之前调用
email = request.user.email
title = request.POST.get('title', '')
file = request.FILES['file']
filename = file.name
instance = Usermie.objects.get(email=request.user.email)

# Save to model
user_directory_path(instance=instance, filename=filename)
Project.objects.create(
    title=title,  file=file,
)

一个简单的解决方案可以是在没有文件的情况下保存对象,然后像这样保存文件

email = request.user.email
title = request.POST.get('title', '')
file = request.FILES['file']
filename = file.name
instance = Usermie.objects.get(email=request.user.email)

# Save to model
user_directory_path(instance=instance, filename=filename)
project = Project.objects.create(title=title)
project.file = file
project.save()

如果像你说的那样,你想在文件路径中使用的id是用户的id,而不是项目的id..那么没有问题,因为你保存时用户已经存在该项目。由于 emailUser 的外键,您只需执行:

def user_directory_path(instance, filename):
    # file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
    return 'project/{0}/{1}'.format(instance.email.id, filename)

但我要指出的是,在 Django 的做事方式中,创建一个名为 email 的字段作为 User 的外键实际上非常令人困惑。数据库中的字段将被称为 email_id.. 并且模型字段的值将 return User.. 的实例而不是实际的电子邮件地址,即使电子邮件地址是列中存储了什么。要获取电子邮件地址,您需要执行以下操作之一:

myproject.email.email    
myproject.email_id

谁都不是很清楚。因此,除非您有充分的理由这样做,否则您应该调用字段 user 并删除 to_field='email'。允许 Django 通过 id 连接表,这是默认行为。

然后如果您需要用户电子邮件地址,您可以随时通过

获取
 myproject.user.email  

好处是,如果用户更改了他们的电子邮件地址,它将随处更改,您不必依赖级联更新来修复所有外键。

相信我,在使用 Django 时,你希望按 id(默认)执行 ForeignKey,除非有原因...