[Django] #32991: Change in behavior of FileField.url between versions 2.2.16 -> 3.2.5; returned 'relative' value contains leading slash

19 views
Skip to first unread message

Django

unread,
Aug 5, 2021, 12:49:21 PM8/5/21
to django-...@googlegroups.com
#32991: Change in behavior of FileField.url between versions 2.2.16 -> 3.2.5;
returned 'relative' value contains leading slash
-------------------------------------+-------------------------------------
Reporter: ElchinM. | Owner: nobody
Type: Bug | Status: new
Component: File | Version: 3.2
uploads/storage |
Severity: Normal | Keywords: FileField.url
Triage Stage: | Has patch: 0
Unreviewed |
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 1
UI/UX: 0 |
-------------------------------------+-------------------------------------
Going through an upgrade of Django from version 2.2.16 to 3.2.5, believe I
have found a regression in FileField.url

I have searched all the release notes between these two versions for any
mentions of "url" but didn't find changes being mentioned to this
behavior. Some of the recent release notes for Django 2.2 (2.2.21 and
2.2.23) mention changes to FileField, maybe this is when the regression
was introduced.

Details of the issue:
Using the same model FileField definition and settings in both versions.

In 2.2.16 FileField.url returns a relative URL without a leading slash.
In 3.2.5 the same call returns a relative URL with a leading slash.

How to reproduce:

Using the following Model definition:

{{{
from django.contrib.gis.db import models

class TestModel(models.Model):
file_field = models.FileField(
upload_to='photos/%Y/%m/%d',
max_length=255,
null=True,
help_text=u"Test"
)
}}}

Using manage.py shell:
In 2.2.16
{{{
>>> test_model = models.TestModel.objects.create()
>>> f = open('setup.py')
>>> from django.core.files.base import File
>>> test_model.file_field.save('new_name', File(f))
>>> test_model.file_field.url
'photos/2021/08/05/new_name'
>>> import django
>>> django.__version__
'2.2.16'
}}}

In 3.2.5:

{{{
>>> from vault import models
>>> test_model = models.TestModel.objects.create()
>>> f = open('setup.py')
>>> from django.core.files.base import File
>>> test_model.file_field.save('new_name', File(f))
>>> test_model.file_field.url
'/photos/2021/08/05/new_name'
>>> import django
>>> django.__version__
'3.2.5'
}}}

The problem with the leading slash is that it is now treated as an
absolute URL.

--
Ticket URL: <https://code.djangoproject.com/ticket/32991>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Aug 5, 2021, 2:25:52 PM8/5/21
to django-...@googlegroups.com
#32991: Change in behavior of FileField.url between versions 2.2.16 -> 3.2.5;
returned 'relative' value contains leading slash
-------------------------------------+-------------------------------------
Reporter: Elchin Mammadov | Owner: nobody
Type: Bug | Status: closed
Component: contrib.staticfiles | Version: 3.2
Severity: Normal | Resolution: invalid
Keywords: FileField.url | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 1 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Mariusz Felisiak):

* status: new => closed
* resolution: => invalid
* component: File uploads/storage => contrib.staticfiles


Comment:

This is an intended change made in
c574bec0929cd2527268c96a492d25223a9fd576 (see #25598, and
[https://github.com/django/django/pull/11564#issuecomment-689521790
comment]).

--
Ticket URL: <https://code.djangoproject.com/ticket/32991#comment:1>

Django

unread,
Aug 5, 2021, 2:26:12 PM8/5/21
to django-...@googlegroups.com
#32991: Change in behavior of FileField.url between versions 2.2.16 -> 3.2.5;
returned 'relative' value contains leading slash
-------------------------------------+-------------------------------------
Reporter: Elchin Mammadov | Owner: nobody
Type: Bug | Status: closed
Component: contrib.staticfiles | Version: 3.2
Severity: Normal | Resolution: invalid
Keywords: FileField.url | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 1 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Mariusz Felisiak):

* cc: Florian Apolloner (added)


--
Ticket URL: <https://code.djangoproject.com/ticket/32991#comment:2>

Django

unread,
Aug 5, 2021, 3:06:04 PM8/5/21
to django-...@googlegroups.com
#32991: Change in behavior of FileField.url between versions 2.2.16 -> 3.2.5;
returned 'relative' value contains leading slash
-------------------------------------+-------------------------------------
Reporter: Elchin Mammadov | Owner: nobody
Type: Bug | Status: closed
Component: contrib.staticfiles | Version: 3.2
Severity: Normal | Resolution: invalid
Keywords: FileField.url | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 1 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Florian Apolloner):

Interesting, this is honestly a somewhat unexpected result. While the docs
do say
(https://docs.djangoproject.com/en/3.2/ref/models/fields/#django.db.models.fields.files.FieldFile.url)
> A read-only property to access the file’s relative URL by calling the
url() method of the underlying Storage class.
that this seems to be a relative URL, it also says that it calls
`Storage.url` which to the best of my knowledge usually always returned an
absolute URL
(https://docs.djangoproject.com/en/3.2/ref/files/storage/#django.core.files.storage.Storage.url)
-- well technically it returned whatever `MEDIA_URL + "/" + path` is (more
or less):
{{{
def url(self, name):
if self.base_url is None:
raise ValueError("This file is not accessible via a URL.")
url = filepath_to_uri(name)
if url is not None:
url = url.lstrip('/')
return urljoin(self.base_url, url)
}}}
so the only way I could imagine this to ever return a relative URL is by
settings `MEDIA_URL` to an empty string. What did you set `MEDIA_URL` to
(and if it is empty, then why)

--
Ticket URL: <https://code.djangoproject.com/ticket/32991#comment:3>

Django

unread,
Aug 5, 2021, 3:15:14 PM8/5/21
to django-...@googlegroups.com
#32991: Change in behavior of FileField.url between versions 2.2.16 -> 3.2.5;
returned 'relative' value contains leading slash
-------------------------------------+-------------------------------------
Reporter: Elchin Mammadov | Owner: nobody
Type: Bug | Status: closed
Component: contrib.staticfiles | Version: 3.2
Severity: Normal | Resolution: invalid
Keywords: FileField.url | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 1 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Elchin Mammadov):

`MEDIA_URL` is indeed an empty string in our settings, which is also the
default value.

--
Ticket URL: <https://code.djangoproject.com/ticket/32991#comment:4>

Django

unread,
Aug 5, 2021, 3:17:59 PM8/5/21
to django-...@googlegroups.com
#32991: Change in behavior of FileField.url between versions 2.2.16 -> 3.2.5;
returned 'relative' value contains leading slash
-------------------------------------+-------------------------------------
Reporter: Elchin Mammadov | Owner: nobody
Type: Bug | Status: closed
Component: contrib.staticfiles | Version: 3.2
Severity: Normal | Resolution: invalid
Keywords: FileField.url | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 1 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Florian Apolloner):

It is true that it is the default value but that was never a useful value
to begin with if you deployed your site. Assume the following (with the
default `MEDIA_ROOT`): you are on page http://example.com/sub1/sub2 and
request the URL of `my_image.jpg` which would yield
`photos/123/my_image.jpg`. Putting that into an `<img>` tag would result
in a final URL of http://example.com/sub1/sub2/photos/123/my_image.jpg. Do
the same on http://example.com/sub3 instead and you'd get
http://example.com/sub3/photos/123/my_image.jpg. So how did you ever serve
your images with this setup?

--
Ticket URL: <https://code.djangoproject.com/ticket/32991#comment:5>

Django

unread,
Aug 5, 2021, 9:55:40 PM8/5/21
to django-...@googlegroups.com
#32991: Change in behavior of FileField.url between versions 2.2.16 -> 3.2.5;
returned 'relative' value contains leading slash
-------------------------------------+-------------------------------------
Reporter: Elchin Mammadov | Owner: nobody
Type: Bug | Status: closed
Component: contrib.staticfiles | Version: 3.2
Severity: Normal | Resolution: invalid
Keywords: FileField.url | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 1 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Elchin Mammadov):

I see your point about the default value of `MEDIA_URL` not being useful
when using `.url` for serving static content from the site. In this use
case `MEDIA_URL` must be configured. This may be the most common use case.

The other use case to consider is when uploading files for consumption by
the web application. After the files have been uploaded, the application
has a need to find a relative path to the uploaded file via a model. And
Django FAQ documentation actually describes how to achieve this
https://docs.djangoproject.com/en/3.2/faq/usage/#how-do-i-use-image-and-
file-fields:
> All that will be stored in your database is a path to the file (relative
to MEDIA_ROOT). You’ll most likely want to use the convenience url
attribute provided by Django
What is returned by `.url` attribute is also a relative path to the file,
which the application can then access via the FileField on the model. If
there is no static content being served, then `MEDIA_URL` will not be set.
This is the use case and the setup used in our application. We are not
using Django templates or serving any static content. In our application
there are only a few places where `.url` attribute is being used, so it is
a simple change for us. There are other attributes provided by FileField
that can be used in lieu of `.url`.

--
Ticket URL: <https://code.djangoproject.com/ticket/32991#comment:6>

Reply all
Reply to author
Forward
0 new messages