Limit number of images user can upload - django

I have a blog feature on my site, users can upload a single main image and multiple supporting images. The issue I am facing is I want to be able to limit the number of images a user can upload. I understand that I could use a for loop on it but if the user goes back later and adds more it would make the for loop useless. So I figured the best way to do this would be to add a field to the model that would count the number of images uploaded and then I can use an if statement to check if more than a said number of images have been uploaded. How would i go about getting the number of images and adding them to the post while it is being created. Or should I go about this a different way
view
#login_required
def createPostView(request):
currentUser = request.user
postForm = PostForm()
if request.method == 'POST':
postForm = PostForm(request.POST, request.FILES)
if postForm.is_valid():
PostFormID = postForm.save(commit=False)
PostFormID.author = request.user
PostFormID.save()
for f in request.FILES.getlist('images'):
test = PostImagesForm(request.POST, request.FILES)
if test.is_valid():
instance = test.save(commit=False)
instance.post_id = PostFormID.id
instance.images = f
instance.save()
return HttpResponseRedirect("/")
return render(request, 'blog/post_form.html', {'postForm': postForm, 'PostImagesForm':PostImagesForm})

You can check the total items in a list before looping by using len.
len(request.FILES.getlist('images'))
>>> 10
So something like:
if len(request.FILES.getlist('images')) > 3:
raise Exception("Only three images allowed")
else:
// process the images in the for loop

Related

How do i maintain a user cover uploaded image in fields whiles adding new images without it getting deleted in django

How do i get Django to store already uploaded cover image to a user without getting it deleted when a new image is uploaded, but simply replace it? I'm having a challenge trying to figure out how to maintain old cover images while adding new once to a user. what happens is that when i upload a new cover image it simply deletes the previous one from the database.
Here is my cover image models:
class AccountCover(models.Model):
account = models.ForeignKey(Account,on_delete=models.CASCADE)
cover_image = models.ImageField(max_length=255,upload_to=get_cover_cover_image_filepath,default=get_default_cover_image,)
Here the view to upload cover image
cover = AccountCover.objects.filter(account=account.id).first()
if request.user:
forms = CoverImageForm(request.POST, request.FILES,instance=cover,
initial = {'cover_image':cover.cover_image})
if request.method == 'POST':
f = CoverImageForm(request.POST, request.FILES,instance=cover)
if f.is_valid():
data = forms.save()
data.account = cover.account
data.save()
return redirect('account:edit', account.id)
else:
f = CoverImageForm()
context['f'] = f
I would suggest adding a field called historical which is a boolean field in your class. Then, when a new image is uploaded you have to set historical=False and historical=True for all others. You can achieve this with:
cover = AccountCover.objects.filter(account=account.id).first()
if request.user:
forms = CoverImageForm(request.POST, request.FILES,instance=cover,
initial = {'cover_image':cover.cover_image})
if request.method == 'POST':
f = CoverImageForm(request.POST, request.FILES,instance=cover)
if f.is_valid():
data = forms.save()
data.account = cover.account
cover.historical=True
data.historical= False
data.save()
cover.save()
return redirect('account:edit', account.id)
else:
f = CoverImageForm()
context['f'] = f

For loop on form not functing properly

I have a feature that allows users to upload multiple images to their blog but it is not working correctly. When a user uploads multiple images only one of them is uploaded to the postgres db.
view
def DetailPostView(request, pk):
model = Post
post = Post.objects.get(pk=pk)
if request.method == 'POST':
test = PostImagesForm(request.POST, request.FILES)
files = request.FILES.getlist('images')
if test.is_valid():
for f in files:
instance = test.save(commit=False)
instance.post = Post.objects.get(pk=pk)
instance.save()
else:
print(instance.errors)
postgallery = PostImages.objects.filter(post_id=post)
context = {
'post':post, 'PostImagesForm':PostImagesForm, 'postgallery':postgallery
}
return render(request, 'blog/post_detail.html', context)
form
class PostImagesForm(ModelForm):
class Meta:
model = PostImages
fields = ('images',)
widgets = {
'images': forms.ClearableFileInput(attrs={'multiple': True}),
}
you can see i am getting the list of files via the files = request.FILES.getlist('images') then running a for loop on the contents.
If I break the code in the stack trace I can see that the two files are in the list so i am very confused on why it is not properly iterating though the list and uploading each file to the db.
Update
Took a look into the docs and found a section on multi image upload and the docs are doing it the same way I am. Still very confused.
I believe the issue was the because I was not passing the current image into the model so it only uploaded the first image.
Solution
if request.method == 'POST':
for f in request.FILES.getlist('images'):
test = PostImagesForm(request.POST, request.FILES)
if test.is_valid():
instance = test.save(commit=False)
instance.post = Post.objects.get(pk=pk)
instance.images = f
instance.save()

Django initial value for MultiChoice Field ignored for ModelForm

this is my first post here and I am very new to Django but I just can't seem to find a solution for this problem... I've searched stackoverflow and google but nothing seems to work for me...
I have a wine-app and want to be able to add and remove wines from the user's stock. In the list of wines the user can choose a wine to add and the ID of this wine is passed in the POST data. Since the data is getting lost after the first time the view is rendered I saved the ID in a cookie, which is working, but the problem is when I work with ModelForm de user has to select the foreign key for the user and for the wine, which is bad, so I tried to make it hidden and set the Fk_user and Fk_wine after the user choose the number of bottles to be added but before validation. Here's the problem after google everyone suggested I should use the "initial" and pass that to the form, but this is clearly not working because if I make the fields visible in the form I can see that it is not preselected...
viewy.py:
def addStockView(request):
wineId = request.POST.get('addStock')
if 'addStock' in request.POST:
wine = get_object_or_404(Wine, idwine=int(wineId))
userId = request.user.id
user = get_object_or_404(AuthUser, id=userId)
if request.method == 'POST':
#wineIdNew = request.COOKIES.get('wineIdToAdd')
#wineNew = get_object_or_404(Wine, idwine=wineIdNew)
form = StockForm(request.POST, initial={'fk_wine': wineNew.idwine, 'fk_auth_user': user.id})
if form.is_valid():
form.save()
return redirect('home')
else:
form = StockForm(initial={'fk_wine': wine.id,
'fk_auth_user': user.id})
response = render(request, 'addToStock.html', {'form': form})
response.set_cookie('wineIdToAdd', wineId)
return response
forms.py:
class StockForm(forms.ModelForm):
#fk_wine = ModelChoiceField(queryset=Wine.objects.all(),
# widget=HiddenInput())
#fk_auth_user = ModelChoiceField(queryset=AuthUser.objects.all(),
# widget=HiddenInput())
class Meta:
model = UserWineStock
fields = ['fk_auth_user', 'fk_wine', 'number']
can anyone help me with this..?
Yes, initial data is ignored when a form is bound to submitted data.
Instead of using initial here, you should exclude those two fields from the form and set them on the created object:
form = StockForm(request.POST)
if form.is_valid():
item = form.save(commit=False)
item.fk_wine = wine
item.fk_auth_user = request.user
item.save()
return redirect('home')
(Also, please don't call your fields things like fk_auth_user. Just call it user.)

Can't figure out why form.has_changed() is always true?

I'm trying to learn Django and have come up with a situation I can't figure out. I have the following code:
def contact_add(request):
if request.method == 'POST':
form = ContactManageForm(request.POST)
if form.is_valid():
if form.has_changed(): # <-- ALWAYS RETURNS TRUE!
form.clean()
...
elif 'id' in request.GET: # Request to show an existing contact
new_contact_dynamic = contacts.models.PersonDynamic.objects.get(person_static = request.GET['id'],
current_record_fg = True)
form = ContactManageForm(new_contact_dynamic.__dict__, initial=new_contact_dynamic.__dict__)
else: # This must be to add a new contact
form = ContactAddForm()
return render(request, 'contact_manage.html', {'form': form})
So, if I'm sent an ID number, I read a record and display it on the screen. My template gives the user a 'submit changes' button. My problem, as noted above, is that Django always shows that the form has changed, even if the user hasn't changed any data on the screen (i.e. he just hit the submit changes button without changing anything).
So, am I doing something obviously wrong in my code that's creating this situation? Am I misinterpreting how the form.has_changed() method works?
It's my assumption that when I use the initial=parameter after a GET request, Django is storing that data somewhere and knows the context when the user then hits the 'submit data' button, is this wrong?
Yes you need to initialize your Form with initial data.
In your view the GET and POST requests have no common context. You may want to use sessions for that.
But in this case, it is not necessary. You can retrieve the instance on each request:
def contact_add(request):
if 'id' in request.GET:
new_contact_dynamic = contacts.models.PersonDynamic.objects.get(
person_static = request.GET['id'],
current_record_fg = True
)
if request.method == 'POST':
form = ContactManageForm(request.POST, initial=new_contact_dynamic.__dict__)
...
else: # Show an existing contact
form = ContactManageForm(initial=new_contact_dynamic.__dict__)
else:
form = ContactAddForm()
return render(request, 'contact_manage.html', {'form': form})

Formset populated by wrong data - Django 1.8

Basic problem: I have a list of products, and when I go to edit a single product, the image upload formset is populated with all images in the folder, but it should only be populated by related images (see left hand side of picture below).
When I try to make any changes - to the product or to the images - it is unsuccessful, and the input boxes which were populated by unrelated images now display 'this field is required' (see right hand side of picture below)
Obviously, I need to successfully edit the product object, and I want this page to be populated only by images related to that product.
The View
def EditProduct(request, pk):
instance = get_object_or_404(Product, id=pk)
ImageFormSet = modelformset_factory(ProductImage,
form=ImageForm, extra=4)
if request.method == 'POST':
product_form = AddEditProductForm(request.POST, request.FILES, instance=instance)
formset = ImageFormSet(request.POST, request.FILES,
queryset=ProductImage.objects.none())
if product_form.is_valid() and formset.is_valid():
product = product_form.save()
images = formset.save(commit=False)
for image in images:
image.product = product
image.save()
return HttpResponseRedirect('/product/')
else:
print (product_form.errors, formset.errors)
else:
product_form = AddEditProductForm(instance=instance)
formset = ImageFormSet(queryset=ProductImage.objects.all()) # possible wrong queryset
return render(request, 'product/edit.html',
{'product_form': product_form, 'formset': formset},
context_instance=RequestContext(request))
So this code is somehow returning ALL of my uploaded images - including unrelated images - to the editing form for a single product - how do I make it so only those images which are related to the instance are displayed in the form?
On a related note - how do you access an individual image in the formset - for example if i wanted to remove one, what kind of modifications am I looking at?
This depends on your model.
formset = ImageFormSet(queryset=ProductImage.objects.all()) # possible wrong queryset
This is indeed the wrong queryset. You should do something like:
queryset=ProductImage.objects.filter(product_id = pk)
if you gave the product_image field a related_name than you could do
queryset=instance.related_product_images
https://docs.djangoproject.com/en/1.8/ref/models/fields/#django.db.models.ForeignKey.related_name