I have a model for user profiles that has a post save signal that auto creates a user profile whenever a new user is created. I don't want the post save signal to create a user profile when a new superuser is created. So far I have not been able to get this to work.
Any ideas about how to fix this?
the model:
from django.db import models
from tinymce import models as tinymce_models
from accounts.models import CustomUser
from phone_field import PhoneField
from constrainedfilefield.fields import ConstrainedFileField
import os
from io import BytesIO
from django.core.files.uploadedfile import InMemoryUploadedFile
import sys
from PIL import Image
from django.db.models.signals import post_save
import PIL
class UserProfile(models.Model):
user = models.OneToOneField(CustomUser, null=True, on_delete=models.CASCADE)
preferred_name = models.CharField(null=True, blank=True, max_length= 75)
pronouns = models.CharField(null=True, blank=True, max_length= 40)
phone = PhoneField(blank=True, help_text='Contact phone number')
job_title = models.CharField(null=True, blank=True, max_length= 75)
birthdate = models.DateField(null=True, blank=True)
bio = tinymce_models.HTMLField(null=True, blank=True)
profile_image = ConstrainedFileField(
null=True,
blank=True,
upload_to='projects/employee_profiles',
content_types=['image/png', 'image/jpg', 'image/jpeg', 'image/gif'],
max_upload_size=2097152,
)
def save(self, *args, **kwargs):
super(UserProfile, self).save(*args, **kwargs)
if self.profile_image:
if os.path.exists(self.profile_image.path):
image = Image.open(self.profile_image)
outputIoStream = BytesIO()
basewidth = 100
wpercent = basewidth / image.size[0]
hsize = int(image.size[1] * wpercent)
imageTemproaryResized = image.resize((basewidth, hsize))
imageTemproaryResized.save(outputIoStream, format='PNG')
outputIoStream.seek(0)
self.profile_image = InMemoryUploadedFile(outputIoStream, 'ConstrainedFileField',
"%s.png" % self.profile_image.name.split('.')[0], 'image/png',
sys.getsizeof(outputIoStream), None)
super(UserProfile, self).save(*args, **kwargs)
def save(self, *args, **kwargs):
if self.profile_image:
super().save()
img = Image.open(self.profile_image.path)
if img.height > img.width:
# make square by cutting off equal amounts top and bottom
left = 0
right = img.width
top = (img.height - img.width) / 2
bottom = (img.height + img.width) / 2
img = img.crop((left, top, right, bottom))
# Resize the image to 50X50 resolution
if img.height > 60 or img.width > 60:
output_size = (60, 60)
img.thumbnail(output_size)
img.save(self.profile_image.path)
elif img.width > img.height:
# make square by cutting off equal amounts left and right
left = (img.width - img.height) / 2
right = (img.width + img.height) / 2
top = 0
bottom = img.height
img = img.crop((left, top, right, bottom))
# Resize the image to 50X50 resolution
if img.height > 60 or img.width > 60:
output_size = (60, 60)
img.thumbnail(output_size)
img.save(self.profile_image.path)
super(UserProfile, self).save(*args, **kwargs)
def create_user_profile(sender, instance, created, **kwargs):
if (created) and (not instance.is_superuser):
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=CustomUser)
def __str__(self):
return str(self.user)
Ok the soluation is very easy the instance in the post save means the user profile which is dont have is_superuser method on it and it always return None and this result to False.
Soluation:
Access the user from the instance and then check if he is superuser or not by change the condition to:
if created and not instance.user.is_superuser:
Related
I'm currently trying to resize and rename my images on save
The problem is that I would like to resize my images in order to make them square without stretching the images, and then change the name of the image to a random number.
Currently I'm doing that inside my model :
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(null=True, blank=True)
image = models.ImageField(default='/profile_pics/default.jpg',
null=True,
blank=True,
upload_to='profile_pics')
def __str__(self):
return f'{self.user.username} Profile'
def save(self, **kwargs):
super().save()
new_name = random.randint(1, 236325984)
img = Image.open(self.image.path)
if img.height > 300 or img.width > 300:
output_size = (200, 200)
img.thumbnail(output_size)
if img.height < 299 or img.width < 299:
output_size = (200, 200)
img.thumbnail(output_size)
img.save(self.image.path)
I also tried to put in my save function os.rename to rename :
new_name = random.randint(1, 236325984)
name = f'{new_name}.{img.format.lower()}'
os.rename(self.image.path, os.path.join(os.path.dirname(self.image.path), name))
But that's not working and giving me an error : [WinError 32]
import os
from uuid import uuid4
def path_and_rename(path):
def wrapper(instance, filename):
ext = filename.split('.')[-1]
# get filename
if instance.pk:
filename = '{}.{}'.format(instance.pk, ext)
else:
# set filename as random string
filename = '{}.{}'.format(uuid4().hex, ext)
# return the whole path to the file
return os.path.join(path, filename)
return wrapper
and add it to image field
image = models.ImageField(upload_to=path_and_rename('upload/here/'), ...)
I'm trying to get image uploads to also save as thumbnails, which works. The problem was when I did an update the custom method saved the thumbnail again in a different directory. so I modified my save function to look like this.
models.py
class Photo(models.Model):
title = models.CharField(max_length=64)
description = models.CharField(max_length=255)
created = models.DateTimeField(auto_now_add=True)
image = models.ImageField(upload_to='photos/%Y%m')
thumbnail = models.ImageField(blank=True, upload_to='thumbnails/%Y%m')
submitter = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
year = models.ForeignKey(Year, blank=True, on_delete=models.CASCADE)
people = TaggableManager(through=TaggedPeople, verbose_name='People')
tags = TaggableManager(through=TaggedGeneric, verbose_name='Tags')
def save(self, *args, **kwargs):
try:
this = Photo.objects.get(id=self.id)
if this.thumbnail != self.thumbnail:
this.thumbnail.delete(save=False)
except:
if self.thumbnail:
img = Image.open(BytesIO(self.thumbnail.read()))
if hasattr(img, '_getexif'):
exif = img._getexif()
if exif:
for tag, label in ExifTags.TAGS.items():
if label == 'Orientation':
orientation = tag
break
if orientation in exif:
if exif[orientation] == 3:
img = img.rotate(180, expand=True)
elif exif[orientation] == 6:
img = img.rotate(270, expand=True)
elif exif[orientation] == 8:
img = img.rotate(90, expand=True)
img.thumbnail((360,360), Image.ANTIALIAS)
output = BytesIO()
img.save(output, format='JPEG', quality=95)
output.seek(0)
self.thumbnail = File(output, self.thumbnail.name)
return super().save(*args, **kwargs)
def __str__(self):
return self.title
and my views
class PhotoCreateView(LoginRequiredMixin, CreateView):
model = Photo
fields = ['image', 'title', 'description', 'year', 'people', 'tags']
template_name = 'photoapp/create.html'
success_url = '/photo/?page=1'
extra_context = {'tags':GenericTag.objects.all().order_by('name'),'people':PeopleTag.objects.all().order_by('name'),}
def form_valid(self, form):
form.instance.thumbnail = self.request.FILES['image']
form.instance.submitter = self.request.user
return super().form_valid(form)
class PhotoUpdateView(LoginRequiredMixin, UpdateView):
template_name = 'photoapp/update.html'
model = Photo
fields = ['title', 'description', 'year', 'people', 'tags']
success_url = '/photo/?page=1'
So the CreateView now works fine, and I have stopped the duplicate thumbnail files, but the UpdateView does not work. How can I fix this?
I figured it out. I just had to add a save to the try section.
def save(self, *args, **kwargs):
try:
this = Photo.objects.get(id=self.id)
if this.thumbnail != self.thumbnail:
this.thumbnail.delete(save=False)
return super().save(*args, **kwargs)
except:
if self.thumbnail:
img = Image.open(BytesIO(self.thumbnail.read()))
if hasattr(img, '_getexif'):
exif = img._getexif()
if exif:
for tag, label in ExifTags.TAGS.items():
if label == 'Orientation':
orientation = tag
break
if orientation in exif:
if exif[orientation] == 3:
img = img.rotate(180, expand=True)
elif exif[orientation] == 6:
img = img.rotate(270, expand=True)
elif exif[orientation] == 8:
img = img.rotate(90, expand=True)
img.thumbnail((360,360), Image.ANTIALIAS)
output = BytesIO()
img.save(output, format='JPEG', quality=95)
output.seek(0)
self.thumbnail = File(output, self.thumbnail.name)
return super().save(*args, **kwargs)
I had a similar complaint in Django-Wagtail, where saving image.save() programmatically duplicated on the file system the imported image. Do get around this I used Python Glob to get the path, and OS to remove the original image after the save function had been loaded. It worked well enough for Wagtail, I wanted to capture the issue for others - as there isn't much on the web about this. It's frustrating, doubles your disk space usage due to this functionality if you aren't careful! I don't like that it renames the files/moves them, but it is what it is.
from django.core.management.base import BaseCommand, CommandError
from wagtail.images.models import Image
from django.core.files.images import ImageFile
import os
from os import path
import glob
#import sqlite3
class Command(BaseCommand):
help = "just a thing to "
def handle(self, *args, **options):
target_path = "/home/inmyth/inmyth/media/images/"
my_images = []
if os.path.exists(target_path):
my_images = glob.glob(target_path + "*.png")
for mj_imgs in my_images:
print(mj_imgs)
image_file = ImageFile(open(mj_imgs, 'rb'), name=mj_imgs[:-4])
img_label = mj_imgs.rfind("/") + 1
image = Image(title=mj_imgs[img_label:-4], file=image_file)
image.save()
os.remove(mj_imgs)
pass
I know there are several similar questions here, but none of them seem to resolve my issue. I am using Django-Rest-Framework.
I am creating the user-profile simultaneously with the creation of user using signals.
As my question is pretty self explanatory, this is my code
models.py
from django.contrib.auth.models import AbstractUser
from django.utils.translation import ugettext_lazy as _
from django.db import models
from django.conf import settings
from PIL import Image
GENDER_SELECTION = (
('Male', 'Male'),
('Female', 'Female'),
)
class CustomUser(AbstractUser):
username = models.CharField(max_length=100, blank=True, null=True)
email = models.EmailField(_('email address'), unique=True)
gender = models.CharField(max_length=20, choices=GENDER_SELECTION)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username', 'first_name', 'last_name', 'gender']
def __str__(self):
return self.email
class UserProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='profile')
profile_pic = models.ImageField(upload_to='profile/', default='default.png', blank=True)
def __str__(self):
return f'{self.user.first_name} Profile'
def save(self, *args, **kwargs):
super(UserProfile, self).save(*args, **kwargs)
uploaded_image = Image.open(self.profile_pic.path)
if uploaded_image.height > 300 or uploaded_image.width > 300:
output_size = (300, 300)
uploaded_image.thumbnail(output_size)
uploaded_image.save(self.profile_pic.path)
This is what I have tried
In models.py
# ...
def save(self, *args, **kwargs):
super(Profile, self).save(*args, **kwargs)
if self.profile_pic == 'default.png':
if CustomUser.gender == 'Male':
self.profile_pic = 'user_default_m.png'
return self.profile_pic
else:
self.profile_pic = 'user_default_f.png'
return self.profile_pic
else:
uploaded_image = Image.open(self.profile_pic.path)
if uploaded_image.height > 300 or uploaded_image.width > 300:
output_size = (300, 300)
uploaded_image.thumbnail(output_size)
uploaded_image.save(self.profile_pic.path)
Update - Pasting signals.py file as requested
signals.py
#receiver(post_save, sender=CustomUser)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=CustomUser)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
Here is something I noticed, you are trying to resize the uploaded image and you are trying to set a default-image in the same go. Also you are trying to do this post_save.
I am assuming you are allowing upload of image by the user when he edits his/her profile which I am inferring since you have over-ridden the save method.
I would try to avoid clubbing those two. Even though they are related to Image operations, in-essence, they are different operations.
Keep your models.py file lean and restore it to your initial code.
Now, as you are already using signals, you can try pre_save in your signals.py file. Edit your signals.py file and add the following code snippet
#receiver(pre_save, sender=UserProfile)
def set_profile_image(sender, instance, *args, **kwargs):
# Obtain the gender of the just created user
gender = CustomUser.objects.all().last().gender
# Check for the gender and assign the file
if gender == 'Male' and instance.profile_pic == 'default.png':
instance.profile_pic = 'default_m.png'
elif gender == 'Female' and instance.profile_pic == 'default.png':
instance.profile_pic = 'default_f.png'
This should work.
I have an Ourteam App that allows you to upload an image, name, title, and social media information for employees. Whenever I create an object the "default.jpg" file is deleted from the media_root.
This is my model:
from django.db import models
from cms.models.pluginmodel import CMSPlugin
from django.utils.translation import ugettext_lazy as _
from smartfields import fields
from smartfields.dependencies import FileDependency
from smartfields.processors import ImageProcessor
from django.template.defaultfilters import slugify
class Employee(CMSPlugin):
# Set Name
name = models.CharField(_('name'), max_length=48)
# Define Slug
slug = models.SlugField(max_length=40, null = False, blank = True)
# Set Title
title = models.CharField(_('title'), max_length=48)
# Set Image upload path and image properties
image_upload_path = 'ourteam/%Y/%m/%d'
image = fields.ImageField(upload_to=image_upload_path,
blank=True, default='ourteam/default.jpg',
dependencies=[
FileDependency(processor=ImageProcessor(
format='JPEG', scale={'max_width': 150, 'max_height': 150}))
])
created = models.DateTimeField(_('created'), auto_now_add=True)
email = models.EmailField(_('email'), max_length=254)
# Social Media
twitter = models.CharField(_('twitter'), max_length=24, blank=True, default='https://www.twitter.com')
linkedin = models.CharField(_('linkedin'), max_length=24,blank=True, default='https://www.linkedin.com')
facebook = models.CharField(_('facebook'), max_length=24,blank=True, default='https://www.facebook.com')
class Meta:
verbose_name = _('employee')
verbose_name_plural = _('employee')
db_table = 'employee'
ordering = ('-created',)
get_latest_by = 'created'
def __unicode__(self):
return u'%s' % self.title
def __str__(self):
return self.name
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Employee, self).save(*args, **kwargs)
def get_all_employees():
all_entries = Employee.objects.all().order_by('created')
return all_entries
def slug(sluggy):
sluggy = sluggy.replace(' ', '-').lower()
return slugify(sluggy)
You should try with that :
def user_directory_path(instance, filename):
# file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
return 'user_{0}/{1}'.format(instance.user.id, filename)
class MyModel(models.Model):
upload = models.FileField(upload_to=user_directory_path)
I'm been working on this Photo Organizer and Sharing App Part I at http://lightbird.net/dbe/photo.html. I'm trying to generate a thumbnail and when I do . I get this error.
I have Windows Vista.
IOError at /admin/photo/image/add/
(13, 'Permission denied')
Request Method: POST
Request URL: http://127.0.0.1:8000/admin/photo/image/add/
Django Version: 1.4.3
Exception Type: IOError
Exception Value: (13, 'Permission denied')
Exception Location:C:\Python26\lib\site-packages\PIL\Image.py in save, line 1399
Python Executable:C:\Python26\python.exe
Python Version: 2.6.0
Python Path:
['C:\\djcode\\mysite',
'C:\\Python26\\python26.zip',
'C:\\Python26\\DLLs',
'C:\\Python26\\lib',
'C:\\Python26\\lib\\plat-win',
'C:\\Python26\\lib\\lib-tk',
'C:\\Python26',
'C:\\Python26\\lib\\site-packages',
'C:\\Python26\\lib\\site-packages\\PIL']
Server time: Sun, 10 Feb 2013 23:49:34 +1100
My models.py is
from django.db import models
from django.contrib.auth.models import User
from django.contrib import admin
from string import join
from django.core.files import File
from os.path import join as pjoin
from tempfile import *
import os
from PIL import Image as PImage
from mysite.settings import MEDIA_ROOT
class Album(models.Model):
title = models.CharField(max_length=60)
public = models.BooleanField(default=False)
def __unicode__(self):
return self.title
class Tag(models.Model):
tag = models.CharField(max_length=50)
def __unicode__(self):
return self.tag
class Image(models.Model):
title = models.CharField(max_length=60, blank=True, null=True)
image = models.FileField(upload_to="images/")
tags = models.ManyToManyField(Tag, blank=True)
albums = models.ManyToManyField(Album, blank=True)
created = models.DateTimeField(auto_now_add=True)
rating = models.IntegerField(default=50)
width = models.IntegerField(blank=True, null=True)
height = models.IntegerField(blank=True, null=True)
user = models.ForeignKey(User, null=True, blank=True)
thumbnail2 = models.ImageField(upload_to="images/", blank=True, null=True)
def __unicode__(self):
return self.image.name
def save(self, *args, **kwargs):
"""Save image dimensions."""
super(Image, self).save(*args, **kwargs)
im = PImage.open(pjoin(MEDIA_ROOT, self.image.name))
self.width, self.height = im.size
# large thumbnail
fn, ext = os.path.splitext(self.image.name)
im.thumbnail((128,128), PImage.ANTIALIAS)
thumb_fn = fn + "-thumb2" + ext
tf2 = NamedTemporaryFile()
im.save(tf2.name, "JPEG")
self.thumbnail2.save(thumb_fn, File(open(tf2.name)), save=False)
tf2.close()
# small thumbnail
im.thumbnail((40,40), PImage.ANTIALIAS)
thumb_fn = fn + "-thumb" + ext
tf = NamedTemporaryFile()
im.save(tf.name, "JPEG")
self.thumbnail.save(thumb_fn, File(open(tf.name)), save=False)
tf.close()
super(Image, self).save(*args, ** kwargs)
def size(self):
"""Image size."""
return "%s x %s" % (self.width, self.height)
def __unicode__(self):
return self.image.name
def tags_(self):
lst = [x[1] for x in self.tags.values_list()]
return str(join(lst, ', '))
def albums_(self):
lst = [x[1] for x in self.albums.values_list()]
return str(join(lst, ', '))
def thumbnail(self):
return """<img border="0" alt="" src="/media/%s" height="40" />""" % (
(self.image.name, self.image.name))
thumbnail.allow_tags = True
class AlbumAdmin(admin.ModelAdmin):
search_fields = ["title"]
list_display = ["title"]
class TagAdmin(admin.ModelAdmin):
list_display = ["tag"]
class ImageAdmin(admin.ModelAdmin):
search_fields = ["title"]
list_display = ["__unicode__", "title", "user", "rating", "size", "tags_", "albums_","thumbnail", "created"]
list_filter = ["tags", "albums"]
def save_model(self, request, obj, form, change):
obj.user = request.user
obj.save()
The problem is here:
from django.core.files import File
from os.path import join as pjoin
from tempfile import *
class Image(models.Model):
# ...
thumbnail2 = models.ImageField(upload_to="images/", blank=True, null=True)
def save(self, *args, **kwargs):
"""Save image dimensions."""
super(Image, self).save(*args, **kwargs)
im = PImage.open(pjoin(MEDIA_ROOT, self.image.name))
self.width, self.height = im.size
# large thumbnail
fn, ext = os.path.splitext(self.image.name)
im.thumbnail((128,128), PImage.ANTIALIAS)
thumb_fn = fn + "-thumb2" + ext
tf2 = NamedTemporaryFile()
im.save(tf2.name, "JPEG")
self.thumbnail2.save(thumb_fn, File(open(tf2.name)), save=False)
tf2.close()
# small thumbnail
im.thumbnail((40,40), PImage.ANTIALIAS)
thumb_fn = fn + "-thumb" + ext
tf = NamedTemporaryFile()
im.save(tf.name, "JPEG")
self.thumbnail.save(thumb_fn, File(open(tf.name)), save=False)
tf.close()
super(Image, self).save(*args, ** kwargs)
How do I fix this error?
It looks to me like django doesn't have the permissions it needs to access your MEDIA_ROOT folder.
Have a look at your MEDIA_ROOT settings in your settings.py file. Then check the permissions on the folder (something like ls -lsa /path/to/media_root from a bash shell). Make sure the user running django as write permission to the folder.
Also, as asermax points out, make sure you have created an images directory within your MEDIA_ROOT.
Have a look at the documentation for serving static files particularly the section on serving other directories
UPDATE
Perhaps it's this issue. Try replacing im.save(tf2.name, "JPEG") with im.save(tf2, "JPEG")
set SELinux permisions
http://wiki.apache.org/httpd/13PermissionDenied
take a look to this, it may be there the solution