How can I upload multiple files to a model field? - django

I want to upload multiple files through a ModelForm,with all files to be assigned to a file field of the Model.I have gone through the docs and I saw an example on it and I ve implemented it here but I can only get my form to pick multiple files but only one get saved and assigned to filesfield.Below are my codes
models.py
class Feed(models.Model):
user=models.ForeignKey(User,on_delete=models.CASCADE,related_name='feeds')
text=models.TextField(blank=False,max_length=500)
files = models.FileField(upload_to="files/%Y/%m/%d")
forms.py
class FeedForm(ModelForm):
class Meta:
model=Feed
fields=('text','auth','files')
widgets={"files":forms.FileInput(attrs={'id':'files','required':True,'multiple':True})}
and views.py
def post_feed(request):
form_class = FeedForm
if request.method == 'POST':
form = form_class(request.POST,request.FILES)
if form.is_valid():
feed = form.save(commit=False)
feed.user = User.objects.get(pk=1)
feed.pub_date=timezone.now()
#instance = Feed(files=request.FILES['files'])
# feed.files=request.FILES['files']
feed.save()
return redirect('home')
else:
form = form_class()
return render(request, 'post_feed.html', {'form': form,})
from django.views.generic.edit import FormView
from .forms import FeedForm
class FileFieldView(FormView):
form_class=FeedForm
template_name='post_feed.html'
'''success_url=??? #I dont know what to write here.I thought of putting this
render(request, 'post_feed.html', {'form': form,}) because I just want
to reload the page but it gave an error,so I removed it entirely.'''
def post_feed(self,request,*args,**kwargs):
form_class=self.get_form_class()
form=self.get_form(form_class)
filez=request.FILES.getlist('files')
if form.is_valid():
for f in filez:
f.save()
return self.form_valid(form)
else:
return self.form_invalid(form)
Kindly help me out,Thanks in advance.

You have to create a separate model for the files and connect them with a foreign key:
class Feed(models.Model):
user=models.ForeignKey(User, on_delete=models.CASCADE, related_name='feeds')
text=models.TextField(blank=False, max_length=500)
class FeedFile(models.Model):
file = models.FileField(upload_to="files/%Y/%m/%d")
feed = models.ForeignKey(Feed, on_delete=models.CASCADE, related_name='files')
I hope this helps.

Phew, it took me a whole day to figure out this. My goal was to assign multiple files to one instance of a class, like a Blog instance can have multiple Images. First things first, you cannot do this with one models.FileField inside a model (for example inside Blog class), because this field was not designed to save multiple files. So the solution is to create separate model for the files and connect them with One-to-Many Relationship (Foreign Key) as it was answered by #Carlos Mermingas.
Enough words, here is the code for the above situation:
# models.py
class Feed(models.Model):
user=models.ForeignKey(User, on_delete=models.CASCADE)
text=models.TextField(blank=False, max_length=500)
class FeedFile(models.Model):
file = models.FileField(upload_to="files/%Y/%m/%d")
feed = models.ForeignKey(Feed, on_delete=models.CASCADE)
# forms.py
...
from django.forms import ClearableFileInput
...
class FeedModelForm(forms.ModelForm):
class Meta:
model = Feed
fields = ['text']
class FileModelForm(forms.ModelForm):
class Meta:
model = FeedFile
fields = ['file']
widgets = {
'file': ClearableFileInput(attrs={'multiple': True}),
}
# widget is important to upload multiple files
# views.py
from .models import FeedFile
...
def create_to_feed(request):
user = request.user
if request.method == 'POST':
form = FeedModelForm(request.POST)
file_form = FileModelForm(request.POST, request.FILES)
files = request.FILES.getlist('file') #field name in model
if form.is_valid() and file_form.is_valid():
feed_instance = form.save(commit=False)
feed_instance.user = user
feed_instance.save()
for f in files:
file_instance = FeedFile(file=f, feed=feed_instance)
file_instance.save()
else:
form = FeedModelForm()
file_form = FileModelForm()
# the rest is the basic code: template_name, context, render etc.
# in your template.html <form> tag must include enctype="multipart/form-data"
Bonus: if you want to see uploaded files in admin panel, you can use InlineModelAdmin objects. Here is the code:
# admin.py of your app
from django.contrib import admin
from .models import Feed, FeedFile
class FeedFileInline(admin.TabularInline):
model = FeedFile
class FeedAdmin(admin.ModelAdmin):
inlines = [
FeedFileInline,
]
admin.site.register(Feed, FeedAdmin)
For the more details on file upload, Model Forms, how to include widget in Model Form

Would suggest using an M2M field from Feed model to FeedFile model.
Makes it all the more easier while querying for files of a particular Feed object, which i feel is also the most common usecase for Feed objects
class Feed(models.Model):
user=models.ForeignKey(User, on_delete=models.CASCADE, related_name='feeds')
text=models.TextField(blank=False, max_length=500)
files=models.ManyToManyField(FeedFile)
class FeedFile(models.Model):
file = models.FileField(upload_to="files/%Y/%m/%d")

Related

Django forms dynamic getting author as a logged in user in model forms

I'm trying to make some forms that will allow users to add some objects, delete them or edit but I've stucked with thing like author of model. Let's say we got model Shot which got field
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
Because I've created custom user model to expand user by some fields that I want, and then we creating modelForm, creating views etc. and finally got form. When we will try to submit this form, it won't add this object submited in form to db because form has no filled field author author which means this field == Null and that's why it won't add this to db. So my question is how to get it dynamic, for example when user with nick "thebestuser" will try to add this modelForm it will work and mark author as "thebestuser"? Ofc I could add to form field author, but it's the worst way in my opinion and every user would be allowed then to add object for example as a another user, let's say user with nick "anothernick" could add form as a user with "thebestuser" which is In my opinion not acceptable.
models.py
from django.db import models
from django.contrib.auth.models import User
from streamers.models import Streamer
from django.conf import settings
from django.utils import timezone
class Shot(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=70)
url = models.CharField(max_length=100)
streamer = models.ForeignKey(Streamer, on_delete=models.CASCADE)
published_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
forms.py
from django import forms
from .models import Shot
class AddShot(forms.ModelForm):
class Meta:
model = Shot
fields = [
'title',
'url',
'streamer',
]
views.py
#login_required
def add_shot(request):
form = AddShot(request.POST or None)
if form.is_valid():
form.save()
instance = form.save(commit=False)
instance.published_date = request.published_date
instance.author = request.user
instance.save()
context = {
'form': form
}
return render(request, 'shots/add_shot.html', context)
You'll need to do it in your view. When you save your form pass commit=False to your save method, add your user, then save the returned instance.
def my_view(request):
form = AddShot(request.POST)
instance = form.save(commit=False)
instance.author = request.user
instance.save()
Documented here: https://docs.djangoproject.com/en/2.1/topics/forms/modelforms/#the-save-method

Try to Upload Several Pictures, but only 1 is getting saved

I am trying to allow user to upload several pictures, but only one picture is saving to the database. This is my Model
from django.db import models
from django.contrib.auth.models import User
class File(models.Model):
files = models.FileField(upload_to='images/')
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='files')
next, the form for the upload looks like this:
from django import forms
from .models import File
class FileForm(forms.ModelForm):
class Meta:
model=File
fields=('files',)
widgets={'files':forms.FileInput(attrs={'id':'files','required':True,'multiple':True})}
finally, the view 'upload pics' is the following:
def upload_pics(request, user_id):
if request.method == "POST":
form = FileForm(request.POST, request.FILES)
files = request.FILES.getlist('file_field')
if form.is_valid():
pics = form.save(commit=False)
pics.user = request.user
pics.files = request.FILES[files]
return redirect("groups:index")
else:
form = FileForm()
render(request, 'accounts/account_form.html', {'form':form})
return render(request, 'accounts/account_form.html', {'form':form})
the problem is that only 1 picture is being saved. any help is really appreciated!
Your code looks fine, but it seems like what you are looking for is Django Formsets. It will allow you to upload multiple images at once. Also take a look at the answer to this question: how to upload multiple images to a blog post in django

Django inline forms

I have to models, an Article and a Photo. There is a many2many relation between them. I need to design an Admin form that works better for editing them.
I batch upload photos and the Photo models are created. Then when I write the articles, I want to be able to edit the Photo models as an inline form.
When I set it up as described so far, I get an article edit form, with a selector to click on the name of each photo. I would like to have all unassigned photos shown, with a text box for the cutline on each photo and checkbox. When I put a checkbox and save the article the pictures are associated with that article. I also need to save the updated Photo objects with the text under each photo.
Suggestions for how I should go about this?
Models.py
class Photo(models.Model):
cutline = models.CharField("Cutline", max_length = 1012, null = True)
uploadDate = models.DateField()
imagefile = models.FileField(upload_to=user_directory_path, max_length=250,
class Articles(models.Model):
pictures = models.ManyToManyField(Photo, related_name='article', blank=True)
title = models.CharField("Title", max_length = 255)
story = models.TextField("Article")
Forms.py
from django import forms
from django.forms import BaseInlineFormSet
from django.forms.formsets import BaseFormSet
from django.forms import ModelForm
from django.db.models import Count, Q
from News.models import Articles, Photo
# Create the form class.
class PhotoIForm(ModelForm):
class Meta:
model = Photo
fields = ['cutline', 'imagefile']
class ArticleForm(ModelForm):
class Meta:
model = Articles
fields = ('title', 'story')
class BasePhotoFormSet(BaseInlineFormSet):
def __init__(self, *args, **kwargs):
super(BasePhotoFormSet, self).__init__(*args, **kwargs)
self.queryset = Photo.objects.annotate(articles_count=Count('article')).filter(articles_count=0)
print(a)
Views.py
#login_required
def article_edit(request):
# Create the formset, specifying the form and formset we want to use.
#PhotoFormSet = modelformset_factory(Photo, formset=BasePhotoFormSet, fields = ('cutline', 'imagefile')) #This doesn't work - seems to fail with the m2m fk
PhotoFormSet = modelformset_factory(Photo, fields = ('cutline', 'imagefile')) # close but it doesn't allow me to set up the initial items and limit photos to ones without assigned articles
article = Articles.objects.get(pk = 2718)
if request.method == 'POST':
article_form = ArticleForm(request.POST, article=article)
photo_formset = PhotoFormSet(request.POST)
if article_form.is_valid() and photo_formset.is_valid():
# Save user info
print("Data is valid save"
else:
article_form = ArticleForm(instance=article)
photo_formset = PhotoFormSet()
context = {
'article_form': article_form,
'photo_formset': article_formset,
}
return render(request, 'News/articleInline.html', context)

Django - Createview form_valid object.id error

I'm trying to create a form where the object created (a project) has a relationship with another model (the channel). The problem is I can't workout how to call the channel's primary key for the project's relationship.
Models.py:
class Project(models.Model):
channel = models.ForeignKey(
'Channel',
on_delete=models.CASCADE,
)
Views.py:
class ProjectCreate(CreateView):
model = Project
fields = ['name', 'description']
def form_valid(self, form):
Project = form.save(commit=False)
form.instance.channel = Channel.objects.get(id=self.kwargs['channel'])
Project.channel = channel
return super(ProjectCreate, self).form_valid(form)
I think something else needs to be added to the forms.py file as well:
Forms.py:
class ProjectForm(forms.Form):
name = forms.CharField(max_length=50)
description = forms.CharField(widget=forms.Textarea)
Firstly, you should use a ModelForm so that you can save it to create the instance. Don't include channel in the fields, because you're going to set it in the view.
class ProjectForm(forms.ModelForm):
class Meta:
model = Project
fields = ['name', 'description']
Then, assuming that your url pattern is correctly configured to include the channel, all you need to do is set the channel on the form instance and call the parent class' form_valid method.
def form_valid(self, form):
form.instance.channel = Channel.objects.get(id=self.kwargs['channel'])
return super(ProjectCreate, self).form_valid(form)

How to save user or session data in FormView along with form data?

I am trying to develop a small app a user can upload images after logging in. I have used ModelForm to generate the form for uploading the image and its decription. The ArtWork model has foreign key relationship with the User model. I exclude the user field from the ModelForm. Now I do not have any clue how to save the logged in user along with the form data into the database. Thanks in advance.
Here is my ArtWork Model
class Artwork(models.Model):
art_image = models.ImageField("Art Image", upload_to="images", blank=True,null=True)
art_name = models.CharField(max_length=100, blank=True,null=True)
user = models.ForeignKey(AuthUser, default=1)
def __unicode__(self):
return self.art_name
Here is my View
class UploadArtworkView(FormView):
form_class = ArtworkForm
success_url = "/"
template_name = "artwork/upload.html"
def form_valid(self, form):
#artwork = Artwork(art_image= self.get_form_kwargs().get('files')['art_image'])
#print "Art name is" + form.post['art_name']
form.save()
print self.request.user.name
return super(UploadArtworkView,self).form_valid(form)
Here is the Form
from django.forms import ModelForm
from .models import Artwork
class ArtworkForm(ModelForm):
class Meta:
model = Artwork
fields = ['art_image','art_name']
Your use case is very similar to this example in the Django docs. You have already realised that form_valid is the correct method to override, so you are very close to the answer. The trick is to access the object using self.form.instance, and set the user:
def form_valid(self, form):
form.instance.user = self.request.user
return super(UploadArtworkView,self).form_valid(form)