How to get file name for uploading image into s3 bucket? - django

Hello I am facing an issue while adding a file in to s3 bucket....
I am sharing my model
class UserProfile(SoftDeletionModel, BaseModel):
user = models.ForeignKey(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=100, blank=True)
contact_number = models.CharField(max_length=20, blank=True)
profile_pic=models.TextField(blank=True, null=True)
Here is my serializer class
class UserprofileSerializer(ModelSerializer):
class Meta:
model = UserProfile
exclude = ['deleted_at','created_date','modified_date']
extra_kwargs = {'user': {'required': False}}
Here is my view
class UserProfileUpdateView(UpdateAPIView):
def get_queryset(self, *args, **kwargs):
queryset_list = UserProfile.objects.filter(
user=self.request.user)
return queryset_list
serializer_class = UserprofileSerializer
The issue I am facing is..... THe profile pic uploaded as a image.... But in my model it created as a textfield , for saving the s3 bucket proxy url.....
I need to get the file name for creating a path in s3 bucket ......
So how can I do that ?

You can use request.FILES['profile_pic'] here 'profile_pic' is the input field name where you are uploading.
image = request.FILES['profile_pic']
then you can use image object as Body parameter of S3.
Suppose you have written a function upload_to_s3 which uploads image to S3 using boto3.
Exclude profile_pic from serializer.
Change your code as following:
def upload_to_s3(image_object):
// upload to s3 and get url using boto3
return url
class UserProfileUpdateView(UpdateAPIView):
serializer_class = UserprofileSerializer
def perform_update(self, serializer):
image = request.FILES['profile_pic']
serializer.validated_data['profile_pic'] = upload_to_s3(image)
serializer.save()
Convert profile_pic field type to URLField()

Related

Django Rest Framework - overriding save in backend is not creating custom id

I am working on a project. I have Django for my backend and Vue for my front end. When using the templates and saving in the Django project I have no issues.
However, when I POST to my projects API the following save from my modal is not being created.
models.py
class DevProjects(models.Model):
PROJECT_TYPE = [
('New Application', 'New Application'),
('Update Application', 'Update Application'),
('Bug Fixes', 'Bug Fixes')
]
PROJECT_STATUS = [
('New', 'New'),
('In Progress', 'In Progress'),
('Complete', 'Complete'),
]
project_id = models.CharField(max_length=15, editable=False, unique=True)
project_title = models.CharField(max_length=100)
project_desc = models.CharField(max_length=500)
project_category = models.CharField(max_length=25, choices=PROJECT_TYPE, null=True, blank=True)
project_status = models.CharField(max_length=25, choices=PROJECT_STATUS, default='New')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateField(auto_now=True)
created_by = models.ForeignKey(User, related_name='projects', on_delete=models.CASCADE)
def save(self, *args, **kwargs):
super(DevProjects, self).save(**kwargs)
self.project_id = 'PROJ-' + str(self.id)
super(DevProjects, self).save(**kwargs)
def __str__(self):
return self.project_title
I have the project_id being created on save which gets the original ID but just adds 'PROJ-' in front. Whenever I submit the form from my frontend, that save definition is not being called, thus not creating the project_id.
Project ID is what I use to send a GET request to get the projects.
serailizers.py
class DevProjectSerializer(serializers.ModelSerializer):
class Meta:
model = DevProjects
fields = ("project_id", "project_title", "project_desc", "project_category", "project_status")
views.py
class DevProjectViewSet(viewsets.ModelViewSet):
serializer_class = DevProjectSerializer
queryset = DevProjects.objects.all()
def perform_create(self, serializer):
serializer.save(created_by=self.request.user)
Whenever I post, I get the following error:
IntegrityError: UNIQUE constraint failed: ckcSupportWeb_devprojects.project_id
What do I need to do for the project_id to generate when POSTing from DRF? Any and all help is appreciated.
UPDATE
I can try to use the following code in my viewset:
def create(self, *args, **kwargs):
self.project_id = 'PROJ-' + str(self.id)
super(DevProjectViewSet, self).save(**kwargs)
But, I get the following error:
self.project_id = 'PROJ-' + str(self.id)
AttributeError: 'DevProjectViewSet' object has no attribute 'id'
I am truly stuck on how to handle this for API post requests.
From what I can understand, you can relax the unique constraint on the project_id and your previous code should work fine.
Since the project code is not editable, it won't be updated from a POST API call.
I was able to get rid of all of these issues with writing a simple function in utils.py that gets the latest ID created, adds 1 and then sets the new project_id.
Try with this code snippet
def save(self, *args, **kwargs)
self.project_id = 'PROJ-' + str(self.id)
super(DevProjects, self).save(**kwargs)

Detected path traversal attempt - Django/Heroku(Bucketeer)

I'm getting this error when trying to upload using FileField. I'm using Bucketeer on Heroku to upload to an AWS bucket. I've seen a few threads on this issue but haven't been able to figure it out.
The file upload view:
class UploadTicketAttachment(APIView):
permission_classes = []
parser_classes = (MultiPartParser, FormParser)
def post(self, request, format=None):
user = request.user
serializer = AttachmentSerialiazer(data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.validated_data['uploaded_by'] = user
serializer.save()
return Response(serializer.data['id'])
else:
return Response(f'{serializer.errors}, attachment upload failed')
The model:
class Attachment(models.Model):
file = models.FileField(upload_to="/ticket_attachments", blank=True, null=True)
created_on = models.CharField(max_length=20, null=True)
uploaded_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, related_name="uploaded_by")
parent_ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, null=True, related_name="attachment")
def __str__(self):
return self.file.name
For the settings/bucketeer configuration I followed which uses django-storages:
https://dev.to/heroku/properly-managing-django-media-static-files-on-heroku-o2l
I don't think the issue is on that end since I set it up the exact same way in another project and it works fine with the only difference being that the other project uses ImageField instead of FileField.
Django version is 4.0.2. Any ideas? Thanks

Easy Thumbnail with Django raising access denied error

I'm using S3Boto3Storage to save docs in my aws s3 and tried to use easy-thumbnails to generate thumbnail images, please find the code below
Model class
class ThumbnailTestModel(models.Model):
sample1 = models.FileField(
storage=S3Boto3Storage(),
help_text="Field to store the sample document of Professional",
null=True,
blank=True,
upload_to=s3_professional_sample_storage_path)
sample1_file_name = models.CharField(blank=True,null=True,max_length=1000, default=True)
View class
class ThumbnailTestModelView(mixins.CreateModelMixin, mixins.ListModelMixin,
mixins.UpdateModelMixin, viewsets.GenericViewSet):
queryset = ThumbnailTestModel.objects.all()
permission_classes = (AllowAny, )
serializer_class = ThumbnailSerializer
and the serialize
class ThumbnailSerializer(serializers.ModelSerializer):
sample1 = serializers.FileField(read_only=True, required=False, allow_null=True)
sample1_base64 = serializers.CharField(write_only=True, required=False, allow_null=True)
sample1_thumbnail = serializers.SerializerMethodField(required=False, read_only=True, allow_null=True)
class Meta:
model = ThumbnailTestModel
fields = ['id','sample1', 'sample1_file_name', 'sample1_base64', 'sample1_thumbnail']
def validate(self, validated_data):
validated_data = super(ProductProfessionalSerializer,
self).validate(validated_data)
sample1_base64 = validated_data.pop('sample1_base64', None)
if sample1_base64:
validated_data['sample1'] = ContentFile(
base64.b64decode(sample1_base64),
name=validated_data["sample1_file_name"])
def get_sample1_thumbnail(self, instance):
return AWS_URL + get_thumbnailer(instance.sample1)['avatar'].url
Here's the response I get
[{"id":5,"sample1":"https://wizcounsel-dev.s3.amazonaws.com/sample_document/None/add_team_2.png","sample1_file_name":"add_team_2.png","sample1_thumbnail":"https://wizcounsel-dev.s3.amazonaws.com/sample_document/None/add_team_2.png.150x100_q85_crop.png"}]
However accessing the generated thumbnail url returns an access denied error, all objects in the same folder are in fact public, on inspecting the AWS folder doesn't seem to have the thumbnail file
I'm super new to Django and hence the question might appear naive, Thanks
Apparently the thumbnails were getting created locally and this was the cause of the error, fixed by adding the following line to settings
THUMBNAIL_DEFAULT_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

Always getting {"detail":"Unsupported media type \"application/json\" in request."} error when I try to post data on postman

I am working on a project that requires me to upload an image. However when I am trying to upload one and posting I ma getting the above error. I have no clue what to do anymore.
I have already tried using FileUploadParser and creating class Base64ImageField too. Please Help.
models
class UserProfile(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE, default=None, null=True)
avatar = models.ImageField(upload_to='', blank=True, null=True)
code = models.CharField(max_length=8, unique=True, default=unique_rand)
emailVerified = models.NullBooleanField(null=True, default=None)
facebookId = models.CharField( null=True,unique=True, default=None,max_length=255)
googleId = models.CharField(null=True,unique=True,default=None,max_length=255)
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$')
mobile = models.CharField(validators=[phone_regex, MinLengthValidator(10)], max_length=10, null=True, default=None)
mobileVerified = models.NullBooleanField(null=True,default=None)
status = models.BooleanField(default=False)
serializers
class UserProfileSerializer(serializers.ModelSerializer):
user = UserSerializer()
avatar = Base64ImageField(required=False)
code = serializers.CharField(read_only=True)
serializers.FileField(use_url=False)
class Meta:
model = UserProfile
fields = '__all__'
extra_kwargs = {'user': {'required': False}}
def create(self, validated_data):
user_data = validated_data.pop('user')
user = User.objects.create(**user_data)
image = validated_data.pop('avatar')
upr=UserProfile.objects.create(user=user,image=image,**validated_data)
return upr
views
class UserCreate(generics.ListCreateAPIView):
serializer_class = UserProfileSerializer
user_serializer = UserSerializer
queryset = UserProfile.objects.all()
parser_classes = (FormParser,MultiPartParser)
def pre_save(self, request):
request.avatar = self.request.FILES.get('file')
def post(self, request):
print(request.data)
serializer= UserProfileSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Maybe try using "multipart form" instead of JSON as your POST payload type. For the value (I am using Insomnia), select the dropdown on the right and select "File" instead of "Text". Then upload a file.
That worked for me, not sure if it's the same problem. Hope that helps!
Here's a post that has the answer you're looking for.
Posting a File and Associated Data to a RESTful WebService preferably as JSON

Get ID of Django record after it has been created in a ModelViewSet

I want to get the ID of the record created during a Django REST Framework API call so I can use it in a bit of code I need to run as a result of the API call.
In brief I am uploading an image file using a POST call to my Photo model and I want, as a result of that POST call (and while processing it in the ViewSet), to insert the ID of the Photo record that has just been created into a record in another model (in my case called the Item model).
My question is whereabouts in the PhotoViewSet can I access the ID of the created record?
My Photo model looks like this:
class Photo(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
item = models.ForeignKey(Item, on_delete=models.CASCADE)
photo = models.ImageField(upload_to="userphotos/", height_field=None, width_field=None, max_length=100)
date_submitted = models.DateTimeField(auto_now_add=True)
The associated serializer looks like this:
class PhotoSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Photo
read_only_fields = ('user', 'photo', 'item')
And the associated ModelViewSet looks like this:
class PhotoViewSet(viewsets.ModelViewSet):
queryset = Photo.objects.all()
serializer_class = PhotoSerializer
parser_classes = (MultiPartParser, FormParser,)
def perform_create(self, serializer):
the_itemID = self.request.data['item_id']
the_userID = self.request.data['user_id']
the_user = User.objects.get(pk=the_userID)
the_item = Item.objects.get(pk=the_itemID)
serializer.save(user=the_user, item=the_item, photo=self.request.data.get('photo'))
Calling serializer.save() should return the instance that has just been create.
instance = serializer.save(...)
instance_id = instance.id
serializer.save() returns a Photo object, at which point you can access its id.