I'm doing a migration from an old site to a new one, and this includes moving a lot of logos/images/PDFs etc around. On the old site I'm exporting these using a base64 encode, and I base 64 decode them on the new site. So far, so good. Everything seemed to just work.
But, for some reason the files ended up in my MEDIA_ROOT instead of in MEDIA_ROOT/. So this means there are a few thousand files in my MEDIA_ROOT, not good.
A bit of code:
ntf = NamedTemporaryFile(delete=True)
ntf.write(base64.standard_b64decode(obj['logo']))
ntf.flush()
c.logo.save('photo-{0}.img'.format(c.id), File(ntf))
In this case, c is a Django model instance, and logo is an ImageField. The definition of the Django model c:
class C(models.Model):
def file_path(self, filename=None):
return (format(self.id).join(['c_obj', '.jpg'])).lower()
name = models.CharField(_(u'name'), max_length=150)
logo = models.ImageField(_(u'logo'), upload_to=file_path, null=True, blank=True)
Anyone who can tell me what's going wrong here?
Your file_path function is not returning a directory name. It is returning 'c_obj13.jpg' (when self.id is 13). Because of this, there is no directory (note the absence of a directory seperator).
Related
I am using the model with FileField to deal with file uploading. Now the files can be uploaded successfully. However, there is one more small improvement I want to make, which is to create folder for the user with the username.
Here is the code I've tried
class UserFiles(models.Model):
user = models.OneToOneField(User)
file = models.FileField(upload_to='files/users/user.username/%Y_%m_%d/')
this would give the folder of 'user.username' instead of 'John'(one example of username)
I have also tried other ways like files/users/%user.username/%Y_%m_%d/
,but it would not give the folder with the user name. Not sure how the syntax should be or whether this is possible.
Can you give some suggestions on this?
Thank you very much for your help and explanation.
Instead of a string try passing a function:
def generate_filename(self, filename):
url = "files/users/%s/%s" % (self.user.username, filename)
return url
class UserFiles(models.Model):
user = models.OneToOneField(User)
file = models.FileField(upload_to=generate_filename)
I Am trying to give an uploaded image a nicer path, using this code (in models.py):
def get_image_path_photos(instance, filename):
return os.path.join('photos', str(instance.someproperty), filename)
and the model
class Photo(models.Model):
someproperty = models.CharField(max_length=17, blank=False, null=False, default="something")
photo = models.ImageField(upload_to=get_image_path_photos, blank=True, null=True)
When I save this after a new insert, it saves it in the path /photos/something/ (it keeps using the default value).
When I edit it, add a photo and save it, it will save it to the correct path.
So it must have something to do that while saving the new object, it doesn't exist yet.
I tried the same with instance.id and this keeps being None as well (I read using auto increment on the id solves this, but this sounds as using the default value as well, and using the default pk/id is auto increment).
I found some simular questions, but none with the answer that solves my problem.
I thought of going to use the pre_save signal.... but somehow my guts says this isn't the right way.
The solution of my problem I found out myselve, please see my answer... A good lesson, don't use slugname definitions the same as the field name.....
Sorry about this. The problem is a bit more complicated. I use the field someproperty also in the url as a slug on the posts....
I just found out something I didn't expected.
i did my post (using django rest framework) from the url using the default value in the url... but I filled in the field with something else.
than, because I define the slugname the same as the fieldname, it overwrites anything you fill in in the field with the value from the url....
This isn't exactly what I meant to be done, but makes sence.
Probably the solution is to call the slug name not the same as the field name......
I keep this question and answer anyway, because for me it was quite a puzzle..... (might be of help to somebody)
as an addition to the answer of jpic:
I used the urls in django rest framwwork, lets say; http:\someurl\api\photos\\
and post there the photo.
posting the photo avatar_big.png using someproperty=bar:
saved the photo in photos\something\ when using the url http\someurl\api\photos\something
and saved the photo in photos\bar\ when using the url http:\someurl\api\photos\bar
the problem is (i guess, still have to check this) that the slug name I use for the url is the same as the fieldname.
this is the code I use in views.py (class based view I use in the django-rest-framework):
class PhotoBySomePropertyListOrCreateModelView(ListOrCreateModelView):
permissions = (IsAuthenticated, )
form = PhotoForm
def get_queryset(self):
someproperty=self.kwargs['someproperty']
return Photo.objects.filter(someproperty=someproperty)
and in urls.py:
url(r'^api/photos/(?P<someproperty>[\w:]+)/$', PhotoBySomePropertyListOrCreateModelView.as_view(resource=PhotoResource)),
here you see the problem, it doesn't listen to the field in 'someproperty', but to the value in the url ....
changing it in
url(r'^api/photos/(?P[\w:]+)/$', PhotoBySomePropertyListOrCreateModelView.as_view(resource=PhotoResource)),
should do the trick.... also adjust the view of course
In summary, what I'm trying to accomplish here is:
To perform transformation over uploaded files' names for organization and security's sake
Not to keep old files in storage forever
Let's say that you have a Company model which has a logo. Something like
class Company(models.Model):
name = ...
logo = models.FileField(blank=True, null=True)
Now, since I'm a bit paranoid and I don't really like the fact that uploaded files get the name given by the (potentially evil) user, I add a upload_to parameter that points to a function that's something like
def logo_getfilename(instance, filename):
extension = ... # get just the original extension from the file
return 'logos/' + str(uuid.uuid4()) + extension
Ok, so now only the view is missing!
def company_edit(request, ...):
company = ... # get the company and stuff
if request.method == 'POST':
form = CompanyAdminForm(request.POST, request.FILES, instance=company)
last_file_path = None
if not company.logo is None:
last_file_path = company.logo.path
# ^^ after calling is_valid(), file_path gets changed to
# the would-be-default-behavior
if form.is_valid():
# first we write the new file
form.save()
# now we remove the old one
os.unlink(last_file_path)
Although this is currently working, I'm not really comfortable with it because
I'm using os.unlink() instead of FieldFile.delete() which seems wrong
I'm assuming a local filesystem storage
I'm still not doing anything against naming collisions (they may still happen)
I'm disregarding multiple chunks and assuming that form.save() will deal with everything
I'm not considering transactional behavior (previous file should be deleted only after the .save() model changes are commited to the database
I feel there are some problems there I don't even know about
So to acheive these simple (and not as uncommon as that) goals, what would be your recommendations?
Instead of making a random username, why not use the primary key for the Company? When they upload a new file, just have it overwrite the existing one (which I think it will do automatically as long as you change the file name before you save). This should remove your need to do os.unlink and (maybe) stop your transactional worries.
I have a userprofile of the form
class profile():
#the next line is just an abstract
profile_images='volumes/media/root/userprofile/profile_images/'
image=models.ImageField(upload_to=profile_images)
in the directory "profile_images" there are the last 5 files the user uploaded as profile images, ie:
image_1
image_2
image_3
image_4
image_5
lets say the current profile.image is image_1. now i want to allow the user to select one of the previous images. the function i wrote to change the image to the one i received from the form looks like that:
def change_profile_image(userprofile,path_to_new_image):
f = open(path_to_new_image, 'r')
userprofile.image = ImageFile(f)
userprofile.save()
as an example the user selects image_3, and after execution of that code the forementioned directory looks like that:
image_1
image_2
image_3
image_4
image_5
volumes/media/root/userprofile/profile_images/image_3
which, of course, is not what i wanted. what i want is to just change the file associated with the ImageField of my profile instance, without Django copying any files.
any ideas how to solve that?
ok, actually it's as easy as
userprofile.image=path_to_new_image
no need to worry with opening files, deleting and rewriting them.
Theoretically, you could overwrite userprofile.image.path, but it’s not too obvious how to do that.
Here is some more information.
Programmatically saving image to Django ImageField
Django: How to replace/overwrite/update/change a file of FileField?
How can I replace/override an uploaded file?
I have created a app which will upload the file at a particular location. How can I read the file uploaded after the model is saved? When I click on the file link on change_field_page it gives page not found. I'm using Django 1.2 and django-admin for this.
Here's my models.py:
class UploadClass(models.Model):
id=models.AutoField(primary_key=True)
template_name=models.ForeignKey(sas,verbose_name=ugettext_lazy('Template Name'))
sample=models.FileField(upload_to='%Y/%B/',verbose_name=ugettext_lazy('Sample'))
status=models.IntegerField(ugettext_lazy('Status'),choices=statusChoices,default=0)
created_on=models.DateTimeField(ugettext_lazy('Created on'),auto_now_add=True)
def __unicode__(self):
return (self.template_name.name)
I'm not doing anything informs.py. How can I open the file after saving the object?
One way to do this is to create a view for the 'url' and return the file. Are there any others?
In terms of the file not being linked correctly from the admin, check your MEDIA_ROOT and your MEDIA_URL point to, ultimately, the same place. Also, can you give examples of how the %Y/%B/ is working out as folder names, please? They may not be as you expect.