Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django FileField storage option

I have this model:

class UserProfile(models.Model):
    #..........
    photo = models.ImageField(upload_to = get_upload_file_name,
                              storage = OverwriteStorage(),
                              blank = True, null = True,
                              height_field = 'photo_height',
                              width_field = 'photo_width')

And this is my storage function:

class OverwriteStorage(FileSystemStorage):
    def _save(self, name, content):
        self.delete(r'.*')
        return super(OverwriteStorage, self)._save(name, content)

    def get_available_name(self, name):
        return name

How can I do the following 2 things:

  1. Whenever a user uploads a file (i.e. an image), I want to delete the old one, no matter if the name is the same or not. I tried to delete anything that matches the regex above, but this is not working.

  2. If the user uploads an image called "me.jpg" I want to rename it differently, depending on the user username for example. So I will do something like return super(OverwriteStorage, self)._save(SOMETHING_ELSE_HERE, content) How to do this? can I pass an additional parameter to the OverwriteStorage funcion?

And an additional third question: I've created a ModelForm for this form. So a user can upload an image. So when someone presses 'choose file', a windows window pops up in order to browse and choose a photo. How can I only display certain files here? (eg. only .jpg and .jpeg files)

Thanks!

EDIT: the get_upload_file_name function

def get_upload_file_name(instance, filename):
    return "%s/%s/profile_photo/%s" % (instance.user.username[0].lower(), instance.user.username, filename)

EDIT2: I have included my models.py

import datetime
import os
import urllib2, urlparse
import re

from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.utils.translation import ugettext_lazy as _
from imagekit.models import ImageSpecField
from imagekit.processors import ResizeToFill
from django.core.files.storage import FileSystemStorage
from django.contrib.staticfiles import finders
from django.conf import settings
from django.core.files.base import ContentFile
from django.forms import widgets

now = datetime.datetime.now()

def get_upload_file_name(instance, filename):
    now = datetime.datetime.now()

    file_name = str(now.year)  + '_' + \
                str(now.month) + '_' + \
                str(now.day)   + '_' + \
                str(now.hour)  + '_' + \
                str(now.minute)+ '_' + \
                str(now.second)+ '.' + \
                filename.split('.')[-1]

    return "%s/%s/profile_photo/%s" % (instance.user.username[0].lower(),
                                       instance.user.username,
                                       file_name)

class OverwriteStorage(FileSystemStorage):
    def _save(self, name, content):
        self.delete(name)
        return super(OverwriteStorage, self)._save(name, content)

class UserProfileManager(models.Manager):

    def create_user_profile(self, user):
        user_profile = self.create(user = user)
        return user_profile

class UserProfile(models.Model):

    ### it is now.year - 13 because legitimate persons on this website should be over 14 years old
    YEARS = tuple(
                  zip([format(x,'04d') for x in range(now.year-120, now.year-13)],
                      [format(x,'04d') for x in range(now.year-120, now.year-13)]
                      )
                  )
    MONTHS = (
              ('January','January'),('February','February'),('March','March'),('April','April'),
              ('May','May'), ('June','June'),('July','July'),('August','August'),
              ('September','September'),('October','October'),('November','November'), ('December', 'December')

             )
    GENDERS = (('M', 'Male'), ('F', 'Female'))

    user = models.OneToOneField(User, related_name = 'MoreAboutUser', unique=True, verbose_name=_('user'))
    year_of_birth = models.CharField(max_length=10, blank = True,  null = True, choices=YEARS)
    month_of_birth = models.CharField(max_length=10, blank = True,  null = True, choices=MONTHS)
    gender = models.CharField(max_length=1, blank = True,  null = True, choices=GENDERS)
    photo = models.ImageField(upload_to = get_upload_file_name,
                              blank = True, null = True,
                              height_field = 'photo_height',
                              width_field = 'photo_width',
                              #widget = widgets.FileInput(attrs={'accept': 'image/gif,image/png,image/jpeg'})
                              )
    photo_height = models.PositiveIntegerField(blank = True, default = 0)
    photo_width = models.PositiveIntegerField(blank = True, default = 0)
    creation_time = models.DateTimeField(auto_now_add = True, auto_now = False)
    update_time = models.DateTimeField(auto_now_add = False, auto_now = True)

    class Meta:
            verbose_name = _('user profile')
            verbose_name_plural = _('user profiles')

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

    objects = UserProfileManager()

    def get_profile_photo_url(self):
        if self.photo and hasattr(self.photo, 'url'):
            return self.photo.url
        else:
            return '/static/images/generic_profile_photo.jpg'

def create_user_profile(sender, instance, created, **kwargs):

    if created:
        try:
            profile = UserProfile.objects.create_user_profile(user = instance)
            profile.save()
        except:
            pass

post_save.connect(create_user_profile, sender=User)
like image 469
Mihai Zamfir Avatar asked Mar 28 '14 11:03

Mihai Zamfir


People also ask

How do I store images in Django?

In Django, a default database is automatically created for you. All you have to do is add the tables called models. The upload_to tells Django to store the photo in a directory called pics under the media directory. The list_display list tells Django admin to display its contents in the admin dashboard.

What is default storage in Django?

By default, Django stores files locally, using the MEDIA_ROOT and MEDIA_URL settings. The examples below assume that you're using these defaults. However, Django provides ways to write custom file storage systems that allow you to completely customize where and how Django stores files.

What is FileField in Django?

FileField is a file-upload field. Before uploading files, one needs to specify a lot of settings so that file is securely saved and can be retrieved in a convenient manner. The default form widget for this field is a ClearableFileInput.

How do I create a media folder in Django?

MEDIA_ROOT: It is the path to the directory in which all the media files will be saved. Here, we have written(BASE_DIR, "media") which means that Django will create a folder named "media" under the base directory and all the media files will be saved in the "media" folder.


1 Answers

The storage API doesn't know anything about your model, so it cannot take other fields' values in consideration — it doesn't know the name of the old file stored in that field, nor it knows which user owns the model record.

You seem on the right track with providing an upload_to = get_upload_file_name option to your ImageField; the get_upload_file_name function (which you haven't posted in your question) will be able to construct the user-based filename for the image, as it gets a reference to the model instance, so it knows the user who owns the model instance.

As for the deletion of the old file, you will likely need to implement that in your model's save() method. It is too late at that point to find the old filename from your existing model instance, as its photo field will be already updated with its new value; still you can retrieve a copy of the existing record from the database and delete the old file through it. Here is an example implementation:

class UserProfile(models.Model):

    ...

    def save(self, *args, **kwargs):

        # grab a copy of the old record from the database
        oldrec = type(self).objects.get(pk=self.pk)

        # save the current instance, and keep the result
        result = super(UserProfile, self).save(*args, **kwargs)

        # if the path is the same, the file is overwritten already
        # and no deletion is necessary
        if oldrec.photo.path != self.photo.path:
            oldrec.photo.delete()

        # return the result received from the parent save() method
        return result

To specify acceptable types for your file selection, you will need to provide an accept attribute to the <input> tag that will be rendered for your field; you'll need to override the widget in a custom form.

like image 65
lanzz Avatar answered Sep 30 '22 16:09

lanzz