How to update a file location in a FileField?
我在模型中有一个
目前,我使用自定义
在模型的
这里发布的所有解决方案,以及我在网上看到的所有解决方案都涉及使用第三方应用程序或您已经拥有的解决方案。
我同意@Phillip,没有简单的方法可以完成您想要的事情,即使使用第三方应用程序,也需要一些工作才能使其适应您的目的。
如果您有许多需要此行为的模型,则只需实现pre_save信号并只编写一次该代码即可。
我也建议您阅读Django Signals,我相信您会发现它非常有趣。
很简单的例子:
1 2 3 4 5 6 7 8 9 10 | from django.db.models.signals import pre_save from django.dispatch import receiver @receiver(pre_save, sender=Product) def my_signal_handler(sender, instance, **kwargs): """ Sender here would be the model, Product in your case. Instance is the product instance being saved. """ # Write your solution here. |
这是一个可以为您处理django-smartfields的应用程序。
我为此添加了一个特殊的处理器,只是因为它似乎是一个有用的功能。
工作原理:
-
更改
upload_to 中指定的文件名,并 -
每当更改
label 字段时,FileDependency 都会处理文件名。
值得注意的是,只有在使用FileSystemStorage的情况下,才使用file_move_safe重命名文件,因为@Phillip提到您不想使用Cloud文件存储,因为通常这些后端不支持文件重命名。
还几个笔记。您不必使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | from django.db import models from smartfields import fields, processors from smartfields.dependencies import FileDependency from smartfields.utils import UploadTo def name_getter(name, instance): return instance.label class TestModel(models.Model): label = fields.CharField(max_length=32, dependencies=[ FileDependency(attname='dynamic_file', keep_orphans=True, processor=processors.RenameFileProcessor()) ]) dynamic_file = models.FileField( upload_to=UploadTo(name=name_getter, add_pk=False)) |
我认为您使用save()方法的方法是正确且"简单"的方法,除了我会使用pre_save信号而不是覆盖save方法(这通常是个坏主意)来做到这一点。
如果您要在其他模型上重复这种行为,则使用连接到pre_save信号的方法还可以使您简单地重新使用该方法。
有关pre_save的更多信息:https://docs.djangoproject.com/en/1.8/ref/signals/#pre-save
好的,它不能是lambda,因为lambda由于某些原因无法序列化,但这是简单的答案。
1 2 3 4 5 6 7 8 9 10 11 12 13 | def pic_loc(instance, filename): """ :param instance: Product Instance :param filename: name of image being uploaded :return: image location for the upload """ return '/'.join([str(instance.pk), str(instance.slug), filename]) Class Product(models.Model): image = models.ImageField(upload_to=pic_loc) slug = models.SlugField() user = models.ForeignKey(User, related_name="products") |
然后找到说pk = 1与:
slug ='new-thing'将//myexample.com/MEDIA_ROOT/1/new-thing/mything.png
1 | <img src="{{ obj.image.url }}"> |
这是假设您已设置MEDIA_ROOT,因为上载会转到媒体和媒体url。如在生产中一样处理静态文件,则以MEDIA_URL命名。
upload_to将对象实例和文件名都传递给您的函数,从那里您可以对其进行操作。
要更改实际的文件名,您需要在save()方法中做一些额外的工作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | from django.core.files import File class Product(models.Model): label = CharField(max_length=255) ... def save(self, **kwargs): # here we use os.rename then change the name of the file # add condition to do this, I suggest requerying the model # and checking if label is different if self.pk: # Need this to mitigate error using self.pk if Product.objects.get(pk=self.pk).label != self.label: path = self.image.path rename = '/'.join(path.split('/')[:-1]) + '/' + self.label os.rename(path, rename) file = File(open(rename)) self.image.save(self.label, file) return super(Product, self).save(**kwargs) |
如果文件扩展名很重要(很可能很重要),请在创建标签时将其添加到标签中,或者我们将旧文件扩展名作为字符串的一部分:
1 2 3 4 5 | filename, file_extention = os.splitext(path) rename += file_extension # before renaming and add to label too os.rename(path, rename) self.image.save(self.label + file_extension, file) |
我实际上建议写一个重命名函数作为您app_label.utils的一部分
检查文件是否存在很简单
1 2 3 | if os.path.isfile(rename): # you can also do this before renaming, # maybe raise an error if the file already exists |