I want to give a Admin the ability to update the image associated to a Product record. I have an edit class that allows the admin to update various elements of a record all of which are updating correctly except for the image fields. The problem is uploading image is working in Creating but not in Updating.
Model:
class Product(models.Model):
id = models.AutoField(primary_key=True, db_column='product_id')
user = models.ForeignKey(UserProfile, models.CASCADE, blank=True, null=True,db_column='user_id')
category = models.ForeignKey(Category, models.CASCADE, blank=True, null=True)
product_name = models.CharField(max_length=500, blank=True, null=True)
description = models.TextField(null=True, blank=True)
quantity = models.IntegerField(default=0, blank=True, null=True)
unit_price = models.FloatField(blank=True, null=True)
dis_price = models.FloatField(blank=True, null=True, db_column='discount_price')
available_qty = models.PositiveIntegerField(default=0,blank=True, null=True)
created_at = models.DateTimeField(default=datetime.datetime.now())
image1 = models.ImageField(db_column='product_image1', null=True, blank=True, upload_to='media/images/')
image2 = models.ImageField(db_column='product_image2', null=True, blank=True, upload_to='media/images/')
image3 = models.ImageField(db_column='product_image3', null=True, blank=True, upload_to='media/images/')
Serializer :
from .models import Product
#Products Adding Serilaizer
class ad_products(serializers.ModelSerializer):
class Meta:
model = Product
fields = ['category','product_name','description','quantity','unit_price','dis_price','image1','image2','image3']
# Product Updating Serializer
class ad_products_update(serializers.ModelSerializer):
class Meta:
model = Product
fields = ['category','product_name','description','unit_price','dis_price','image1','image2','image3']
Views :
POST
class Admin_products(CreateAPIView):
serializer_class = ad_products
#transaction.atomic
def post(self,request,token):
try:
token1 = KnoxAuthtoken.objects.get(token_key=token)
except:
return Response(status=status.HTTP_404_NOT_FOUND)
user = token1.user_id
usertable = UserProfile.objects.get(id=user)
userdata = usertable.id
if(UserProfile.objects.filter(id=userdata, is_active='True')):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid(raise_exception=True):
productcategory = serializer.validated_data['category']
productname = serializer.validated_data['product_name']
description = serializer.validated_data['description']
quantity = serializer.validated_data['quantity']
unitprice = serializer.validated_data['unit_price']
discountprice = serializer.validated_data['dis_price']
image1 = serializer.validated_data['image1']
image2 = serializer.validated_data['image2']
image3 = serializer.validated_data['image3']
if(Category.objects.filter(category_name__iexact=productcategory)):
tablecategory = Category.objects.get(category_name__iexact=productcategory)
table = Product.objects.create(product_name=productname,
description=description, quantity=quantity, unit_price=unitprice,
dis_price=discountprice,user=usertable, category=tablecategory.id, image1=image1, image2=image2, image3=image3)
return Response("Success", status=status.HTTP_200_OK)
else:
data = {'message': "Category Not Found"}
return Response(data, status=status.HTTP_400_BAD_REQUEST)
else:
data = {"message":'User is in In-Active, please Activate your account'}
return Response(data, status=status.HTTP_406_NOT_ACCEPTABLE)
Image is successfully uploaded to media folder and path is stored in Database.
PUT
class Admin_products_update(CreateAPIView):
serializer_class = ad_products_update
#transaction.atomic
def put(self,request,token,pid):
try:
token1 = KnoxAuthtoken.objects.get(token_key=token)
except:
return Response(status=status.HTTP_404_NOT_FOUND)
user = token1.user_id
usertable = UserProfile.objects.get(id=user)
userdata = usertable.id
if(UserProfile.objects.filter(id=userdata, is_active='True')):
if(Product.objects.filter(user=userdata, id=pid)):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid(raise_exception=True):
category = serializer.validated_data['category']
productname = serializer.validated_data['product_name']
description = serializer.validated_data['description']
unitprice = serializer.validated_data['unit_price']
discountprice = serializer.validated_data['dis_price']
image1 = serializer.validated_data['image1']
image2 = serializer.validated_data['image2']
image3 = serializer.validated_data['image3']
if(Category.objects.filter(category_name__iexact=category)):
cat = Category.objects.get(category_name__iexact=category)
Product.objects.filter(id=pid, user=userdata).update(
category = cat.id, product_name=productname, description=description,
unit_price=unitprice, dis_price=discountprice,image1=image1, image2=image2, image3=image3)
data = {"message":'Product Details Updated successfully'}
return Response(data, status=status.HTTP_200_OK)
else:
data = {'message': "Category Not Found"}
return Response(data, status=status.HTTP_400_BAD_REQUEST)
else:
data = {'message' : "Product Details Not Found"}
return Response(data, status=status.HTTP_404_NOT_FOUND)
else:
data = {"message":'Account is in In-Active, please Activate your account'}
return Response(data, status=status.HTTP_406_NOT_ACCEPTABLE)
When i use Update method image path is storing in DB, but Image is not storing in Media folder.
can anyone have an idea how to solve this issue
<queryset>.update(...) only adds kind of annotations to the queryset and affects only SQL query generation and does not call model.save method. But only model.save operates with model instances and calls field's save method which reaches file storage. A queryset (<model>.objects.filter().update()) cannot not do anything with file storages.
So instead of writing an update query you should instantiate model instance and save it. DRF documentation has examples of implementing serializers which save instances (as model instances, not direct DB update)
You use ModelSerializer which by default does call instance.save in update method implementation thus it is unclear how you got to your implementation. Just follow the docs and let model.save happen.
Related
models.py
#
from django.db import models
from user.models import User
from chat.models import TradeChatRoom, AuctionChatRoom
class Goods(models.Model):
class Meta:
db_table = 'Goods'
ordering = ['-created_at'] # 일단 추가해뒀습니다
seller = models.ForeignKey(User, on_delete=models.CASCADE, related_name='sell_goods')
buyer = models.ForeignKey(User, on_delete=models.CASCADE, related_name='buy_goods', null=True)
trade_room = models.ForeignKey(TradeChatRoom, on_delete=models.CASCADE)
auction_room = models.ForeignKey(AuctionChatRoom, on_delete=models.CASCADE)
title = models.CharField(max_length=256)
content = models.TextField()
category = models.CharField(max_length=32)
status = models.BooleanField(null=True)
predict_price = models.IntegerField()
start_price = models.IntegerField()
high_price = models.IntegerField(null=True)
start_date = models.DateField(null = True)
start_time = models.DateTimeField(null=True)
created_at = models.DateTimeField(auto_now_add=True)
like = models.ManyToManyField(User, related_name='like_goods', null=True)
class GoodsImage(models.Model):
class Meta:
db_table = "GoodsImage"
goods = models.ForeignKey(Goods, on_delete=models.CASCADE)
image = models.ImageField(upload_to='goods/')
serializer.py
from rest_framework import serializers
from .models import Goods,GoodsImage
class GoodImageSerializer(serializers.ModelSerializer):
image = serializers.ImageField(use_url=True)
def get_image(self, obj):
image = obj.goods_set.all()
return GoodsPostSerializer(instance=image, many = True, context = self.context)
class Meta:
model = GoodsImage
field =('image',)
class GoodsPostSerializer(serializers.ModelSerializer):
image = GoodImageSerializer(many=True, read_only = True)
class Meta:
model = Goods
fields = (
'seller', 'buyer','auction_room','title','content',
'category','status','predict_price','start_price','high_price',
'trade_room','start_date','start_time','created_at','like','image',
)
read_only_fields = ("seller",)
def create(self, validated_data):
goods = Goods.objects.create(**validated_data)
images_data = self.context['request'].FILES
for image_date in images_data.getlist('image'):
GoodsImage.objects.create(goods = goods, image = image_date)
return goods
error
images_data = self.context['request'].FILES
KeyError: 'request'
I want to save multiple images, but I keep getting an error. I don't know what to do anymore.
I searched for a method and followed it, but it seems that I am the only one who gets an error.
Please help if you know how to solve this problem.
And I want to know if it is correct to put it in a list like "image":["12.jpeg,"13.jpeg] when inserting multiple images through postman.
It's hard not being able to solve this problem. please help me if you know the answer
Change GoodImageSerializer calling this:
GoodImageSerializer(instance=images, many = True, context={'request': request})
Then change your GoodsPostSerializer's create method like this:
def get_image(self, obj):
image = obj.goods_set.all()
request = self.context['request']
return GoodsPostSerializer(instance=image, many = True, context={'request': request})
I have defined Item model in models.py exactly like below.
class Item(models.Model):
transaction_id = models.CharField(max_length=25, unique=True)
order = models.ForeignKey(Order, on_delete=models.CASCADE)
item_type = models.ForeignKey(ItemType, on_delete=models.SET_DEFAULT, default=1)
title = models.CharField(max_length=200)
description = models.TextField(null=True, max_length=3000)
seller_user_id = models.IntegerField(null=True)
buyer_user_id = models.IntegerField(null=True)
listing_id = models.IntegerField(null=True)
creation_tsz = models.DateTimeField()
price = models.DecimalField(max_digits=9, decimal_places=2)
shipping_cost = models.DecimalField(max_digits=9, decimal_places=2)
quantity = models.IntegerField()
currency_code = models.CharField(null=True, max_length=5)
product_data = JSONField(null=True)
personalization = models.TextField(max_length=1000, null=True)
I have also defined my ItemSerializer with part below.
class ItemSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(read_only=True)
transaction_id = serializers.CharField(read_only=True, max_length=25)
title = serializers.CharField(max_length=200)
description = serializers.CharField(allow_null=True, max_length=3000)
seller_user_id = serializers.IntegerField(allow_null=True, validators=[validate_positive])
buyer_user_id = serializers.IntegerField(allow_null=True, validators=[validate_positive])
listing_id = serializers.IntegerField(allow_null=True, validators=[validate_positive])
creation_tsz = serializers.DateTimeField()
price = serializers.DecimalField(label='Price', max_digits=9, decimal_places=2, validators=[validate_positive])
shipping_cost = serializers.DecimalField(max_digits=9, decimal_places=2, validators=[validate_positive])
quantity = serializers.IntegerField(read_only=True)
currency_code = serializers.CharField(allow_null=True, max_length=5)
product_data = serializers.CharField(allow_null=True)
personalization = serializers.CharField(max_length=1000, allow_null=True)
def update(self, item:Item, validated_data):
#Validating whether
if 'product_data' in validated_data:
schema_obj = item.item_type.schema
try:
print(validated_data)
jsonschema.validate(
json.loads(validated_data['product_data']),
schema=schema_obj)
except Exception as e:
raise ValidationError({'product_data':[f'Schema for product data is not valid. {str(e)}']})
for key in validated_data:
setattr(item, key, validated_data[key])
item.save()
return item
def validate_product_data(self, value):
"""
Validate whether property is json parsable
:param value:
:return:
"""
try:
cur_obj = json.loads(value)
return value
except Exception as e:
raise ValidationError("This field should be in format of JSON.")
In this problem I want frontend application(a VueJS app) to automatically show up error message dialog based on serializers label and ValidationError messages. Thus I have decided to use custom exception handler by changing settings.py like below.
....
REST_FRAMEWORK = {
....
'EXCEPTION_HANDLER': 'OrderManagement.utils.exception_handler',
....
}
....
And finally exception handler function was like below.
def exception_handler(exc, context):
response = views.exception_handler(exc, context)
if isinstance(exc, exceptions.ValidationError):
response.data['validation_meta'] = {key: {'nicename': NICE_NAME_DICT.get(key, key)} for key in exc.detail}
ser = context['view'].get_serializer_class()()
if isinstance(exc, (exceptions.NotAuthenticated)) and response:
response.status_code = status.HTTP_401_UNAUTHORIZED
if response:
response.data['errcode']=response.status_code
return response
I was able to get serializer into "ser" variable but I was not able to get all fields with "label" properties.
After gathering label and/or help_text information my goal is to produce rest response like below for my VueJS app.
{
"price":[
"Price should be a number"
],
"metadata":{
"price":{
"label":"Price",
"help_text":""
}
}
}
I have tried to get it by creating object of related serializer and use "get_fields()" method. I have also tried to gather field specific data on class properties.
How can I extract "label" properties of all fields from 'ser' variable(which is context serializer)? I guess "help_text" property would be gathered with similar methodology.
Code below worked so well. Firstly there should be instantiated an object from ItemSerializer and fields dictionary has all fields with label and help_text data.
def exception_handler(exc, context):
response = views.exception_handler(exc, context)
if isinstance(exc, exceptions.ValidationError):
ser = context['view'].get_serializer_class()
ser_obj = ser()
response.data['validation_meta'] = {}
for key in exc.detail:
if key in ser_obj.fields:
response.data['validation_meta'][key] = {'label': ser_obj.fields[key].label, 'help_text': ser_obj.fields[key].help_text}
if isinstance(exc, (exceptions.NotAuthenticated)) and response:
response.status_code = status.HTTP_401_UNAUTHORIZED
if response:
response.data['errcode']=response.status_code
return response
I have a Task model. I want to assign task to multiple users so i have taken ManytoMany relationship. So Django is creating a ManytoMany table but i want to track that which user has completed task and when. So I took intermediary model by using through='TaskComplete'. Now I can not see task_assign_to feild in form. And even i declare in modelForms and submit it gives below error.
Cannot set values on a `ManyToManyField` which specifies an intermediary model. Use audit.TaskComplete's Manager instead.
Now I want that admin selects the user from main form and into intermediary model.
I tried but can not find any solution for this. below is my code. Please guide me how to do it?
My Model:
class Task(models.Model):
task_audit_title = models.ForeignKey(MainAudit,on_delete= models.CASCADE, related_name='audit_title_for_task',verbose_name= ('Audit Title'))
task_subtask_name = models.ManyToManyField(SubTask, related_name='subtask_for_task',verbose_name= ('Subtask Title'))
task_subject = models.CharField(verbose_name= ('Task Subject'),max_length=100,blank=False)
task_description = models.CharField(verbose_name= ('Task Description'),max_length=1000,blank=True)
task_assign_to = models.ManyToManyField(User, related_name='task_assign_to', through='TaskComplete')
task_assign_by = models.ForeignKey(User,on_delete= models.CASCADE, related_name='task_crt_by')
task_deadline = models.DateTimeField(null=True,blank=True)
task_perticulars = models.ManyToManyField(Perticular, related_name='task_perticular', blank=True)
task_created_time = models.DateTimeField(default=timezone.now)
task_modified_by = models.ForeignKey(User,on_delete= models.CASCADE, related_name='task_mod_by', null=True, blank=True)
task_modified_time = models.DateTimeField(null=True,blank=True)
is_del = models.BooleanField(default=0)
class Meta:
permissions = (
("change_temp_delete_task", "Can delete temporarily"),
)
def __str__(self):
return self.task_subject
def get_absolute_url(self):
return reverse('create-task')
class TaskComplete(models.Model):
task_title = models.ForeignKey(Task,on_delete= models.CASCADE, related_name='assigned_task')
is_completed = models.BooleanField(default=0)
task_cmt_by_doer = models.CharField(verbose_name= ('Submit Comment'),max_length=100,blank=True)
completed_by = models.ForeignKey(User,on_delete= models.CASCADE, related_name = 'task_completed_by')
completed_time = models.DateTimeField(null=True,blank=True)
My View:-
class TaskCraeteView(LoginRequiredMixin,SuccessMessageMixin,CreateView):
# permission_required = 'Company.add_company'
model=Task
success_message = " Task Craeted successfully!"
reverse_lazy('create-task')
login_url = 'login'
template_name = 'create-task'
form_class = TaskCreateForm
# fields =[]
def form_valid(self,form):
form.instance.task_assign_by = self.request.user
My traceback my traceback link
My Form
class TaskCreateForm(forms.ModelForm):
class Meta:
model = Task
fields = ['task_audit_title','task_subtask_name','task_subject','task_description',
'task_assign_to','task_deadline','task_perticulars']
I can not get a clear answer after two days of searching for what must probably be one of the most common things to do with a DRF:
I have the following model:
class ProcessedStockAmounts(models.Model):
prodName = models.ForeignKey(Productlist, on_delete=models.CASCADE, blank=False, unique=False)
amount = models.CharField(unique=False, max_length=255)
time = models.ForeignKey(StockTakingTimes, on_delete=models.CASCADE, blank=False, unique=False, default=1)
def __str__(self):
return str(self.prodName)
And I am returning a JSON object via my API that looks like this:
[{'prodName': 'SV1', 'amount': '1111111', 'time' : 1}]
When I insert my prodName with a value it has no problem, but obviously my user will not know the prodName ID and only the prod name. So when I try to insert the above I get the following error:
ValueError: Cannot assign "'SV1'": "ProcessedStockAmounts.prodName" must be a "Productlist" instance.
This was the closest I got to an answer and when I do the following it actually inserts:
p = ProcessedStockAmounts(amount='33', prodName = Productlist.objects.get(productid = 'SV1'), time = StockTakingTimes.objects.get(times='06:00'))
p.save()
but giving data this way is obviously defeating the purpose.
My serializer looks like the following:
class TestSerializer(serializers.ModelSerializer):
# time = serializers.SlugRelatedField(read_only=True, slug_field='time')
prodName = serializers.CharField()
# prodName = serializers.SlugRelatedField(read_only=True, slug_field='prodName')
class Meta:
model = ProcessedStockAmounts
fields = ('prodName','amount','time')
With my view:
class InsertMultiProcessedStock(APIView):
def post(self, request, format='json'):
serializer = TestSerializer(data = request.data, many=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors)
Productlist model:
class Productlist(models.Model):
productid = models.CharField(unique=True, max_length=20) # Field name made lowercase.
proddescription = models.CharField(db_column='prodDescription', max_length=255, blank=True, null=True) # Field name made lowercase.
packaging = models.ForeignKey(Packaging, on_delete=models.CASCADE, blank=True, null=True)
unitweight = models.FloatField(db_column='unitWeight', blank=True, null=True)
def __str__(self):
return self.productid
This would have been easier if you had the related model. But the commented-out slugrelatedfield is the way you should do it, using the actual field name:
prodName = serializers.SlugRelatedField(read_only=False, slug_field='productid')
Your serializer is wrong, You must use relationship serializer.
prodName = ProductlistSerializer(many = False)
But I found Your model defintion is very confusing
Here is my model:
class Browser(models.Model):
profile_name = models.CharField(max_length=400)
browser_type = (
('fr', 'Firefox'),
('ch', 'Chrome'),
('op', 'Opera'),
('ot', 'Other'),
)
browser_name = models.CharField(choices=browser_type, max_length=2)
device_name = models.CharField(max_length=400)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Bookmark(models.Model):
browser = models.ForeignKey(Browser, on_delete=models.CASCADE, null=True, blank=True)
title = models.TextField()
url = models.TextField()
iv = models.TextField()
salt = models.TextField()
iteration = models.IntegerField(default=1500)
tags = TaggableManager()
I only want to update certain fields, so here is the modelform
class BookmarkFormEdit(ModelForm):
class Meta:
model = Browser
exclude = ('tags', 'browser_name', 'device_name', 'profile_name')
but my problem is, values are not updating as expected . Here is the view:
def bookmark_edit(request, pk=None):
if request.method == 'POST':
bookmark = Bookmark.objects.get(pk=pk)
frm = BookmarkFormEdit(request.POST, instance=bookmark)
print(request.POST.get('iteration')) // printing correct value from front-end
if frm.is_valid():
x = frm.save()
print(x.iteration) // not saving the new value !
return JsonResponse({'status': 'created'})
else:
return JsonResponse({'error': frm.errors})
return render(request, 'bookmark_edit.html', {'pk': pk})
You are incorrectly defined model in the form. You should use Bookmark model instead of Browser.
class BookmarkFormEdit(ModelForm):
class Meta:
model = Bookmark
You may need to define fields to include/exclude as you want for this model.