I've tried to research and understand on the documentation, but unable to solve my problem.
I've a ModelForm which allows me to select and upload multiple files.
However, upon saving, only 1 file is saved to my media root folder despite multiple selection.
My guess is that the filename for all files in getlist are same (since they're uploaded at the same time), it overwrites each other in a way and end up only saving 1 media. Appreciate the assistance from the community, thank you !
forms.py
class FileFieldForm(forms.ModelForm):
stuff_image = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
class Meta:
model = Thing
fields = ('title', 'description', 'quantity','stuff_image')
--
model.py
def generate_filename(instance, filename):
ext = filename.split('.')[-1]
return '' + str(int(time())) + '.' + ext
class Thing(models.Model):
title = models.CharField(max_length=255)
description = models.TextField(blank = True)
quantity = models.IntegerField(blank =True)
creation_date = models.DateTimeField(auto_now_add=True)
stuff_image = models.FileField(upload_to=generate_filename)
def __unicode__(self):
return self.title
class Meta:
ordering = ['title']
#receiver(post_delete, sender=Thing)
def stuff_post_delete_handler(sender, **kwargs):
Thing = kwargs['instance']
storage, path = Thing.stuff_image.storage, Thing.stuff_image.path
storage.delete(path)
--
view.py
def create_stuff(request):
if request.method == 'POST':
form = FileFieldForm(request.POST, request.FILES)
files = request.FILES.getlist('stuff_image')
if form.is_valid():
for f in files:
form.save()
return redirect('list-stuff')
else:
form = FileFieldForm()
return render(request, 'stuffapp/create_stuff.html', {'form': form})
If you want to have multiple images linked to Thing model, you need to create an Image model with ForeignKey of Thing model. The FileField can only relate to one file at a time.
To upload multiple images/files in django, preferabble use the package django-multiupload, see the (documentation)[https://pypi.python.org/pypi/django-multiupload] or (GitHub Page)[https://github.com/Chive/django-multiupload]. Just follow the instructions and the examples. Works flawlessly.
Related
I have a model.py in which I would like to upload multiple files. In my models I have main class and another which I'm trying to use to upload multiple file as suggested. Problem is, I get the following error in my class in views.py 'Exception Value: Unknown field(s) (file) specified for Post' and I can't get it to upload multiple files.
model.py
class Post(models.Model):
title = models.CharField(max_length=40, verbose_name="Naslov predmeta")
# a lot more field here but not important
file_1 = models.FileField(blank=True, upload_to='PN_datoteke/%Y/%m/%d/', verbose_name="Datoteka 1", validators=[validate_file_size])
file_2 = models.FileField(blank=True, upload_to='PN_datoteke/%Y/%m/%d/', verbose_name="Datoteka 2", validators=[validate_file_size])
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk':self.pk})
class File(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
file = models.FileField(blank=True, upload_to='PN_datoteke/%Y/%m/%d/', verbose_name="Datoteke", validators=[validate_file_size])
views.py
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['title', 'content_don', 'subject', 'rn_number', 'file_1', 'file_2']
def form_valid(self, form):
form.instance.author = self.request.user
#here I have a lot of code to process data which is writen in the fields
response = super(PostCreateView,self).form_valid(form)
#################################################################
##### this code below I'm trying to use to upload multiple files
obj = form.save(commit=False)
if self.request.FILES:
for f in self.request.FILES.getlist('file'):
obj = self.model.objects.create(file=f)
return response
I also tried using forms, but I can't get my scripts/querys and simmilar to work then. Is there a way to add file from class File in models to my views.py class and then use it to upload multiple fiels? Or is there a easier way?
forms.py
from django import forms
from .models import Post, File
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content_don', 'subject', 'rn_number', 'report_division', 'report_department', 'nc_type', 'number_res_people', 'res_person_1', 'res_person_2', 'res_person_3', 'res_person_4', 'urgency',
'date_days', 'cost_pn', 'cost_pn_tip', 'reklamacija', 'file_1', 'file_2', 'file_3', 'file_4']
class PostFileForm(PostForm): #extending form
file = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
class Meta(PostForm.Meta):
fields = PostForm.Meta.fields + ['file',]
html template has a script to add multiple files upload option to my field, I'm not using widgets in forms.
<script>
$(document).ready(function(){
$('#id_file_1').attr("multiple","true");
})
</script>
I am a beginner with Django and I have been enjoying it so far. I figured out how to use model formsets, but I cannot figure out how to make my form automatically use logged in User as the 'updated_by' field.
models.py
class Inventory(models.Model):
item = models.CharField(max_length=50, unique=True)
stock = models.IntegerField()
par = models.IntegerField()
date_updated = models.DateTimeField(auto_now=True)
updated_by = models.ForeignKey(User, on_delete=models.PROTECT)
def __str__(self):
return self.item
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
phone = PhoneField(blank='True', help_text='Contact Phone Number')
def __str__(self):
return f'{self.user.username} Profile'
def save(self):
super().save()
I think the problem lies in your views.py. Try getting request.user before saving the form.
i think you should have made form for Inventory if yes(let InvntoryForm) than in view.py file you have done something like this:-
if request.method == 'POST':
Inven_form=InventoryForm(data=request.POST)
if Inven_form.is_valid():
user=Inven_form.save()
#in between add this
Inven_form.updated_by=request.user.username
user.save()
I would use the 'commit=False' argument which will create a new object and assign it without saving to your database. You can then set the user attribute and call save() with no arguments.
For example, this is how I assigned the user attribute to my blog app.
in views.py
if form.is_valid():
# Create a new entry and assign to new_article.
new_article = form.save(commit=False)
# Set the new article attribute to the current user.
new_article.user = request.user
# Save to database now the object has all the required data.
new_article.save()
Here is the full code for the add_article view if this helps.
#login_required
def add_article(request):
""" Add a new article. """
if request.method != 'POST':
# No data submitted, create a blank form.
form = AddArticleForm()
else:
# POST data submitted, process data.
form = AddArticleForm(request.POST, request.FILES)
if form.is_valid():
new_article = form.save(commit=False)
new_article.author = request.user
new_article.save()
return back_to_blog_page()
context = {'form': form}
return render(request, 'add_article.html', context)
I have a form where I am supposed to upload an image, but I can't get the image to save. Everything else in the form works properly except the image.
I'm sure there are some issues with my addGame method, but I've tried it dozens of different ways with no luck.
I've gone through the documentation, but it appears I'm still doing something wrong, as the image never gets saved.
(Just as a side note: I'm using Pillow for cropping the image, and I'm not sure if I'm doing that properly either, but I just added that in recently, and since the image doesn't save I have no way of knowing whether that is implemented correctly. I'm leaving the cropping part commented out while I try to get the upload to work.)
forms.py
class GameForm(forms.ModelForm):
image = forms.ImageField()
code = forms.Textarea()
deleteGame = forms.BooleanField(required=False, widget=forms.HiddenInput())
class Meta:
model = Game
fields = ('title', 'image', 'description', 'requirements', 'code', 'deleteGame')
views.py:
#login_required
def add_game(request):
user = request.user
if request.method == 'POST':
form = GameForm(request.POST, request.FILES)
if form.is_valid():
form = form.save(commit=False)
image = request.FILES['image']
box = (200, 200, 200, 200)
cropped = image.crop(box)
form.image = cropped
form.user = request.user
form.save()
return HttpResponseRedirect('/userprofile')
else:
form = GameForm()
args = {}
args.update(csrf(request))
args['user'] = user
args['form'] = form
return render_to_response('addgame.html', args)
models.py
class Game(models.Model):
user = models.ForeignKey(User, blank=True)
title = models.CharField(max_length=256)
image = models.ImageField(upload_to='games', blank=True)
description = models.CharField(max_length=256)
requirements = models.CharField(max_length=256)
code = models.TextField()
deleteGame = models.BooleanField(default=False)
def __unicode__(self):
return self.title
My media settings look like this:
MEDIA_ROOT = 'media/'
MEDIA_URL = '/media/'
File Structure:
If I add an image via the admin portal, it saves properly, but I get an error like this in my log:
Not Found: /media/games/Screen_Shot_2015-12-29_at_1.03.05_AM.png
The enctype="multipart/form-data" is needed in the form tag in your template if you intend to send files along with the form data.
I want individual users to be able to upload their files into a single folder (so each user has their own root folder, where they can upload their own files), but I am not sure of the best way to go about implementing this.
I originally planned to use the users email as the their folder name, and all their uploads would be saved in this folder. However, from what I have gathered the only way of retrieving this information is through the request function, and I cannot manage to get an instance of request into the models.py file, and therefore cannot add it to my 'upload_to' directory.
Any other ideas of how to separate users files, or how to get an instance of request in the models file would be greatly appreciated!!
Here is my current model:
def user_directory_path(instance, filename):
return 'user_{0}/{1}'.format(instance.user.id, filename)
class UploadModel(models.Model):
user = models.OneToOneField(User)
file = models.FileField(upload_to=user_directory_path)
And it's associated error:
Exception Value: UploadModel has no user.
I don't recommend you to use user email or any other information that can be updated as folder name because you won't change folder name each time he changes his email or his username. So, use user id that is unique and unchangeable.
Here is a complete example from Django documentation, to access instance information in your models to build path with user id :
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)
In this case, it use the user id in the folder name. Of course, your can replaceFileField with ImageField.
More information in django docs : FileFields.upload_to
You could maybe separate the folder by their username? You can create a function that would create a folder using the users username like so:
def get_user_image_folder(instance, filename):
return "%s/%s" %(instance.user.username, filename)
and in your model you could easily use the upload_to and add the function that you just created to it:
class Images(models.Model):
user = models.ForeignKey(User)
image = models.ImageField(upload_to=get_user_image_folder,
verbose_name='Image', )
You don't have to use request in Models, you use instance instead.
To get this to work I implemented the solution from the docs as suggested by Louis Barranqueiro, whereby the models looks like:
# models.py
def user_directory_path(instance, filename):
return 'user_{0}/{1}'.format(instance.user.id, filename)
class Document(models.Model):
file = models.FileField(upload_to=user_directory_path)
uploaded_at = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='documents')
But crucially, I also changed my DocumentUploadView class to include a save step, so that the document saves with the user attribute (note also the initial commit=False save step, which is crucial):
# views.py
class DocumentUploadView(View):
def get(self, request):
documents_list = Document.objects.all()
return render(self.request, 'file_upload.html', {'documents': documents_list})
def post(self, request):
form = DocumentForm(self.request.POST, self.request.FILES)
if form.is_valid():
document = form.save(commit=False)
document.user = request.user
document.save()
data = {'is_valid': True, 'name': document.file.name, 'url': document.file.url}
else:
data = {'is_valid': False}
return JsonResponse(data)
Finally my forms.py looks like this:
# forms.py
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
fields = ('file',)
For anyone else in the future that stumble across this, I got access to the current user's ID by adding the user object to the model in the view.
views.py
from .models import Document
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Document(docfile=request.FILES['docfile'])
newdoc.owner = request.user
newdoc.save()
Then, in models.py you can retrieve the ID from owner created previously in view.
def user_directory_path(instance, filename):
return 'Users/user_{0}/{1}'.format(instance.owner.id, filename)
class Document(models.Model):
docfile = models.FileField(upload_to=user_directory_path)
In your views.py you must pass the instance argument like this:
def post(self, request, *args, **kwargs):
if 'uploadFile' in request.POST:
f = UploadFileForm(request.POST, request.FILES, instance=request.user.uploadmodel)
if f.is_valid():
f.save()
return HttpResponseRedirect('/sucess_url')
And your forms.py
from django import forms
from .models import UploadForm
class UploadFileForm(forms.ModelForm):
class Meta:
model = UploadForm
fields = ('file',)
I also faced the same problem. Here is how I solved it. We have to create the user referencing in the view.
Below is the code for models.py
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 Document(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
docfile = models.FileField(upload_to=user_directory_path)
date = models.DateTimeField(auto_now_add=True, blank=True)
Also update the code in views.py as below:
def upload(request):
# Handle file upload
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Document(docfile=request.FILES['docfile'],user=request.user)
newdoc.save()
latest_documents = Document.objects.all().order_by('-id')[0]
# Redirect to the document list after POST
return HttpResponseRedirect(reverse('list'))
else:
form = DocumentForm() # A empty, unbound form
# Load documents for the list page
documents = Document.objects.filter(user=request.user)
The most important one is in the line,
newdoc = Document(docfile=request.FILES['docfile'],user=request.user)
Somehow I could not get the model instance to read the user. This approach is working for me.
I'm trying to create a dynamic folder to store some csv files for each user.
I have the DemoUser and the Document. It's a 1 to N relationship.
I want to store all documents from a user on its folder, named by the user id.
However I can't figure out how to do it. I can't get the owner name.
here is my code:
models.py:
def get_upload_path(instance, filename):
now = timezone.now().strftime("%Y_%m_%d")
if(instance.owner.id is not None):
return 'graphs/documents/{0}/email/' + now +'_{1}'.format(instance.owner.id, filename)
class Document(models.Model):
docfile = models.FileField(upload_to=get_upload_path, validators=[import_document_validator])
owner = models.ForeignKey(DemoUser)
date_published = models.DateTimeField(_('date published'), default=timezone.now)
views.py:
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Document(owner=DemoUser.objects.get(id=request.user.id))
newdoc.save()
newdoc.docfile = request.FILES['docfile']
newdoc.save()
The file is uploaded, but the address is:
graphs/documents/{0}/email/2015_10_07_MOCK_DATA_2_qyt9xGe.csv
it doesn't get the instance.owner.id value. I tried to save the Document before adding the docfile to try to set a owner to the object and get the owner.id value but no success. Can you please help me?
Thank you so much!
It should be:
return 'graphs/documents/{0}/email/{1}_{2}'.format(instance.owner.id, now, filename)
A simple method I found to create dynamic directories for each user to store files was to create a new method for a model that assigns a string to the instance that represents the username. This is only good for simple cases like this where you need the current user as a string to help formatting file paths.
#models.py
def get_upload_path(instance, filename):
now = timezone.now().strftime("%Y_%m_%d")
return 'graphs/documents/{0}/email/{1}_{2}'.format(instance.owner, now, filename)
class Document(models.Model):
docfile = models.FileField(upload_to=get_upload_path)
date_published = models.DateTimeField(_('date published'), default=timezone.now)
def keep_owner(self,owner):
self.owner = owner
#views.py
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = form.save(commit=False)
newdoc.keep_owner(request.user)
newdoc.save()