【问题标题】:How to save a file to a model using upload_to for dynamic paths如何使用 upload_to 将文件保存到模型以获取动态路径
【发布时间】:2018-01-03 13:03:33
【问题描述】:

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

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

在大多数情况下,这个对象不会被保存到数据库中 然而,所以如果它使用默认的 AutoField,它可能还没有 其主键字段的值。

我的解释是创建新记录和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,
)

【问题讨论】:

    标签: python django django-models django-file-upload


    【解决方案1】:

    如果,如您所说,您要在文件路径中使用的 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..,模型字段的值将返回User.. 的实例,而不是实际的电子邮件地址,即使电子邮件地址是存储在列中的内容。要获取电子邮件地址,您需要执行以下操作之一:

    myproject.email.email    
    myproject.email_id
    

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

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

     myproject.user.email  
    

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

    相信我,在使用 Django 时,除非有原因,否则您希望通过 id(默认)执行 ForeignKey...

    【讨论】:

    • And the bonus is that if the user changes their email address it will change everywhere, you don't have to rely on cascaded updates to fix all the foreign keys. 非常好的理由在 ForeignKey 中使用 id
    • 谢谢!这实际上给了我更好的视野。我将返回并将所有其他模型中的电子邮件字段更改为用户 ID。直到现在我才意识到用户更改电子邮件的问题。谢谢@MHassan。我一直都知道这一点,但没有意识到我正在这样做。
    • 不客气,很高兴我能提供帮助。我应该提到的另一件事是,一旦创建了项目,就不能允许用户更改,因为这会更改目录路径并且 django 将不再找到文件。
    【解决方案2】:

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

    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()
    

    【讨论】:

    • 这是一个很好的查看方式,但是这样做我不会得到用户的主键,而是会使用项目模型的主键。
    • 在这种情况下,你应该写'project/{0}/{1}'.format(instance.email_id, filename),因为email字段是User模型的外键。
    • 哦,我明白你在说什么,但是因为我的电子邮件字段具有属性to_field="email",它将返回一个路径project/user_email/filename。我可能需要改变一些东西才能让它工作。谢谢!
    • 如果要访问用户表中的其他字段,可以使用双下划线__,如project/{0}/{1}'.format(instance.email__id, filename)project/{0}/{1}'.format(instance.email.id, filename)。希望这会奏效。
    猜你喜欢
    • 2018-02-25
    • 1970-01-01
    • 1970-01-01
    • 2012-03-01
    • 2021-01-23
    • 1970-01-01
    • 2020-09-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多