Reputation: 149
I am facing a problem uploading the user profile picture. Now when I create a user in Django admin and upload a file from the admin dashboard it works correctly and no errors. It goes to my AWS S3 bucket as it should go, but this is obviously not feasible, I have been looking for the solution for 3 to 4 days but no success or any satisfactory results. I obviously won't be providing the dashboard access to the user. The database used is MongoDB, with a database engine as djongo.
Here is my settings.py
INSTALLED_APPS = [
'profileupload',
's3direct',
'storages',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.humanize',
]
STATIC_URL = '/static/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
AWS_SECRET_ACCESS_KEY = 'MY_SPECIAL_KEY'
AWS_ACCESS_KEY_ID = 'MY_SPECIAL_KEY_NAME'
AWS_STORAGE_BUCKET_NAME = 'S3_BUCKET'
AWS_S3_FILE_OVERWRITE = False
AWS_DEFAULT_ACL = None
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
My urls.py
from django.urls import path, include
from .views import signup_form
urlpatterns = [
path('signup', signup_form, name='signup'),
]
My models.py
class profile(models.Model):
profile_id = models.AutoField(primary_key=True,unique=True)
profile_username = models.CharField(max_length=100,unique=True)
profile_name = models.CharField(max_length=150)
profile_email = models.EmailField(max_length=200)
profile_create_time = models.DateField(auto_now_add=True)
profile_dob = models.DateField()
profile_password = models.CharField(max_length=50, null=True)
profile_picture = models.ImageField(default='default.jpg', upload_to='profile_pics')
def __str__(self):
return str(self.profile_username)
My views.py
def signup_form(request):
if request.method == 'POST':
if request.POST.get('profile_username') and request.POST.get('profile_name') and request.POST.get('profile_email') and request.POST.get('profile_dob') and request.POST.get('profile_password') and request.POST.get('profile_picture'):
pr = profile()
pr.profile_username = request.POST.get('profile_username')
pr.profile_name = request.POST.get('profile_name')
pr.profile_email = request.POST.get('profile_email')
pr.profile_password = request.POST.get('profile_password')
pr.profile_dob = request.POST.get('profile_dob')
pr.profile_picture = request.POST.get('profile_picture')
try:
pr.save()
print('setProfile success')
return redirect('index.html')
except Exception as e:
return render(request, 'signup.html')
return render(request, 'signup.html')
else:
return render(request, 'signup.html')
My Sign up Form 'signup.html'
{% extends 'index.html' %}
{% block content %}
<form method='POST'>
{% csrf_token %}
<div>
<label>USERNAME</label>
<input type="text" placeholder="" name="profile_username" required/>
</div><br>
<div>
<label>NAME</label>
<input type="text" placeholder="" name="profile_name" required/>
</div><br>
<div>
<label>EMAIL</label>
<input type="email" placeholder="" name="profile_email" required/>
</div><br>
<div>
<label>Password</label>
<input type="password" placeholder="" name="profile_password" required/>
</div><br>
<div>
<label>DOB</label>
<input type="date" placeholder="" name="profile_dob" required/>
</div><br>
<div>
<label>Profile Picture</label>
<input type="file" placeholder="" name="profile_picture" required/>
</div><br>
<button type="submit">submit</button>
</form>
<a href="/">Home</a>
{% endblock content %}
Also, I want to change the name of the uploaded file, the file which is uploaded by Django admin takes the file name as it is, but when I will expose this app to the public the file name must be proper to avoid overwritten or having the same file multiple times
Upvotes: 2
Views: 18560
Reputation: 10699
As already mentioned, one way is by directly using the boto3
library.
Another way is by using the save
functionality of django-storages
(which also uses boto3
in the background).
settings.py
...
INSTALLED_APPS = [
...
"storages",
...
]
...
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
AWS_ACCESS_KEY_ID = 'my-access-key-id'
AWS_SECRET_ACCESS_KEY = 'my-secret-access-key'
# Depending on the AWS account used, you might also need to declare AWS_SESSION_TOKEN as an environment variable
AWS_STORAGE_BUCKET_NAME = 'my-bucket'
...
views.py
from io import BytesIO
from django.core.files.storage import default_storage
from rest_framework.decorators import api_view
from rest_framework.response import Response
@api_view(('GET',))
def save_file(request):
file_name = "toguro.txt"
file_content = b"I have my full 100% power now!"
file_content_io = BytesIO(file_content)
default_storage.save(file_name, file_content_io)
return Response({"message": "File successfully saved"})
settings.py
...
INSTALLED_APPS = [
...
"storages",
...
]
...
AWS_ACCESS_KEY_ID = 'my-access-key-id'
AWS_SECRET_ACCESS_KEY = 'my-secret-access-key'
# Depending on the AWS account used, you might also need to declare AWS_SESSION_TOKEN as an environment variable
...
views.py
from io import BytesIO
from rest_framework.decorators import api_view
from rest_framework.response import Response
from storages.backends.s3boto3 import S3Boto3Storage
# For a clear separation-of-concern, you should consider placing this code to its appropriate place
class MyStorage1(S3Boto3Storage):
bucket_name = 'my-bucket-1'
class MyStorage2(S3Boto3Storage):
bucket_name = 'my-bucket-2'
@api_view(('GET',))
def save_file(request):
file_name_1 = "toguro.txt"
file_name_2 = "sensui.txt"
file_content_1 = b"I have my full 100% power now!"
file_content_2 = b"I will release the S-Class demons!"
file_content_io_1 = BytesIO(file_content_1)
file_content_io_2 = BytesIO(file_content_2)
storage1 = MyStorage1()
storage2 = MyStorage2()
storage1.save(file_name_1, file_content_io_1)
storage2.save(file_name_2, file_content_io_2)
return Response({"message": "File successfully saved"})
Upvotes: 6
Reputation: 149
Okay, so I got some notion of applying the wrong access keys and taking wrong inputs in views.py
First of all taking correct inputs in views.py
pr.profile_picture = request.POST.get('profile_picture')
instead of using the above, the following would help:
pr.profile_picture = request.FILES["profile_picture"]
also, when I use the above with single inverted commas it won't work, so keep that in mind.
Uploading the file to S3
Now there will be some other ways to do this but changing the name of the file at the same time.
I made another file specially to handle images and changing the name of the file.
import boto3
session = boto3.Session(
aws_access_key_id= 'secret sauce',
aws_secret_access_key = 'secret sauce'
)
class image():
def UploadImage(name,image):
filename = name+'_picture.jpg'
imagedata = image
s3 = boto3.resource('s3')
try:
object = s3.Object('bucket sauce', filename)
object.put(ACL='public-read',Body=imagedata,Key=filename)
return True
except Exception as e:
return e
above method is called in views.py
ret = image.UploadImage(pr.profile_username,pr.profile_picture)
put it in the try block to avoid errors.
Upvotes: 3