Automatically Update Profile Picture in Django

Viewed 607

I have written a command which reads a csv file and creates 50+ users at once. Also, I have downloaded a bunch of avatars that I plan to assign randomly to each user. This is where the problem occurs. I have written the code below which assigns a random image as the default image to each user.

models.py

def random_image():
    directory = os.path.join(settings.BASE_DIR, 'media')
    files = os.listdir(directory)
    images = [file for file in files if os.path.isfile(os.path.join(directory, file))]
    rand = choice(images)
    return rand


class UserProfile(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    avatar = models.ImageField(upload_to='avatars', default=random_image)

    def save(self, *args, **kwargs):
        # check model is new and avatar is empty
        if self.pk is None and self.avatar is not None:
            self.avatar = random_image
        super(UserProfile, self).save(*args, **kwargs)

However, the code seems to run only when the user uploads a picture which is not what I desire. What I want is once the user is logged it, the model should check if it has an avatar and if not, to assign a random image. So, my questions are these:

  1. How can I implement this?
  2. Because the system keeps checking this after each log in, it seems to me that it is a little inefficient. To replace this strategy, what would be a better alternative?

**Edit: ProjectName/management/commands/CreateUsers.py

class Command(BaseCommand):
    help = 'Creates bulk users given a csv file.'

    def handle(self, *args, **options):

        ## Get the Groups
        TopManagement = Group.objects.get(name='TopManagement')
        Finance = Group.objects.get(name='Finance')

        ## Read the csv file
        with open(os.path.join(settings.FILES_DIR,'bulkusers.csv'), newline='') as csvfile:
            ausers = csv.reader(csvfile, delimiter = ',')
            next(ausers, None)  # skip the headers
            
            print('Creating Users...')
            
            # Create users
            for row in ausers:
                newuser = User.objects.create_user(
                    username = row[0],
                    password = row[1],
                    email = row[2],
                    is_superuser = 0,
                    is_staff = 0,
                    is_active = 1,
                    date_joined = datetime.datetime.now(tz=timezone.utc)
                )
                
                # Assign user to group
                if row[3] == 'TopManagement':
                    TopManagement.user_set.add(newuser)
                    newuser.groups.add(TopManagement)
                elif row[3] == 'Finance':
                    newuser.groups.add(Finance)

            print('Users created successfully!')
2 Answers

It seems to me that your save() override should work, i.e. it will set a random avatar on user creation. Just use Django ORM in your script, i.e. create users using User model in your script so that it calls save method.

I think that your condition is not correct though, I believe you should check that avatar is None:

if self.pk is None and self.avatar is None:
  self.avatar = random_image()

If someone faces the same problem in the future, here is a solution that worked for me.

from django.db.models.signals import post_save
from django.dispatch import receiver


def random_image():
    directory = os.path.join(settings.BASE_DIR, 'media')
    files = os.listdir(directory)
    images = [file for file in files if os.path.isfile(os.path.join(directory, file))]
    rand = choice(images)
    return rand


class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    avatar = models.ImageField(upload_to='avatars', default=random_image)

    def __str__(self):
        return self.user.username


@receiver(post_save, sender=User)
def create_or_update_user_profile(sender, instance, created, **kwargs):
    if created:
        UserProfile.objects.create(user=instance)
    instance.userprofile.save()

I do not know why the save function was not invoked but after a couple of indirect tips from @GProst I managed to change it with a receiver tag. Now, whenever I call the custom command python manage.py CreateUsers the UserProfile model is immediately activated and a random image is assigned to each user.

Related