I have a model that stores an image in a media subdirectory, "media/games/". The image uploads to the correct location but when I try to retrieve it in the admin page it's trying to retrieve it in the base media/ path, and doesn't seem to reach down into the games folder, so that if I look under:
localhost:8000/media/games/image.png
it will show the image, but if I am in admin and click on the image link for the preview it tries to find it at:
localhost:8000/media/image.png
Shouldn't the ImageField be "games/image.png" instead of just "image.png"? I don't think the image field is storing the path correctly.
Here are my files:
MODELS:
from django.db import models
from django.core.files.storage import FileSystemStorage
fs = FileSystemStorage(location="media/games/")
class Game(models.Model):
title = models.CharField(max_length=127, unique=True)
slug = models.SlugField(max_length=127, unique=True)
summary = models.TextField(null=True, blank=True)
description = models.TextField(null=True, blank=True)
release_date = models.DateField('date released', null=True, blank=True)
released = models.BooleanField(default=False)
purchase_link = models.URLField(max_length=255, null=True, blank=True)
card_image = models.ImageField(storage=fs, null=True, blank=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.TimeField(auto_now=True)
def __str__(self):
return str(self.title)
URLS:
from django.conf.urls import url, include
from django.contrib import admin
from InvenTorrey.settings import base
from django.conf.urls.static import static
from games import urls as game_urls
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^^api/v1/', include(game_urls)),
]
if base.DEBUG is True:
urlpatterns += static(base.MEDIA_URL, document_root=base.MEDIA_ROOT)
SETTINGS:
BASE_DIR =
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
MEDIA_ROOT = os.path.join(BASE_DIR, "media/")
STATIC_ROOT = os.path.join(BASE_DIR,"static/")
STATIC_URL = '/static/'
MEDIA_URL = '/media/'
Instead of storage argument try to use upload_to:
card_image = models.ImageField(upload_to='games/', null=True, blank=True)
This allows you to save images to media/games/ directory, and also will add games/ to image's url path.
Related
I am uploading an image file from Flutter to Django, the image is getting saved properly in my backends directory under assets/images, and when I query the database with a get I get the proper path of the file. But when I go to the URL in the browser the image does not appear and I get an error. I am using rest-framework for my app.
Model:
class Product(models.Model):
supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE)
name = models.CharField(max_length=200)
quantity = models.IntegerField(default=0)
barcode = models.CharField(max_length=200)
cost_price = models.FloatField(default=0)
selling_price = models.FloatField(default=0.0)
image = models.ImageField(upload_to='images/', null=True)
Settings.py:
MEDIA_ROOT = os.path.join(BASE_DIR, 'assets')
MEDIA_URL = '/pm/assets/'
Urls.py:
urlpatterns = [
path('', include(router.urls)),
]
urlpatterns += staticfiles_urlpatterns()
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Serializer:
class SuppliersSerializer(serializers.ModelSerializer):
product_set = ProductsSerializer(read_only=True, many=True)
class Meta:
model = Supplier
fields = ['pk','user','name','email','phone','product_set']
class ProductsSerializer(serializers.ModelSerializer):
transactions_set = TransactionsSerializer(read_only=True, many=True)
class Meta:
model = Product
fields = ['pk','name','quantity','barcode','cost_price','image', 'selling_price', 'transactions_set']
JSON response:
{
"pk": 13,
"name": "pi3",
"quantity": 3,
"barcode": "11111",
"cost_price": 10.0,
"image": "/pm/assets/images/533a6ac0-f682-4814-9237-89df8e02fda715039130977982609.jpg",
"selling_price": 20.0,
"transactions_set": []
},
But when visiting: http://localhost:8000/pm/assets/images/533a6ac0-f682-4814-9237-89df8e02fda715039130977982609.jpg
I get this error:
Solution was that it was trying to use the URL /pm/pm/assets/images, I changed in my settings.py:
MEDIA_ROOT = os.path.join(BASE_DIR, 'assets')
MEDIA_URL = '/assets/'
The image is now being served by django successfully
I am trying to save Community image and it saves it to /api/media/imagename.jpg, but when I retrieve a community it gives me /media/imagename.jpg without /api and then it can't find a image.
So I have this Community model with FileField:
class Community(models.Model):
"""DB Model for Community"""
name = models.CharField(max_length=150, unique=True)
description = models.TextField(default="")
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
number_of_members = models.IntegerField(default=1)
community_image = models.FileField(blank=False, null=False, default="", upload_to="")
def __str__(self):
return self.name
And I have this in my settings.py:
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
And then this is in my urls.py:
# urls.py in authentication_api application
router = DefaultRouter()
router.register('communities', views.CommunityViewSet)
urlpatterns = [
path('', include(router.urls)),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
# urls.py in root urls file
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('authentication_api.urls'))
]
What should I change to it returns correct path to image.
If you need any more code sample please comment.
Ideally, you should store media files like:
media/api/imagename.jpg
And not:
api/media/imagename.jpg
If you want to store like:
media/api/imagename.jpg
Then in the community_image field, you just need to change:
upload_to=""
To:
upload_to="api/"
I am using DRF for storing user uploaded images to S3 and in S3 i can see that images are public accessible using the URL.
My concern over here is there any best way to secure this images and restrict them for owner of that image only to view it.
I am using Heroku to deploy my DRF API Framework but i see this as security concern for my user who are uploading image files to S3 bucket.
I am trying to isolate user images by their name it self.But still it is public so i can access this images for another user just figure it out there name.
Here is S3 URL for media Images
https://xxx.s3.amazonaws.com/media/persons/niravjoshi/20181218152410.jpg
Here is my settings.py for Django
import os
import pymysql # noqa: 402
pymysql.install_as_MySQLdb()
import dj_database_url
from decouple import config
import django_heroku
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
#SECRET_KEY = 'feufm)u(pvsvb%&_%%*)p_bpa+sv8zt$#_-do5q3(vou-j*d#p'
SECRET_KEY = config('SECRET_KEY')
DEBUG = config('DEBUG', default=False, cast=bool)
DATABASES = {
'default': dj_database_url.config(
default=config('DATABASE_URL')
)
}
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
#Django Project Apps
'persons',
'rest_framework',
'allauth',
'allauth.account',
'allauth.socialaccount',
'allauth.socialaccount.providers.google',
#'social_django',
]
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/
AWS_REGION = os.environ.get('AWS_REGION', '') # e.g. eu-west-1
AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY', '')
AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_KEY', '')
AWS_STORAGE_BUCKET_NAME = os.environ.get('S3_BUCKET', '')
AWS_QUERYSTRING_AUTH = False
AWS_S3_CUSTOM_DOMAIN = os.environ.get("AWS_S3_CUSTOM_DOMAIN", "")
MEDIAFILES_LOCATION = 'media'
DEFAULT_FILE_STORAGE = 'DjangoE2ISAapi.storage_backends.MediaStorage'
MEDIA_URL = "https://%s/%s/" % (AWS_S3_CUSTOM_DOMAIN, MEDIAFILES_LOCATION)
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
AWS_STATIC_LOCATION = 'static'
STATICFILES_STORAGE = 'DjangoE2ISAapi.storage_backends.StaticStorage'
STATIC_URL = "https://%s/%s/" % (AWS_S3_CUSTOM_DOMAIN, AWS_STATIC_LOCATION)
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'),
]
django_heroku.settings(locals())
from DjangoE2ISAapi.restconf.main import *
Here is my storage_backends.py
from django.conf import settings
from storages.backends.s3boto3 import S3Boto3Storage
class StaticStorage(S3Boto3Storage):
location = settings.AWS_STATIC_LOCATION
class MediaStorage(S3Boto3Storage):
location = settings.MEDIAFILES_LOCATION
Here is my Person model.py.
from django.core.serializers import serialize
from django.db import models
from django.conf import settings
import json
from django.core.serializers.json import DjangoJSONEncoder
# Create your models here.
def upload_file(instance,filename):
import os
from django.utils.timezone import now
filename_base, filename_ext = os.path.splitext(filename)
return "persons/{user}/{filename}".format(user=instance.UserName, filename=now().strftime("%Y%m%d%H%M%S")+filename_ext.lower())
class PersonQuerySet(models.QuerySet):
def serialize(self):
list_values=list(self.values('UserName','PersonId','PersonName','Person_Image','Person_sex','Person_BDate'))
print (list_values)
return json.dumps(list_values,sort_keys=True,indent=1,cls=DjangoJSONEncoder)
class PersonManager(models.Manager):
def get_queryset(self):
return PersonQuerySet(self.model,using=self._db)
class Person(models.Model):
UserName = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,)
PersonId = models.AutoField(primary_key=True)
PersonName = models.CharField("person's first name", max_length=30,null=False)
Person_Image = models.ImageField(upload_to=upload_file,null=True, blank=True)
SEX = (('M','Male'),('F','Female'), ('N','None'), )
Person_sex = models.CharField(max_length=1,choices=SEX,null=False)
Person_BDate = models.DateField(null=False)
Person_CDate = models.DateField(null=False,auto_now_add=True)
objects = PersonManager()
def __str__(self):
return str(self.PersonName) or ""
def serialize(self):
data={
'UserName': self.UserName,
'PersonId': self.PersonId,
'PersonName': self.PersonName,
'Person_Image':self.Person_Image,
'Person_sex': self.Person_sex,
'Person_Bdate': self.Person_BDate
}
data = json.dumps(data,sort_keys=True,indent=1,cls=DjangoJSONEncoder)
return data
#property
def owner(self):
return self.UserName
Here is response of Person API View:
The docs for boto's ACLs are here. I suggest just using the private "canned policy" -- since your users don't have S3 accounts anyway, it's by far the simplest idea. Your app will of course have to keep track of which user "owns" which files (which should be a very, very simple Django model!).
In order to enforce users only being able to download through your own application, just pass a small value to the expires_in parameter when generating the URL. Users will only get a valid download link through your application, and that link will be invalidated after their download.
Here is an example of the code used to generate the link for downloading :
#login_required
def download_document(request, file_id):
'''
Request handler to download file
'''
file = Document.objects.get(pk=file_id)
s3 = get_aws_s3_client() #function to create s3 session
download_url = s3.generate_presigned_url(
'get_object',
Params= {'Bucket': file.bucket_name, 'Key': file.key},
ExpiresIn=5, #the url won't be valid after only 5 seconds
)
return redirect(download_url)
You can go further and make the view valid only for the file owner by adding this code :
if file.owner == request.user :
return redirect(download_url)
else :
# render 403.html since access denied.
Edit :
As requested, this solution requires using a specific model to store informations related to each document.
The model will look similar to this :
class Image(models.Model):
customer = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete = models.CASCADE,)
key = models.CharField(max_length=120) #uuid64 will be stored here and used for s3 urls
name = models.CharField(max_length=120, null=True, blank=True)
size = models.FloatField()
human_size = models.CharField(max_length=120, null=True, blank=True)
filetype = models.CharField(max_length=120, null=True, blank=True)
fextension = models.CharField(max_length=30, null=True, blank=True)
bucket_name = models.CharField(max_length=120, null=True, blank=True)
region = models.CharField(max_length=120, null=True, blank=True)
s3link = models.CharField(max_length=170, null=True, blank=True)
timestamp = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
uploaded = models.BooleanField(default=False)
active = models.BooleanField(default=True)
def __str__(self):
return self.name
I can't discuss parts related to serialization since I have never used DRF.
I would add uuid field to every user like.
import uuid
class Person(models.Model):
uuid = models.UUID(default=uuid.uuid4)
....
You can set it as primary key as well instead of AutoField.
and put that unique uuid into URL instead of name in order to look like:
https://xxx.s3.amazonaws.com/media/persons/b3810ec3-dd9d-4f11-a1e1-47835c0058ec/20181218152410.jpg
That image will be still public, but it's impossible to access image if you don't know uuid of particular user.
If you want more secure solution not depending on URL only you need to add some Authentication logic.
I am having trouble linking views to a basic HttpResponse in an ecommerce template I am building.
The error I am getting is 404 but after looking through here and what the docs say I am a little confused as to what I have missed,
The Model
from django.db import models
class Category(models.Model):
parent = models.ForeignKey('self', null=True, blank=True)
name = models.CharField(max_length=255)
slug = models.SlugField(max_length=150)
description = models.TextField()
pub_date = models.DateTimeField(auto_now_add=True)
mod_date = models.DateTimeField(auto_now=True)
class Manufacturer(models.Model):
name = models.CharField(max_length=150)
slug = models.SlugField(max_length=150)
class Product(models.Model):
category = models.ForeignKey(Category)
manufacturer = models.ForeignKey(Manufacturer)
name = models.CharField(max_length=300)
slug = models.SlugField(max_length=150)
description = models.TextField()
photo = models.ImageField(upload_to='itemphotos')
price_in_sterling = models.DecimalField(max_digits =6, decimal_places=2)
available = models.BooleanField()
instock = models.IntegerField()
pub_date = models.DateTimeField(auto_now_add=True)
mod_date = models.DateTimeField(auto_now=True)
My views.py
from django.shortcuts import render
from django.http import HttpResponse
def index(request):
return HttpResponse("Hi, your view worked")
def catagory(request):
return HttpResponse("Hi, you are looking at the catagory landing page")
def manufacturer(request):
return HttpResponse("Hi, here you can shop by brand")
def product(request):
return HttpResponse("Hi, here you can search by product")
My urls.py
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [
url(r'^productcatalog/', include('productcatalog.urls')),
url(r'^admin/', include(admin.site.urls)),
]
My urls.py from the app produtcatalog
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'^productcatalog/$', views.catagory, name='catagory'),
url(r'^productcatalog/$', views.manufacturer, name='brand'),
url(r'^productcatalog/$', views.product, name='products'),
]
I am sure the error is in the second urls.py file but can't seem to catch it.
well I have another method for this
urls.py
from views import product
urlpatterns=[url(r'^product/$', product)]
views.py
def product(request):
view="""<html><head></head><body><p>something</p></body></html> """
return HttpResponse(view)
I don't remember if I used the render method but is really useful specially when you put variables inside a html page
You are using the same url for all the views functions. Try changing it.
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'^productcatalog-1/$', views.catagory, name='catagory'),
url(r'^productcatalog-2/$', views.manufacturer, name='brand'),
url(r'^productcatalog-3/$', views.product, name='products'),
]
Im trying to make a shopping portal with a recommendation system
Not able to display images in the templates...
The HTML pages when opened without running the server opens them though
tried setting the MEDIA_URL and MEDIA_ROOT
settings as given:
MEDIA_ROOT = "/home/jose/Estore/estore/images/"
MEDIA_URL = '/images/'
The Model I defined is as given:
#Model for Books
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100)
section = models.CharField(max_length=50)
authors = models.CharField(max_length=150)
subject = models.CharField(max_length=50)
publisher = models.CharField(max_length=50)
publication_date = models.DateField(blank=True, null=True)
price = models.DecimalField(max_digits=6,decimal_places=2)
photo = models.ImageField(upload_to='product_photo/books/',blank=True)
description = models.TextField()
rating = models.DecimalField(max_digits=2,decimal_places=1)
def __str__(self):
return self.title
Have you set your urlconf?
http://docs.djangoproject.com/en/dev/howto/static-files/#serving-other-directories
here is how to do that. You need to set this in order for static serving to work.
Did this help?