Easy Thumbnail with Django raising access denied error - django

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'

Related

Django REST throwing a 404 error on DetailView

I am trying to create a RetrieveUpdateDestroyAPIView in my DJango REST application but I get a 404 error when I try to visit the URL.
Here is urlpattern in my urls.py file which maps to the views.py file below
re_path(r'^snippets/(?P<username>.+)/(?P<snippet_id>\d+)/$',
views.SnippetDetailView.as_view(), name="snippet-detail"),
Here is my views.py code.
class SnippetDetailView(generics.RetrieveUpdateDestroyAPIView):
"""
Retrieve, update or delete a snippet.
"""
permission_classes = [IsOwnerOrReadOnly]
serializer_class = SnippetSerializer
lookup_url_kwarg = 'snippet_id'
def get_queryset(self):
self.user = get_object_or_404(User, username=self.kwargs['username'])
return Snippet.objects.filter(owner__username=self.user.username)
And this is my models.py code
class Snippet(models.Model):
owner = models.ForeignKey('auth.User', related_name='snippets',
on_delete=models.CASCADE)
highlighted = models.TextField() # to store representation of code
created = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100, default="", blank=True)
code = models.TextField()
linenos = models.BooleanField(default=False)
language = models.CharField(choices=LANGUAGE_CHOICES, default='python',
max_length=100)
style = models.CharField(choices=STYLE_CHOICES, default='friendly',
max_length=100)
class Meta:
ordering = ["created"]
I've been trying to understand what could be wrong with it but am relatively new and I have no idea as to why this is not working. Looked up the documentation and a few examples but I don't see any one of them having multiple named parameters in their urls.py file. I would appreciate any help a lot.

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

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()

Inheritance of permission between the related models in DRF

I have 3 models like bellow:
class CustomUser(AbstractUser):
pass
class PropertyPost(models.Model):
owner = models.ForeignKey(
get_user_model(),
related_name='posts4thisowner',
on_delete=models.CASCADE)
class Image(models.Model):
prop_post = models.ForeignKey(
PropertyPost,
related_name='images4thisproperty',
on_delete=models.CASCADE)
The idea here is that the user can post many PropertyPost and each PropertyPost can have many images.
Things are working fine. My problem is in the permission. I have set a set of permissions as follows for my PropertyPostDetail views, and no permission for the ImageDetail view:
class PropertyPostDetail(generics.RetrieveUpdateDestroyAPIView):
...
permission_classes = (
permissions.IsAuthenticatedOrReadOnly,
custompermission.IsCurrentUserOwnerOrReadOnly,
)
class ImageList(generics.ListCreateAPIView):
queryset = Image.objects.all()
serializer_class = ImageSerializer
name = 'image-list'
class ImageDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Image.objects.all()
serializer_class = ImageSerializer
name = 'image-detail'
which I meant that only the post owner can mess around with its posts.
Now, because the image model is for the PropertyPost I expected that this permission carry over to the images as well. However apparantly any user can edit any image, which is obviously a flaw.
My question is how could I set my image view to inherit permissions from its parent which is PropertyPost.
ok, so hopfully there would be easier solution but here is how I resolved it. I tried here not to create an owner for image model, because image model has PropertyPost as its parent, which has an owner. So Image should inherit that owner:
class Image(models.Model):
prop_post = models.ForeignKey(
PropertyPost,
related_name='images4thisproperty',
on_delete=models.CASCADE)
prop_post_owner=models.CharField(max_length=150,null=True,blank=True)
and this would be the serializer:
class ImageSerializer(serializers.HyperlinkedModelSerializer):
prop_post = serializers.SlugRelatedField(queryset=PropertyPost.objects.all(),
slug_field='pk')
prop_post_owner = serializers.ReadOnlyField(source='prop_post.owner.username')
class Meta:
model = Image
fields = (
'pk',
'url',
'prop_post',
'prop_post_owner'
)
this way my image model has an owner that is comming from the PropertyPost.
now at list level user can create an object and since it is not clear which object the user is going to select, so custompermisions will fail. Finally this is how I prevent non-owners from creating an image field for a property:
class ImageList(generics.ListCreateAPIView):
queryset = Image.objects.all()
serializer_class = ImageSerializer
name = 'image-list'
....
def perform_create(self, serializer):
obj=PropertyPost.objects.get(pk=int(self.request.data['prop_post']))
if self.request.user!=obj.owner:
raise ValidationError('you are not the owner')
serializer.save(prop_post_owner=self.request.user)

Deserializing SlugRelatedField on ForeignKey with Django Rest Framework

I am struggling to set a PrimaryKeyRelatedField using a SlugRelatedField.
My models.py looks like:
class Airport(models.Model):
name = models.CharField(max_length=200, blank=True)
iata = models.CharField(max_length=20, blank=True)
<other unrelated fields>
class Flight(models.Model):
flight_number = models.CharField(max_length=25, blank=False, unique=True)
origin = models.ForeignKey(Airport, related_name='origin', null=True)
destination = models.ForeignKey(Airport, related_name='destination', null=True)
scheduled_departure = models.DateTimeField(null=True)
scheduled_arrival = models.DateTimeField(null=True)
My view looks like:
class FlightList(APIView):
# List all flights, or create a new flight
queryset = Flight.objects.all()
serializer_class = FlightSerializer
def get(self, request, format=None):
flights = Flight.objects.all()
serializer = FlightSerializer(flights, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = FlightSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
print serializer.validated_data
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
If I set my serializers.py to:
class FlightSerializer(serializers.ModelSerializer):
class Meta:
model = Flight
fields = ('flight_number', 'origin', 'destination', 'scheduled_departure', 'scheduled_arrival')
From the shell I see the serializer renders to:
FlightSerializer():
flight_number = CharField(max_length=25, validators=[<UniqueValidator(queryset=Flight.objects.all())>])
origin = PrimaryKeyRelatedField(allow_null=True, queryset=Airport.objects.all(), required=False)
destination = PrimaryKeyRelatedField(allow_null=True, queryset=Airport.objects.all(), required=False)
scheduled_departure = DateTimeField(allow_null=True, required=False)
scheduled_arrival = DateTimeField(allow_null=True, required=False)
I am able to POST origin and destination pk's that match up with Airport entries in the database to create new flights. Great.
However, I actually need to post the airport code, (i.e.) LAX as the origin or destination, and have DRF figure out the appropriate instance.
I think the way to do this is using SlugRelatedField. Despite best efforts, I'm unable to set this up properly.
I am trying to set serializers.py like this:
class FlightSerializer(serializers.ModelSerializer):
origin = serializers.SlugRelatedField(
many=True,
read_only=True,
slug_field='iata',
)
class Meta:
model = Flight
fields = ('flight_number', 'origin', 'destination', 'scheduled_departure', 'scheduled_arrival')
When I pass up a valid airport code (iata) like KMS as the origin it seems to get ignored.
Is this what SlugRelatedField is supposed to be used for?
What am I doing wrong to allow my POST to send up a string, have DRF look at a field for matching db entries and store that as beautifully as it does when I simply pass up a valid PK?
Got some help from a colleague on this.
The problem was I was just misconfiguration of SlugRelatedField. To get at the airport codes, I needed to use a queryset and set the slug_field. That's all:
class FlightSerializer(serializers.ModelSerializer):
origin = serializers.SlugRelatedField(
slug_field='iata',
queryset=Airport.objects.all()
)
class Meta:
model = Flight
fields = ('flight_number', 'origin', 'destination', 'scheduled_departure', 'scheduled_arrival')

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.