I have a model with image compression enabled
class Course(models.Model):
def upload_location(instance,filename):
return "course_images/%s/%s"%(instance.course_name,filename)
subject = models.ForeignKey(Subject,on_delete=models.CASCADE,null=True,default=None)
course_name = models.CharField(max_length=300,default=None)
author = models.ForeignKey(TeacherProfile,on_delete=models.CASCADE,null=True,default=None)
course_description = models.TextField(null=True)
course_cover = models.ImageField(null=True,blank=True,upload_to=upload_location,default="course_images/default.png")
created_at = models.DateTimeField(default=now)
price = models.IntegerField(default=0,null=True,blank=True)
duration = models.CharField(max_length=20, null=True, blank=True)
is_enrolled = models.BooleanField(default=False)
def save(self, *args, **kwargs):
new_image = compress(self.course_cover)
self.course_cover = new_image
super().save(*args, **kwargs)
def __str__(self):
return self.course_name
When I update this model without sending an image from the frontend folder structure gets replicated inside the existing folder. ex:- Course_images/Science/science.jpg is the initial folder structure when creating an object with this model. But after updating the model without the image I can see another Course_images/Science/science.jpg inside Course_images. This starts to happen after adding the compression method. Without overriding the save method this works fine. Also if I change the image folder replication is not happening.
View of the updatecourse
class UpdateCourse(RetrieveUpdateAPIView):
queryset = Course.objects.all()
serializer_class = CourseCreateSerializer
permission_classes = [IsAuthenticated]
Can anyone state a reason for this scenario. Thank you!
def save(self, *args, **kwargs):
if self.course_cover:
im = Image.open(self.course_cover)
im = im.convert('RGB')
# create a BytesIO object
im_io = BytesIO()
# save image to BytesIO object
im.save(im_io, 'JPEG', quality=10)
temp_name = os.path.split(self.course_cover.name)[1]
self.course_cover.save(temp_name, content=ContentFile(im_io.getvalue()), save=False)
super().save(*args, **kwargs)
I updated save method as above. Problem Solved!
Related
I am scheduling a medical camp at some destination. After reaching the destination i want to upload picture of that destination of mapped destination.
I Created two model for that
One is SiteMap
class SiteMap(models.Model):
MpId = models.IntegerField(verbose_name='MPID')
VisitingDate = models.DateField(default=timezone.now,verbose_name='Date')
Vehical = models.ForeignKey(VehicalMaster,on_delete=models.CASCADE,verbose_name='Vehical')
Doctor = models.ForeignKey(User,on_delete=models.CASCADE,verbose_name='Doctor')
District = models.ForeignKey(District,on_delete=models.CASCADE,verbose_name='District')
Block = models.ForeignKey(Block,on_delete=models.CASCADE,verbose_name='Block')
Panchayat = models.CharField(max_length=120,verbose_name="Panchayat")
Village = models.CharField(max_length=120,verbose_name='Village')
updated = models.DateTimeField(auto_now=True)
And secod one is SiteMapImage
class SiteMapImage(models.Model):
MPID = models.ForeignKey(SiteMap,on_delete=models.CASCADE,verbose_name='MPID')
SiteImage = models.ImageField(default='default.png',upload_to='sitemap/%Y/%m/%d')
Location = models.CharField(max_length=200,verbose_name='Location',null=True,blank=True)
Latitude = models.CharField(max_length=120,verbose_name='Latitude',null=True,blank=True)
Longitue = models.CharField(max_length=120,verbose_name='Longitude',null=True,blank=True)
updated = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.MPID}"
def save(self):
super().save()
siteimg = Image.open(self.SiteImage.path)
if siteimg.height>300 or siteimg.width>300:
output_size = (300,300)
siteimg.thumbnail(output_size)
siteimg.save(self.SiteImage.path)
I have created a serializer class for this. Here is the code.
class SiteMapImageSerializer(serializers.ModelSerializer):
class Meta:
model = SiteMapImage
fields = ['MPID','Location','Latitude','Longitue','SiteImage']
and this is my view.py
class SiteMapImageCreateView(generics.CreateAPIView):
lookup_field = 'SiteMap.pk'
serializer_class = SiteMapImageSerializer
def get_queryset(self):
return SiteMapImage.objects.all()
I don't know what wrong i did. but not working in broweser too.
I upload the error image tooo.
I have to save images with s3 when creating a new object for my website.
I have a code to save my images that I use in my model's save method.
When I use the django administration panel, I can easily create my object without any error.
It also works well when I send my data without adding an image.
But when I try to create my object throught my view, I get this error : Error: 'duplicate key value violates unique constraint « store_shop_pkey »' DETAIL: key (id)=(37) already exists
I think that in my serializer's create method, I try to save my object twice : once for the image and once for the rest of my object's keys.
I don't know how to resolve this issue.
It works well with PUT method.
Here is my code :
models.py :
class Shop(models.Model):
name = models.CharField(max_length=255)
category = models.ForeignKey(ShopCategory, on_delete=models.SET_NULL, null=True, blank=True)
description = models.TextField(blank=True, null=True)
path = models.CharField(max_length=255, unique=True, null=True, blank=True) # Set a null and blank = True for serializer
mustBeLogged = models.BooleanField(default=False)
deliveries = models.FloatField(validators=[MinValueValidator(0),], default=7)
message = models.TextField(null=True, blank=True)
banner = models.ImageField(null=True, blank=True)
def save(self, *args, **kwargs):
try:
"""If we want to update"""
this = Shop.objects.get(id=self.id)
if self.banner:
image_resize(self.banner, 300, 300)
if this.banner != self.banner:
this.banner.delete(save=False)
else:
this.banner.delete(save=False)
except:
"""If we want to create a shop"""
if self.banner:
image_resize(self.banner, 300, 300)
super().save(*args, **kwargs)
def delete(self):
self.banner.delete(save=False)
super().delete()
def __str__(self):
return self.name
utils.py :
def image_resize(image, width, height):
# Open the image using Pillow
img = Image.open(image)
# check if either the width or height is greater than the max
if img.width > width or img.height > height:
output_size = (width, height)
# Create a new resized “thumbnail” version of the image with Pillow
img.thumbnail(output_size)
# Find the file name of the image
img_filename = Path(image.file.name).name
# Spilt the filename on “.” to get the file extension only
img_suffix = Path(image.file.name).name.split(".")[-1]
# Use the file extension to determine the file type from the image_types dictionary
img_format = image_types[img_suffix]
# Save the resized image into the buffer, noting the correct file type
buffer = BytesIO()
img.save(buffer, format=img_format)
# Wrap the buffer in File object
file_object = File(buffer)
# Save the new resized file as usual, which will save to S3 using django-storages
image.save(img_filename, file_object)
views.py :
class ShopList(ShopListView):
"""Create shop"""
def post(self, request):
"""For admin to create shop"""
serializer = MakeShopSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors)
serializers.py :
class MakeShopSerializer(serializers.ModelSerializer):
class Meta:
model = Shop
fields = '__all__'
def create(self, validated_data):
# validated_data.pop('path')
path = validated_data["name"].replace(" ", "-").lower()
path = unidecode.unidecode(path)
unique = False
while unique == False:
if len(Shop.objects.filter(path=path)) == 0:
unique = True
else:
# Generate a random string
char = "abcdefghijklmnopqrstuvwxyz"
path += "-{}".format("".join(random.sample(char, 5)))
shop = Shop.objects.create(**validated_data, path=path)
shop.save()
return shop
def update(self, instance, validated_data):
#You will have path in validated_data
#And you may have to check if the values are null
return super(MakeShopSerializer, self).update(instance, validated_data)
Finally, this is the data that I send :
Thank you by advance for your help
The next line is possible to have a problem with primary key:
shop = Shop.objects.create(**validated_data, path=path)
you may try to put every single attribute one by one, like this:
shop = Shop.objects.create(
name=validated_data['name'],
category=validated_data['category'],
...
path=path)
If it didn't resolve your problem, please post more about your error from terminal
Your model's unique key is path, so inside create function of your serializer,
shop = Shop.objects.create(**validated_data, path=path), tries creating a new model.
Consider the second instance you trying to add has the same path as the previous instance, in that case, you get this error since path should be unique and you are trying to add another model with the same value which DBMS rejects.
One of the things you can try is,
create or update the instance. If you are fine with updating the instance when your second instance has the same path as the previous one, then use
Shop.objects.update_or_create(path=path,
defaults=validated_data)
else
try adding unique constraints if your model cannot stand unique just by using the path field.
django documentation
I have a model in Django
class File(models.Model):
NORMAL = 'normal'
AD = 'ad'
FILE_TYPE_CHOICES = (
(NORMAL, 'Normal'),
(AD, 'Ad'),
)
file_name = models.CharField(max_length=50)
file_type = models.CharField(max_length=10, default=NORMAL,
choices=FILE_TYPE_CHOICES)
file_path = models.FileField('file', upload_to='documents/%Y/')
duration = models.IntegerField()
#prompt_id = models.CharField(max_length=50)
def __unicode__(self):
return self.file_name
def save(self, *args, **kwargs):
self.prompt_id = IvrUploadAudioFile(self.file_path)
super(File, self).save(*args, **kwargs)
i have a column prompt_id in file table, but I don't want to show prompt_id field in add/edit.
i Want to insert prompt_id value, which a function IvrUploadAudioFile will return behind the scene. so i am overriding save method here.
so where to write IvrUploadAudioFile function.
how to write this situation
Django models are just normal Python classes, and you can add as many helper methods as you want to the class:
class File(models.Model):
# -- normal stuff
prompt_id = models.CharField(max_length=50, editable=False)
def ivr_upload_audio_file(self):
# do something with self.file_path
return something
def save(self, *args, **kwargs):
self.prompt_id = self.ivr_upload_audio_file()
super(File, self).save(*args, **kwargs)
As for your other question, about not showing the field - you can add editable=False to hide the field in the admin and any ModelForms - however, if you just want to hide it in the admin, but show it in other forms - you can customize your admin to not show that field:
#admin.register(File)
class FileAdmin(admin.ModelAdmin):
fields = ('file_name', 'file_path', 'file_type', 'duration',)
You can write it anywhere as long as it can be imported into your project.
from <insert_path_here> import IvrUploadAudioFile # Function written somewhere else. You need to insert the correct path here
from django.db import models
class File(models.Model):
NORMAL = 'normal'
AD = 'ad'
FILE_TYPE_CHOICES = (
(NORMAL, 'Normal'),
(AD, 'Ad'),
)
file_name = models.CharField(max_length=50)
file_type = models.CharField(max_length=10, default=NORMAL,
choices=FILE_TYPE_CHOICES)
file_path = models.FileField('file', upload_to='documents/%Y/')
duration = models.IntegerField()
#prompt_id = models.CharField(max_length=50, editable = False)
def __unicode__(self):
return self.file_name
def save(self, *args, **kwargs):
self.prompt_id = IvrUploadAudioFile(self.file_path)
super(File, self).save(*args, **kwargs)
To make sure that the prompt_id field is not editable in the admin view, set the editable = False attribute when defining the prompt_id field in the model.
If you're talking about wanting to hide the field in Django admin, you need to set it as editable=False: https://docs.djangoproject.com/en/1.7/ref/models/fields/#editable
class File(models.Model):
. . .
prompt_id = models.CharField(max_length=50, editable=False)
I'm not sure what you intend to do by passing self.file path to your IvrUploadAudioFile class, but the code, as written, will not return the primary key of that object.
I've recently started developing with django + python and everything was going very smooth until I got stuck into a problem that probably is very simple but I can not solve with my inexperience with the framework/language.
I'm receiving an JSON object through an HTTP Request which contains some data and 2 pictures. Prior to those 2 pictures I wanted to save a thumbnail from one of them but I don't see to get that task done. I can save all the data easily including the 2 images. But I can not see to find a way to generate a way an have that in the DB also, as well the folder structure that I want.
My folders should look like:
pictures
user
originals
processed
thumbnails
otherUser
originals
processed
thumbnails
My goal is: Receive 2 pictures, create a thumbnail from one of them and them save all 3 pictures in 3 separate folders and the path to the Database.
Here's how my model code looks like.
class SomeData(models.Model):
owner = models.ForeignKey('auth.User', related_name='canopeo_data')
adjustments = models.CharField(max_length=10)
latitude = GeopositionField()
longitude = GeopositionField()
notes = models.TextField(null=True, blank=True)
original_image = models.ImageField(upload_to=original_image, max_length=255, blank=True)
processed_image = models.ImageField(null=False, upload_to=processed_image, max_length=255)
thumbnail_image = models.ImageField(null=False, upload_to=thumbnail_image, max_length=255)
date_time = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('date_time',)
def save(self, *args, **kwargs):
super(SomeData, self).save(*args, **kwargs)
def original_image(self, filename):
url = "pictures/%s/originals/%s" % (self.owner.username, filename)
return url
def processed_image(self, filename):
url = "pictures/%s/processed/%s" % (self.owner.username, filename)
return url
def thumbnail_image(self, filename):
url = "pictures/%s/thumbnail/%s" % (self.owner.username, filename)
return url
Serializer code...
class SomeDataSerializer(serializers.HyperlinkedModelSerializer):
#url = serializers.HyperlinkedRelatedField(view_name='data', format='html')
owner = serializers.Field(source='owner.username')
thumbnail_image = serializers.Field(source='original_image')
class Meta:
model = SomeData
fields = ('url', 'adjustments', 'latitude', 'longitude', 'notes', 'original_image', 'processed_image',)
View code...
class SomeDataViewSet(viewsets.ModelViewSet):
queryset = SomeData.objects.all()
serializer_class = SomeDataSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def pre_save(self, obj):
obj.owner = self.request.user
I've tried many things such as easy_thumbnails, sorl_thumbnail, and some pre made methods on how to do it.. but I can't see to find a solution specific for what I've been wanting.
Thank you very much!
Looks like you have mistake in definition of the SomeDataSerializer. In model SomeData field original_image defined as ImageField, but in serialiser it's just Field, not ImageField. You should use correct field type:
class SomeDataSerializer(serializers.HyperlinkedModelSerializer):
#url = serializers.HyperlinkedRelatedField(view_name='data', format='html')
owner = serializers.Field(source='owner.username')
thumbnail_image = serializers.ImageField(source='original_image')
...
I have a model like this:
class Item(models.Model):
code = models.CharField(max_length=200, unique=True)
barcode = models.CharField(max_length=300)
desc = models.CharField('Description',max_length=500)
display_desc = models.CharField('Description',max_length=500,
blank=True, null=True)
price = models.FloatField()
discountable = models.BooleanField(blank=True, default=False)
image_path = models.CharField(max_length=300,unique=True, blank=True, null=True)
def __unicode__(self):
return self.code + ' : ' + self.desc
But unfortunately, I don't want to store the item's image in the database, instead I want to store the image path in the server in the image_path column.
So, I created a custom admin.py for this object so that I could edit/insert the object thru the Django admin module. As a result, below is the customized admin.py
class ItemAdminForm(forms.ModelForm):
file_upload = forms.FileField(required=False)
class Meta:
model = Item
def __init__(self, *args, **kwargs):
super(ItemAdminForm, self).__init__(*args,**kwargs)
#if kwargs.has_key('instance'):
# instance = kwargs['instance']
# self.initial['file_upload'] = instance.file_upload
def handle_uploaded_file(f):
destination = open('D:/Project/pensel/penselshop/static/picture', 'wb+')
for chunk in f.chunks():
destination.write(chunk)
destination.close()
return f.name
def save(self,commit=True):
name = None
extension = None
#error here! How could I get the request?
miniform = ItemAdminForm(request.POST, request.FILES)
if miniform.is_valid():
name = handle_uploaded_file(request.FILES['file_upload'])
extension = name.split('.')[1]
model = super(ItemAdminForm, self).save(commit=False)
model.image_path = '/static/picture/' + model.code + extension
if commit:
model.save()
return model
However, during processing the save() function, I noticed that there is an error in getting the request. How can I get the request so that I could retrieve the file? I noticed that the request is automatically added in views.py, but not admin.py
Django's ImageField and FileField fields don't actually store the image in the database either. All that is stored in the database is the path, which you can control yourself. The actual image file is stored on the filesystem. So I'm not sure why you are going to all this trouble...?
But to answer your question of how to get the request in the admin, you can override ModelAdmin.save_model().