如何使用upload_to动态路径

问题描述:

我想创建一个文件夹的每个用户把他们的项目将文件保存到一个模型。因此,他们的文件将具有路径..\project\id\filenameid是用户idfilename是文件的名称。现在使用Filefield中的upload_toinstancefilename)所允许的参数,我意识到instance.id将是None,文件的路径将是..\project\None\filename而不是..\project\id\filename如何使用upload_to动态路径

现在读Django documentation upload_to我看到了这一点:

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

我的解释是,创建一个新的记录,并user_directory_path不是在同一时间实例,那就是,当我打电话createProject模型,instance.idNone。我的问题是,现在有没有办法解决这个问题?虽然我看到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。在create之前调用user_directory_path

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

如果像你说的,要在文件路径使用的ID是的id用户,而不是项目的ID ..那么没有问题,因为用户在保存项目时已经存在。由于email是一个外键User,你只是做:

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) 

但我要指出的是,在做的事情,做一个场称为email这是Django的方式外键User实际上是很混乱。数据库中的字段将被称为email_id ..并且模型字段的值将返回User ..的实例,而不是实际的电子邮件地址,即使电子邮件地址是存储在列中的。要获得电子邮件地址,您需要执行以下操作之一:

myproject.email.email  
myproject.email_id 

两者都不是很清楚。所以,除非你有这样的理由,否则你应该打电话给user,并取消to_field='email'。允许Django通过id加入表格,这是默认行为。

然后,如果你需要的用户电子邮件地址,你可以通过

myproject.user.email 

得到它的任何时间和好处是,如果用户更改他们的电子邮件地址,它会无处不在改变,你不必依赖在级联更新来修复所有外键。

信任我,使用Django,当你想通过id(默认),除非有一个理由这样做ForeignKey的...

+0

'而且奖金是,如果用户更改他们的电子邮件地址,它将在任何地方都会发生变化,您不必依赖级联更新来修复所有外键。“在ForeignKey中使用id的非常好的理由。 –

+0

谢谢!这实际上给了我更好的视力。我将返回并将所有其他模型中的电子邮件字段更改为用户标识。直到现在,我还没有意识到用户更改电子邮件的问题。谢谢@Massan。我一直知道这一点,但没有意识到我在做这件事。 – Jam1

+0

不客气,我很高兴我可以提供帮助。我应该提到的另一件事是,一旦创建了项目,就不能允许用户更改,因为这会改变目录路径,并且django不会再找到这些文件。 –

一个简单的解决方案,可以节省不使用文件对象,然后保存文件中像这样

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

这是看它一个很好的方式,但这样做这样我就不会得到用户的主键,但将使用项目模型的主键。 – Jam1

+1

在这种情况下,你应该写''project/{0}/{1}'.format(instance.email_id,filename)'作为'email'字段是'User'模型的外键。 –

+0

哦,我看到你在说什么,但因为我的电子邮件字段的属性为'to_field =“email”',它将返回一个路径'project/user_email/filename'。我可能不得不改变一些事情来实现它。谢谢! – Jam1