0

我有一个由两个不同系统使用的数据库,因此我实际上需要 FileField 值有一个前导斜杠,如下所示:

/dirs/filename.ext

然而,在 Django 中,FileField 值不能有前导斜杠,因为它破坏了它们与MEDIA_ROOT.

所以我怀疑我要么必须创建一个自定义存储类,要么以某种方式自定义 FileField,以便在读取时删除前导斜杠并在保存时恢复。


如果有人想知道我为什么要这样做:我将文件镜像到单独的Django服务器上。

在 Django 服务器上,文件是相对于媒体根目录的。因此,假设媒体根目录为/path/to/myapp/media,则具有该路径的文件dirs/filename.ext将存在于/path/to/myapp/media/dirs/filename.ext.

同时,当镜像到另一台服务器时,它们是相对于 webroot 存储的。所以路径等价于文件的绝对URL(例如文件dirs/filename.ext存储在/path/to/example.com/dirs/filename.ext,访问为http://example.com/dirs/filename.ext)。

两台服务器都使用相同的数据库。

我意识到一个解决方案是在另一台服务器上使用该字段的任何地方都添加一个斜杠,但这是跨越许多不同的源文件,而在 Django 中,由于记录模型,我应该能够仅在models.py文件中进行更改它将适用于整个 Django 站点。


到目前为止,我已经尝试创建一个自定义版本,FileField它正确地预先设置/了查找和保存,但是在 Django 应用程序中使用时,我无法让它删除前导斜杠。


例子

想象一个名为 Tool 的记录,其中包含一个 PDF 文件作为其手册。在 Django 服务器上,它将因此显示在模板中:

<h1>{{ tool.name }}</h1>
<p>{{ tool.description }}</p>
<p><a href="{{ MEDIA_URL }}{{ tool.file.url }}">Link to Manual</a></p>

同时在另一台服务器上它更像(这是 CF 代码):

<h1>#GetTool.tool_name#</h1>
<p>#GetTool.tool_description#</p>
<p><a href="#GetTool.tool_file#">Link to Manual</a></p>

在第二个服务器示例中,它必须是绝对 URL。

所以,要清楚:

  • 第二台服务器不是 Django 项目
  • 在第二台服务器上更改代码会比在第一台服务器上更耗时
  • 因此 FileField 中的值需要是绝对 URL 才能与 Django 兼容,但需要使用前导斜杠保存以便与第二个服务器兼容。
4

4 回答 4

0

您可以尝试创建自定义字段。就像是:

class MyFileField(models.Field):
    attr_class = MyFieldFile

    def get_directory_name(self):
        return os.path.normpath(force_unicode(datetime.datetime.now().strftime(smart_str(self.upload_to))))

    def get_filename(self, filename):
        return os.path.normpath(self.storage.get_valid_name(os.path.basename(filename)))

    def generate_filename(self, instance, filename):
        return os.path.join(self.get_directory_name(), self.get_filename(filename))

    def get_prep_lookup(self, lookup_type, value):
        if hasattr(value, 'name'):
            value = value.name
        return super(FileField, self).get_prep_lookup(lookup_type, value)

    def get_prep_value(self, value):
        "Returns field's value prepared for saving into a database."
        # Need to convert File objects provided via a form to unicode for database insertion
        if value is None:
            return None
        return unicode(value)

这是默认的 django-1.4 代码。你应该修改它以在需要的地方添加斜杠

您还应该将 django.db.models.fields.files.FieldFile 子类化(您可以在示例中看到 MyFieldFile),因为当您访问字段值时,您实际上会获得此类。并且此类具有修改保存路径的保存方法。所以删除该类中的斜线。

这两个类的方法调用存储类的方法,因此另一种选择是创建替代存储

from django.core.files.storage import FileSystemStorage

class MyStorage(FileSystemStorage):
    def path(self, name):
        try:
            path = safe_join(self.location, name)
        except ValueError:
            raise SuspiciousOperation("Attempted access to '%s' denied." % name)
        return os.path.normpath(path)

    def _save(self, name, content):
        full_path = self.path(name)
        # many lines of code
        return name  # this is used as filename in database

在 django 设置中配置默认​​存储:

DEFAULT_FILE_STORAGE = 'yourapp.import.path.MyStorage'
于 2013-04-02T07:26:58.203 回答
0

如果我理解正确,那么你为什么不能只做一个附加或删除该字段的斜线而不是做一个特殊字段的方法?

这有点骇人听闻,但我想它可以工作。

class MyModelWithAppendingSlash(models.Model):
    my_file = FileField(options_go_here)

    def my_file_appended(self):
        return path = '/' + self.my_file.filename

    def my_file_slash_removed(self):
        return my_file.filename[1:]

然后,当您在 django 应用程序中引用模型时,请执行myModelWithAppendingSlashInstance.my_file_appended().my_file_slash_removed()

我剪掉了很多代码,你必须修改它,但你得到了我在这里想要实现的要点。这对您来说可能完全错误,但这就是我解释您的问题的方式。

于 2013-04-08T12:21:42.567 回答
0

如果没有一行代码,您可以使用数据库中的 VIEW 来解决这个问题。只需创建一个连接“/”(或“ http://example.com/”,如果您希望 url 真正是绝对的)和路径的视图。

CREATE VIEW CF_appname_tool AS
SELECT [other attributes], '/' + file as file
FROM appname_tool;

我需要知道您使用的是 SQL server,因为每个 DBMS 的字符串连接运算符都不同。

我怀疑更改 CF 代码以从 CF_appname_tool 中进行选择太麻烦了。您可以将数据从 appname_tool 移动到并在 Django Tool 模型上some_other_name设置db_table选项:

class Tool(model.Model):
    class Meta:
        db_table = 'some_other_name'

并将appname_toolVIEW 创建为以下选项some_other_name

CREATE VIEW appname_tool AS
SELECT [other attributes], '/' + file as file
FROM some_other_name;

如果您想在生产中以编程方式创建 VIEW,您可以收听post_syncdb信号。

于 2013-04-08T18:00:49.820 回答
0

终于想出了如何做到这一点。除了 FileField 之外,诀窍还在于继承 FieldFile:

class LeadingSlashFieldFile(files.FieldFile):
    def __init__(self, instance, field, name):
        name = re.sub(r'^/', '', name)
        super(LeadingSlashFieldFile, self).__init__(instance, field, name)

class LeadingSlashFileField(models.FileField):
    attr_class = LeadingSlashFieldFile

    def get_prep_lookup(self, lookup_type, value):
        if hasattr(value, 'name'):
            value = value.name
        if value[0] <> '/':
            value = "/" + value
        return super(LeadingSlashFileField, self).get_prep_lookup(lookup_type, value)

    def get_prep_value(self, value):
        value = super(LeadingSlashFileField, self).get_prep_value(value)
        if value is None:
            return None
        value = unicode(value)
        if value[0] <> '/':
            value = "/" + value
        return value

这似乎有效。

于 2013-10-23T20:04:52.463 回答