1

Django URLField喜欢在用户输入的末尾添加一个斜杠(/),强制所有 URL 都用额外的字符存储,这是错误的。如何停止这种行为并保存用户提交的 URL?

4

3 回答 3

3

检查https://github.com/django/django/blob/master/django/forms/fields.pyto_python_ URLField_

您可以看到它url_fields[2] = '/'几乎在方法末尾有一行to_python。它在 url 的末尾附加一个斜杠/。您可以在此行之前将执行此操作的逻辑视为注释。

如果给出了一些查询参数,这个斜杠是必要的。

如果您想避免这种行为,请编写您自己的字段,该字段从您的自定义类扩展URLField并覆盖to_python

于 2013-04-23T04:37:08.530 回答
2

我也一直在为此苦苦挣扎,因为它会导致某些网址出现问题。例如,http://www.nasa.gov/mission_pages/kepler/news/kepler-62-kepler-69.html/失败,但它可以在没有斜杠的情况下工作。

为了扩展 akshar 的答案,这里解释了执行此操作的方法。例如,在我的 models.py 文件中定义它并设置url = NoSlashURLField()而不是models.URLField()在我的模型中删除斜线:

try:
    from urllib.parse import urlsplit, urlunsplit
except ImportError:     # Python 2
    from urlparse import urlsplit, urlunsplit

class NoSlashURLField(models.URLField):
    description = "Remove the goddamn slash"
    __metaclass__ = models.SubfieldBase

    def __init__(self, *args, **kwargs):
        super(NoSlashURLField, self).__init__(*args, **kwargs)

    def to_python(self, value):
        def split_url(url):
            """
            Returns a list of url parts via ``urlparse.urlsplit`` (or raises a
            ``ValidationError`` exception for certain).
            """
            try:
                return list(urlsplit(url))
            except ValueError:
                # urlparse.urlsplit can raise a ValueError with some
                # misformatted URLs.
                raise ValidationError(self.error_messages['invalid'])

        value = super(NoSlashURLField, self).to_python(value)
        if value:
            url_fields = split_url(value)
            if not url_fields[0]:
                # If no URL scheme given, assume http://
                url_fields[0] = 'http'
            if not url_fields[1]:
                # Assume that if no domain is provided, that the path segment
                # contains the domain.
                url_fields[1] = url_fields[2]
                url_fields[2] = ''
                # Rebuild the url_fields list, since the domain segment may now
                # contain the path too.
                url_fields = split_url(urlunsplit(url_fields))
#            if not url_fields[2]:
#                # the path portion may need to be added before query params
#                url_fields[2] = '/'
            value = urlunsplit(url_fields)
        return value
于 2013-04-23T06:09:27.327 回答
0

对于那些在他们的站点上使用通常的 Django 管理表单,并且还使用 South 进行 DB 迁移的人,您可能希望使用以下方法而不是 Stonefury 的方法。他的方法改变了模型字段,这让 South 感到困惑,除非你添加一些特殊的代码。下面的方法只改变了管理员代码,让 South 完全不知道。

在您的应用程序中的某处定义此类:

class NoSlashURLFormField(forms.URLField):

    def to_python(self, value):
        def split_url(url):
            """
            Returns a list of url parts via ``urlparse.urlsplit`` (or raises a
            ``ValidationError`` exception for certain).
            """
            try:
                return list(urlsplit(url))
            except ValueError:
                # urlparse.urlsplit can raise a ValueError with some
                # misformatted URLs.
                raise ValidationError(self.error_messages['invalid'])

        if value:
            url_fields = split_url(value)
            if not url_fields[0]:
                # If no URL scheme given, assume http://
                url_fields[0] = 'http'
            if not url_fields[1]:
                # Assume that if no domain is provided, that the path segment
                # contains the domain.
                url_fields[1] = url_fields[2]
                url_fields[2] = ''
                # Rebuild the url_fields list, since the domain segment may now
                # contain the path too.
                url_fields = split_url(urlunsplit(url_fields))
            value = urlunsplit(url_fields)
        return value

然后编辑您的 admin.py 文件,如下所示:

from your_app.path.to.noslash import NoSlashURLFormField 
from django.contrib.admin.widgets import AdminURLFieldWidget

class MyModelAdmin(admin.ModelAdmin):

    ...

    formfield_overrides = {
        models.URLField: {
            'form_class': NoSlashURLFormField,
            # Need to specify the AdminURLFieldWidget here because it would
            # otherwise get defaulted back to URLInput. 
            'widget': AdminURLFieldWidget,
        }
    }
于 2014-02-21T20:55:09.267 回答