Output (missing port ":1337" in image_url field)
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"user": 1,
"title": "Post 1",
"slug": "post1",
"image_url": "http://0.0.0.0/mediafiles/publisher/sample-image4.jpg",
"content": "First",
"draft": false,
"publish": "2019-04-26",
"updated": "2019-04-26T22:28:35.034742Z",
"timestamp": "2019-04-26T22:28:35.034795Z"
}
]
}
"image_url" field will not link correctly unless it includes the port like this:
"image_url": "http://0.0.0.0:1337/mediafiles/publisher/sample-image4.jpg",
Stack:
I am using Django REST framework to return a list of serialized objects. The objects contain a FileField called "image" and I can output the URL of this image. The only thing is when I click that link in the output in my browser, I cannot access the resource without manually adding the server port in the address like
http://0.0.0.0:1337/mediafiles/publisher/sample-image4.jpg
I'm not sure if it's an nginx issue, a Django settings issue or just how my code is configured. I'm having trouble finding any other reported cases via Google (probably because I'm still new to Django and not sure the correct configuration despite following tutorials).
I tried some of these solutions but they do not output the port.
There's this question, but I'm not using ImageField and I want to find a solution for cases where I'm using FileField. The comment on the main question indicates that adding the port should not be required too, so perhaps it's an infra problem and not a Django problem? Guidance on this would be awesome.
models.py
class Post(models.Model):
class Meta:
ordering = ('timestamp',)
user = models.ForeignKey(User, on_delete=models.PROTECT)
title = models.CharField(max_length=120)
slug = models.SlugField(unique=True)
image = models.FileField(upload_to='publisher/', null=True, blank=True)
content = models.TextField()
draft = models.BooleanField(default=False)
publish = models.DateField(auto_now=False, auto_now_add=False)
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
def __str__(self):
return self.title
def __unicode__(self):
return str(self.id)
def get_absolute_url(self):
return reverse("post:detail", kwargs={"slug":self.slug})
serializers.py
class PostSerializer(serializers.ModelSerializer):
image_url = serializers.SerializerMethodField()
class Meta:
model = Post
fields = [
'id',
'user',
'title',
'slug',
'image_url',
'content',
'draft',
'publish',
'updated',
'timestamp',
]
def get_image_url(self, post):
request = self.context.get('request')
if post.image and hasattr(post.image, 'url'):
image_url = post.image.url
return request.build_absolute_uri(image_url)
else:
return None
urls.py
urlpatterns = [
path('admin/', admin.site.urls),
re_path('blog/(?P<version>(v1|v2))/', include('blog.urls'))
]
...
[
url(r'^posts/$', PostListAPIView.as_view(), name='posts'),
]
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
views.py
class PostListAPIView(generics.ListAPIView):
model = Post
queryset = Post.objects.all()
serializer_class = PostSerializer
docker-compose.yml
version: '3.7'
services:
web:
build: ./app
command: gunicorn hello_django.wsgi:application --bind 0.0.0.0:8000
volumes:
- ./app/:/usr/src/app/
- static_volume:/usr/src/app/staticfiles
- media_volume:/usr/src/app/mediafiles
ports:
- "8000"
env_file: ./app/.env
environment:
- DB_ENGINE=django.db.backends.postgresql
- DB_USER
- DB_PASSWORD
- DB_HOST=db
- DB_PORT=5432
- DATABASE=postgres
depends_on:
- db
networks:
- backend
db:
image: postgres:10.7-alpine
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data/
networks:
- backend
nginx:
build: ./nginx
volumes:
- static_volume:/usr/src/app/staticfiles
- media_volume:/usr/src/app/mediafiles
ports:
- "1337:80"
depends_on:
- web
networks:
- backend
networks:
backend:
driver: bridge
volumes:
postgres_data:
static_volume:
media_volume:
nginx.conf
upstream hello_django {
server web:8000;
}
server {
listen 80;
location / {
proxy_pass http://hello_django;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
location /staticfiles/ {
alias /usr/src/app/staticfiles/;
}
location /mediafiles/ {
alias /usr/src/app/mediafiles/;
}
location /favicon.ico {
access_log off;
log_not_found off;
}
}
I finally found out how to fix the image URLs thanks to this question which is slightly different.
Solution 1
Add the port number in the Host header in the nginx config as follows:
location / {
proxy_pass http://hello_django;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host:1337; <<------- HERE
proxy_redirect off;
}
Solution 2
Change the Host header in the nginx config to http_host
as follows:
location / {
proxy_pass http://hello_django;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host; <<------- HERE
proxy_redirect off;
}
In either case, the image URLs are now returned as follows by DRF (image link).
HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 2,
"user": 1,
"title": "First post",
"slug": "first",
"image_url": "http://0.0.0.0:1337/mediafiles/publisher/background.gif", <----HERE
"content": "Second post content.",
"draft": false,
"publish": "2019-05-22",
"updated": "2019-05-22T09:41:36.257605Z",
"timestamp": "2019-05-22T07:58:01.471534Z"
}
]
}