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
Related
I am using create view to create a model instance (product).
Evey thing is working fine bt after doing some new migrations
i can't get the uploaded image, instead getting default image.
I think upload_to method of models isn't working.
i also used this in my urls
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
This is my settigs vars:
STATIC_URL = '/static/'
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
MEDIA_URL = '/media/'
This is my models.py:
class Product(models.Model):
TYPE = [
('Sell','Sell'),
('Rent','Rent'),
('Sell or Rent','Sell or Rent'),
]
owner = models.ForeignKey(Owner, on_delete=models.CASCADE)
title = models.CharField(max_length = 25)
type = models.CharField(max_length = 12, choices = TYPE, default = 'Sell')
price = models.IntegerField()
description = models.TextField(default="No Description Given")
image = models.ImageField(default='default.jpeg', upload_to='product')
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('store')
and this is my views.py:
class ProductCreateView(LoginRequiredMixin, CreateView):
model = Product
fields = ['title','type', 'price','description', 'image']
def form_valid(self, form):
print(self.request.POST)
owner , created = Owner.objects.get_or_create(user=self.request.user)
form.instance.owner = self.request.user.owner
return super().form_valid(form)
I am getting default image for every new product created.
Thanks
P.S. when i am adding product from admin panel then every thing is working fine.
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 want to upload an image to my Django Rest API and click the link I get and then see the actual image (like any other image on the internet), this is so I can then display the image in my mobile app.
This is my code:
class Assignment(models.Model):
name = models.CharField(max_length=100)
file = models.FileField(blank=False, null=False)
class Meta:
verbose_name_plural = 'Assignments'
def __str__(self):
return "{name}".format(name=self.name)
class AssignmentSerializer(serializers.ModelSerializer):
class Meta:
model = Assignment
fields = ('id', 'name', 'image')
class AssignmentView(viewsets.ModelViewSet):
queryset = Assignment.objects.all()
serializer_class = AssignmentSerializer
router = routers.DefaultRouter()
router.register('assignments', views.AssignmentView),
urlpatterns = [
path('', include(router.urls)),
]
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('example.urls')),
]
//Settings
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
When I click an image in the Rest API I get en error! {"detail":"Not found."}
You can edit your serializer, add a custom method that returns the URL from the file field
class AssignmentSerializer(serializers.ModelSerializer):
class Meta:
model = Assignment
fields = ('id', 'name', 'image', 'url')
url = serializers.SerializerMethodField(source='get_url')
def get_url(self, assignment):
return assignment.file.url
I have tried with your code in my local PC, there is only one error that I get, and it is:
Field name `image` is not valid for model `Assignment`.
which arose from your serializer. You declare the field name file in your model and tried to get a field named image. Though you can change the original field name in your serializer by following way:
image = serializers.FileField(source='file')
And thus your serializer class will be as following:
class AssignmentSerializer(serializers.ModelSerializer):
image = serializers.FileField(source='file')
class Meta:
model = Assignment
fields = ('id', 'name', 'image')
Otherwise, just keep the field name as same as the model.
I've tried with your code and I found both file and image in my directory and also can browse those files.
If you still face any problem, try with the following lines of code in your models.py
from django.conf import settings
from django.core.files.storage import FileSystemStorage
image_storage = FileSystemStorage(
# Physical file location ROOT
location='{0}/'.format(settings.MEDIA_ROOT),
# Url for file
base_url='{0}/'.format(settings.MEDIA_URL),
)
def file_directory_path(instance, filename):
return 'files/{0}'.format(filename)
class Assignment(models.Model):
name = models.CharField(max_length=100)
file = models.FileField(upload_to=file_directory_path, storage=image_storage, blank=False,null=False)
class Meta:
verbose_name_plural = 'Assignments'
def __str__(self):
return "{name}".format(name=self.name)
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 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.