Unable to update image in Django Rest Framework , gives error " The submitted data was not a file. Check the encoding type on the form." - serialization

i was able to post the image but don't know why i am not able to update the image , i am using default form provided by DRF
i did not find any solutions yet for this one
i am uploading image via cloudinary api
the error
{
"image_1": [
"The submitted data was not a file. Check the encoding type on the form."
],
"image_2": [
"The submitted data was not a file. Check the encoding type on the form."
]
}
Model:
class MyDetail(models.Model):
name=models.CharField(max_length=50, null=True, blank=True)
image_1=models.URLField(null=True, blank=True)
image_2=models.URLField(null=True, blank=True)
Views:
class SingleDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset=MyDetail.objects.all()
serializer_class = SingleDetailSerializer
lookup_field='id'
def update(self, request, *args, **kwargs):
instance = self.get_object()
image_1 = request.FILES.get('image_1',None)
image_2 = request.FILES.get('image_2',None)
if image_1:
uploaded_image_1 = cloudinary.uploader.upload(image_1)
request.data['image_1'] = uploaded_image_1['secure_url']
if image_2:
uploaded_image_2 = cloudinary.uploader.upload(image_2)
request.data['image_2'] = uploaded_image_2['secure_url']
serializer = self.get_serializer(instance, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
return Response({"message": "Updated successfully.", "data": serializer.data})
def perform_update(self, serializer):
serializer.save()
serializer:
class SingleDetailSerializer(serializers.ModelSerializer):
image_1 = serializers.ImageField(max_length=None,use_url=True,required=False)
image_2 = serializers.ImageField (max_length=None,use_url=True,required=False)
class Meta:
model = MyDetail
fields = ['id','name','image_1','image_2']
def to_representation(self, instance):
representation = super().to_representation(instance)
representation['image_1'] = instance.image_1
representation['image_2'] = instance.image_2
return representation
my DRF Form image
i even tried from my react form but it was still not working , i was not able to find solutions online and i don't know why it is not able to recognize the file though i uploaded the file. Help me to fix this

Here is how i solved it
Instead of updating image through views , i updated image directly serializer where i modified the url field to image field to upload the image
views :
class SingleDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset=MyDetail.objects.all()
serializer_class = SingleDetailSerializer
lookup_field='id'
serializer :
class SingleDetailSerializer(serializers.ModelSerializer):
image_1 = serializers.ImageField(use_url=True, required=False)
image_2 = serializers.ImageField(use_url=True, required=False)
class Meta:
model = MyDetail
fields = ['id','name','image_1','image_2']
def to_representation(self, instance):
representation = super().to_representation(instance)
representation['image_1'] = instance.image_1
representation['image_2'] = instance.image_2
return representation
def update(self, instance, validated_data):
image_1 = validated_data.get('image_1',None)
image_2 = validated_data.get('image_2',None)
if image_1:
uploaded_image_1 = cloudinary.uploader.upload(image_1)
validated_data['image_1'] = uploaded_image_1['secure_url']
if image_2:
uploaded_image_2 = cloudinary.uploader.upload(image_2)
validated_data['image_2'] = uploaded_image_2['secure_url']
instance = super().update(instance, validated_data)
return instance

Related

Is passing additional data to save() the right way to store the results of logic triggered by a POST?

I've got a django rest framework view that passes data from a POST to another function that returns a result.
I want to store that result in the database so that you can GET a previous result or results.
Currently, I've got the "nornir_result" defined in my serializer and model.
The serializer field is set to required=False (because the result isn't known when the payload is passed in).
Then, to populate/save this information for later GETs, save(nornir_result=nornir_result) gets called during the POST.
Is my approach correct? Passing additional data to save() to store the results of logic triggered by a POST so it can be viewed later?
# views.py
class F5AuditList(APIView):
def get(self, request, format=None):
audits = F5Audit.objects.all()
serializer = F5AuditSerializer(audits, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = F5AuditSerializer(data=request.data)
if serializer.is_valid():
nornir_result = audit.django_result(request.data)
serializer.save(
audit_id=create_unique_number(), nornir_result=nornir_result
)
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
# serializers.py
class F5AuditSerializer(serializers.Serializer):
audit_id = serializers.IntegerField(required=False)
created = serializers.DateTimeField(required=False)
devices = serializers.DictField()
audit_type = serializers.CharField(
max_length=None, style={"base_template": "textarea.html"}, required=False
)
nornir_result = serializers.CharField(
max_length=None, style={"base_template": "textarea.html"}, required=False
)
# models.py
class F5Audit(Document):
audit_id = fields.IntField(primary_key=True)
created = fields.DateTimeField(default=datetime.datetime.utcnow)
audit_type = fields.StringField(max_length=100, default="type of f5 audit")
devices = fields.DictField()
nornir_result = fields.StringField(max_length=1000)

DRF update view with many to many field

Am trying to write update view,but got an error please help me to find the problem,thanks :)
At first I have many to many field in my model.It is my model
class Portfolio(models.Model):
name = models.CharField(max_length=50, unique=True, blank=False, null=True)
market = models.ForeignKey(Market, on_delete=models.DO_NOTHING, related_name='market')
investor = models.ForeignKey('accounts.User', on_delete=models.DO_NOTHING, related_name='investor')
assets = models.ManyToManyField(Assets, related_name='assets')
def __str__(self):
return self.name
After that I have a serializer for my view:
class PortfolioSerializer(serializers.ModelSerializer):
class Meta:
model = Portfolio
fields = ['name', 'market', 'investor', 'assets']
And it's my view:
class PortfolioUpdateView(APIView):
serializer_class = PortfolioSerializer
def put(self, request, *args,):
data = request.data
portfo = Portfolio.objects.get(id=id)
print(portfo)
serilize = self.serializer_class(instance=request.user, data=request.POST)
if serilize.is_valid():
name = serilize.data['name']
market = Market.objects.get(pk=int(request.POST.get('market', '')))
assets = Assets.objects.get(pk=int(request.POST.get('assets', '')))
Portfolio.objects.update(name=name, market=market,
assets=assets,
)
return portfo
else:
pass
and at the end it is my error:
TypeError at /market/update/1
put() got an unexpected keyword argument 'id'
I found the answer by my self,because I needed to use id for get obj so I used request.data that is body's data of object include obj's id and added query-set method for getting the class objs
class PortfolioUpdateView(viewsets.ModelViewSet):
serializer_class = PortfolioSerializer
def get_queryset(self):
portfolio = Portfolio.objects.all()
return portfolio
def put(self, request, *args, **kwargs):
data = Portfolio.objects.get(id=request.data['id'])
update_portfolio = Portfolio.objects.update(name=data['name']
, market=Market.objects.get(pk=int(request.POST.get('market', ''))))
update_portfolio.save()
for asset in data['assets']:
asset_obj = Assets.objects.update(asset_name=asset['asset_name'])
update_portfolio.assets.add(asset_obj)
serializer = PortfolioSerializer(update_portfolio)
return Response(serializer.data)
And this is the URL
router.register("update", PortfolioUpdateView, basename="update")

How to save formwizard POST request to a database

How do I save the form_data to a database?
forms.py
class ContactWizard(SessionWizardView):
template_name ='fitness/general.html'
def done(self, form_list, **kwargs):
form_data = process_form_data(form_list)
return render_to_response('fitness/general2.html', {'form_data': form_data})
def process_form_data(form_list):
form_data = [form.cleaned_data for form in form_list]
return form_data
I have encountered this problem before while doing an online resume builder
def done(self, form_list , **kwargs):
form_data = [
form for form in form_list
]
first_form = form_data[0]
first_instance = first_form.save(commit = False)
#TODO: add other stuff before saving
first_instance.save()
I believe this code may help you. It does nothing extra, as form_data contains all the forms, you can get them by indexing. Of course you can use loop but usually different forms may require different procedures.

How to set permissions for a POST request in ModelViewSet's

How can I write my own permission class for POST requests when using ModelViewSet?
I already tried to write my own permission_classe with no success. Even if my permission class is returning false it is still granting access to the post request
models.py
class Building(models.Model, HitCountMixin):
user = models.ForeignKey(User, on_delete=models.CASCADE) limit_choices_to=Q(country=2921044) | Q(country=798544), on_delete=models.SET_NULL) #<------------ Eltern Element
name = models.CharField(max_length=200, null=True, blank=True)
description = models.TextField(max_length=2000,null=True, blank=True)
facilities = models.TextField(max_length=2000, null=True, blank=True)
...
views.py
class BuildingImageViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows groups to be viewed or edited.
"""
serializer_class = BuildingImageSerializer
permission_classes = (permissions.IsAuthenticated, IsOwner,)
def get_queryset(self):
if self.request.user.is_authenticated:
return BuildingImage.objects.filter(building__user=self.request.user)
return None
permissions.py
class IsOwner(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
print("TEST")
return False
urls.py
router = routers.DefaultRouter()
router.register(r'buildingimages', myrest_views.BuildingImageViewSet, base_name="buildingimage")
If I I try to upload an image it is working, Why?
My IsOwner permission class is evaluated because I can see the print line with "TEST" in the console.
MY SOLUTION:
def has_permission(self, request, view):
if view.action == 'create':
building_url = request.POST.get('building')
building_path = urlparse(building_url).path
building_id = resolve(building_path).kwargs['pk']
building = Building.objects.get(id=building_id)
return building.user == request.user
return True
Pass list of classes, you used has_object_permission(), You need to write code inside has_permission() method.
permission_classes = [<class 'rest_framework.permissions.AllowAny'>]
you have to pass class that derive BasePermission class
permission.py
from rest_framework import permissions
class IsOwner(permissions.BasePermission):
def has_permission(self, request, view):
if <CONDITION>:
return True
else:
return False

Django comments app, getting content type

I am trying to create a comments application to use it everywhere where I need it, so I geuss I have to use ContentType to attach comments to different models of my project.
so here:
my model:
class Comment(models.Model):
user = models.ForeignKey(User, blank=True, null=True)
text = models.TextField((u'Текст комментария'))
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
my view:
def add_comment(request):
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
new_comment = Comment()
new_comment.text = request.POST['text']
new_comment.content_type = ???
new_comment.object_id = request.POST['object_id']
new_comment.user = request.user
new_comment.save()
return HttpResponseRedirect(request.META['HTTP_REFERER'])
else: ...
How can I get a content type of the current model I am working with?
I have app NEWS and model Post in it, so I want to comments my Posts.
I know I can use ContentType.objects.get(app_label="news", model="post"), but I am getting exact value, so in that way my comment app will not be multipurpose.
P.S. sorry for bad English.
Check django.contrib.comments.forms.CommentForm.get_comment_create_data: It returns a mapping to be used to create an unsaved comment instance:
return dict(
content_type = ContentType.objects.get_for_model(self.target_object),
object_pk = force_unicode(self.target_object._get_pk_val()),
user_name = self.cleaned_data["name"],
user_email = self.cleaned_data["email"],
user_url = self.cleaned_data["url"],
comment = self.cleaned_data["comment"],
submit_date = datetime.datetime.now(),
site_id = settings.SITE_ID,
is_public = True,
is_removed = False,
)
So I guess that the line your are looking for is:
content_type = ContentType.objects.get_for_model(self.target_object),
Remenber, self is the form instance, and self.target_object() returns the instance that the current comment is attached to.