Multiple file upload the easy way in django - python

I have to create a form to make a post with fields like Title, description and images. I could successfully create a post with a single image, but when it comes to multiple images, it is not getting saved. Upon research found that i have to use a separate model and form for images. But i cannot see how is it accomplished in the views. So how do i accomplish this the easy way?
Models.py
class news(models.Model):
title = models.CharField(max_length=100)
description= models.TextField(max_length=500)
class newsimages(models.Model):
image = models.ImageField(upload_to='/images/')
related = models.ForeignKey(news,on_delete=models.CASCADE,null=True)

You can do with inline_formset
create a form
class ImageForm(ModelForm):
class Meta:
model = newsimages
exclude = ()
ImageFormSet = inlineformset_factory(news, newsimage, form=ImageForm, extra=1)
You CreateView would be something like:
class NewsCreate(CreateView):
model = news
fields = ['title', 'description']
success_url = some_url
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['images'] = ImageFormSet(self.request.POST or None)
return ctx
def form_valid(self, form):
ctx = self.get_context_data()
images = context['images']
with transaction.atomic():
self.object = form.save()
if images .is_valid():
images.instance = self.object
images.save()
return super().form_valid(form)
in news_form.html
<form method="post" enctype="multipart/form-data">
{{ form.as_p }}
<table>
{{images.management_form}}
{% for f in images.forms %}
{% if forloop.first %}
<thead>
<tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr>
</thead>
{% endif %}
<tr class="{% cycle row1,row2 %} formset_row">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input type="submit" value="Save"/>
</form>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="{% static 'formset/jquery.formset.js' %}"></script>
<script type="text/javascript">
$('.formset_row').formset({
addText: 'add image',
deleteText: 'remove',
prefix: 'images_set'
});
</script>
You will need to source jquery.formset.js which I think you'll find in django admin static files.

Related

Simple-file-upload addon returns no file on submit - django

I've been trying to add a heroku addon on my deploy. I have tried configuring the simple-file-upload but it still doesn't give the desired result.
It returns (Hidden field File) This field is required.
Here are my attempts.
views.py
class AddDownloadView(LoginRequiredMixin,CreateView):
model = Download
form_class = AddDownloadsForm
template_name = 'add_file.html'
success_url = reverse_lazy('all_downloads')
models.py
class Download(models.Model):
File = models.FileField(upload_to='files')
File_Name = models.CharField(max_length=200)
class Meta:
ordering = ('-pk','File_Name')
def get_absolute_url(self):
return reverse('home')
def __str__(self):
return self.File_Name
forms.py
class AddDownloadsForm(forms.ModelForm):
class Meta:
model = Download
fields = ("File",'File_Name')
widgets = {
"File":forms.FileInput(attrs={"class":'simple-file-upload','type':'hidden'}),
"File_Name":forms.TextInput(attrs={"class":"form-control"}),}
add_file.html < form for adding the file uploads >
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.media }}
{{ form.as_p }}
<button class="btn btn-outline-primary">Add</button>
</form>
form for displaying the uploads
<div style="min-height:350px;columns:200px 2;overflow-y:scroll;" class="container alert alert-
default" role="alert">
{% for item in file %}
{% if item.File_Name %}
--> {{ item.File_Name }}<br>
{% if user.is_authenticated %}
==> Edit ||
Delete<hr>
{% endif %}
{% endif %}
{% empty %}<br><br>
<h6><i>No files to download yet</i></h6><br><br>
{% endfor %}
</div>
I've struglled through same problems but have not come up with a solution, please help.

Django wrong request.POST attribute value saved

I am experiencing the issue that the code saves request.POST['name'] value to assumptions.Name field in database, but only the last value in the list which is 'B' in this case. How could I make it save both prefixes 'A' and 'B' to database field Name based on respective forms. When I debug I see both 'A' and 'B' with key 'name'.
views.py
from django.shortcuts import render
from .forms import modelformset_factory, AssumptionsForm
from .models import Assumptions
from django.core.exceptions import ValidationError
model_names = ['A', 'B']
def get_assumptions(request):
AssumptionFormset = modelformset_factory(
Assumptions, form=AssumptionsForm, extra=5)
if request.method == 'POST':
formsets = [AssumptionFormset(request.POST, prefix=thing) for thing in model_names]
if all([formset.is_valid() for formset in formsets]):
for formset in formsets:
for form in formset:
assumption_data = form.save(commit=False)
assumption_data.Name = request.POST.get('name')
assumption_data.save()
else:
formsets = [AssumptionFormset(prefix=thing) for thing in model_names]
return render(request, 'assumptions.html', {'formsets': formsets})
assumptions.html
<div class="form">
<form action="" method="post">
{% for formset in formsets %}
{% csrf_token %}
{{ formset.management_form }}
{{ formset.non_form_errors.as_ul }}
<h1>{{formset.prefix}}</h1>
<table id="formset" class="form">
{% for form in formset.forms %}
{% if forloop.first %}
<thead><tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr></thead>
{% endif %}
<tr class="{% cycle 'row1' 'row2' %}">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input type="hidden" id={{formset.prefix}} name='name', value={{formset.prefix}} />
{% endfor %}
<input type="submit" value="Submit">
</form>
</div>
models.py
from django.db import models
from django.forms import ModelForm
class Assumptions(models.Model):
Worst = models.FloatField(null=True, blank=True, default=None)
Base = models.FloatField(null=True, blank=True, default=None)
Best = models.FloatField(null=True, blank=True, default=None)
Name = models.TextField(null=True, blank=True, default=None)
forms.py
from django import forms
from django.forms import modelformset_factory, ModelForm
from .models import Assumptions
class AssumptionsForm(ModelForm):
class Meta:
model = Assumptions
fields = '__all__'
exclude = ['Name']
I figured it out. I should have used getlist method and then loop through values.
values = request.POST.getlist('name')
Cheers.

non_form_errors doesn't show

I am working with inlineformset_factory and created a forms.BaseInlineFormSet to override the clean function to make some validations. the validation works and the form isn't accepted but the ValidationError message doesn't appear when the page is reloaded.
here is my form class:
class BaseDetailFormSet(forms.BaseInlineFormSet):
def clean(self):
super(BaseDetailFormSet, self).clean()
if any(self.errors):
return self.errors
for form in self.forms:
product = form.cleaned_data['product']
if form.cleaned_data['quantity_sold'] > product.quantity_in_stock:
raise forms.ValidationError(_('not enough products'))
DetailFormset = inlineformset_factory(Invoices,
InvoiceDetail,
fields=('product', 'quantity_sold'),
widgets= {'product': forms.Select(
attrs={
'class': 'search',
'data-live-search': 'true'
})},
formset=BaseDetailFormSet,
extra=1)
and my html code:
<form class="form-inline" method="post" action="">
{% csrf_token%}
{% include '_layouts/form_snippet.html' %}
<table class="table">
{{inlines.management_form}}
{%for form in inlines.forms%}
{% if forloop.first%}
<thead>
<tr>
{%for field in form.visible_fields%}
<th>{{field.label|capfirst}}</th>
{%endfor%}
{%endif%}
</tr>
</thead>
<tr class="formset_row">
{{ form.non_form_errors }}
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<button type="submit" value="Add" class="btn btn-inverse">{% trans 'Submit Invoice' %}</button>
</form>
any ideas how to show the validation error message or why it isn't shown.
non_form_errors belongs to the formset not the form.
So you can display it with:
{{ formset.non_form_errors }}
Or in your case, since you use the variable inlines:
{{ inlines.non_form_errors }}
Since it belongs to the formset, you should do this outside of the forloop through the forms.
If you have errors on the form that don't belong to any field, then you access these with
{{ form.non_field_errors }}

Unable to loop form fields in django

In models.py,I created a character type field called "category".After the user enters the category name,it is saved in the database and now I want to display all the category names stored in the database.I created four category names.I can see all four in the database but when displaying it in the UI, I see NONE instead of the category names.
views.py,
def add_cat(request):
form = CatForm(request.POST or None)
context = {"form":form}
if form.is_valid():
instance = form.save(commit=False)
category = form.cleaned_data.get("category")
instance.category = category
instance.save()
messages.add_message(request, messages.INFO, 'Category Added')
return render(request,"add-cat.html",context)
forms.py,
class CatForm(forms.ModelForm):
class Meta:
model = Add_cat
fields = ['category']
My template file,
{% extends "admin-menu.html" %}
{% block content %}
{% load staticfiles %}
<head>
<link rel="stylesheet" href="{% static 'style.css' %}">
</head>
<h2 style="text-align: center;">Add Category</h2>
<form method="POST">
{% csrf_token %}
<table align="center">
{{form.as_table}}
</table>
<input type="submit" value="Add" style="margin-left: 48%;"/>
<input type="reset" value="Cancel"/>
</form>
{% if messages %}
<ul class="messages" style="list-style-type: none;">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
&nbsp
<form id="id1">
{% for field in form %}
<table align="center">
<tr><th>Category Name</th></tr>
<tr><td>{{field.value}}</td></tr>
</table>
{% endfor %}
</form>
{% endblock %}
Try like this
def add_cat(request):
form = CatForm(request.POST or None)
catagories = <model>.objects.all()
context = {"form":form, 'categories':categories}
if form.is_valid():
instance = form.save(commit=False)
category = form.cleaned_data.get("category")
instance.category = category
instance.save()
messages.add_message(request, messages.INFO, 'Category Added')
return render(request,"add-cat.html",context)
In templates
{% for category in categories %}
<table align="center">
<tr><th>Category Name</th></tr>
<tr><td>{{ category }}</td></tr>
</table>
{% endfor %}

Django Uploading Files

I have a dashboard where you upload files and can see the files you uploaded. Strangely enough, when you go to upload a second file, it uploads but gives the error: IntegrityError at /dashboard/ column client_id is not unique I'm not sure why. My database is fresh and clean. What would cause this? The first file uploads and displays correctly redirecting you to the dashboard. The second file uploads, but doesn't display in the file list and displays that error. Any ideas why this is occurring or how to fix this error? I'm really stuck here so any help would save me big time.
Here is the view:
#login_required(login_url='/dashboard-login/')
def dashboard(request):
current_user = request.user
current_client = request.user.client
files = ClientUpload.objects.filter(client=current_client)
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
new_file = ClientUpload(client=current_client, file_upload = request.FILES['file_upload'])
new_file.save()
return HttpResponsePermanentRedirect('/dashboard/')
else:
form = UploadFileForm()
data = {'form': form, 'client': current_client, 'files': files}
return render_to_response('dashboard.html', data, context_instance=RequestContext(request))
The models:
#python_2_unicode_compatible
class Client(models.Model):
user = models.OneToOneField(User)
company = models.CharField(max_length=100)
def __str__(self):
return self.company
class Meta:
verbose_name_plural = _("Clients")
verbose_name = _("Client")
permissions = (
("can_upload", _("Can upload files.")),
("can_access_uploads", _("Can access upload dashboard.")),
("is_client", _("Is a client.")),
)
def generate_filename(self, filename):
name = "uploads/%s/%s" % (self.client.company, filename)
return name
#python_2_unicode_compatible
class ClientUpload(models.Model):
client = models.OneToOneField(Client)
created_at = models.DateTimeField(auto_now_add=True)
file_upload = models.FileField(upload_to=generate_filename)
def __str__(self):
return self.client.company
class Meta:
verbose_name_plural = _("Client Uploads")
verbose_name = _("Client Upload")
The form:
class UploadFileForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.add_input(Submit(_('submit'), _('Submit')))
super(UploadFileForm, self).__init__(*args, **kwargs)
class Meta:
model = ClientUpload
fields = ('file_upload',)
And lastly, the templates:
Upload file:
{% load i18n %}
{% load crispy_forms_tags %}
{% crispy form %}
File list:
{% load i18n %}
<table class="table">
<tr>
<th>{% blocktrans %}Filename{% endblocktrans %}</th>
<th>{% blocktrans %}Size{% endblocktrans %}</th>
<th>{% blocktrans %}Upload Time{% endblocktrans %}</th>
</tr>
{% for file in files %}
{% with uploaded_file=file.file_upload %}
<tr>
<th><a href='{{ uploaded_file.url }}'>{{ uploaded_file.name }}</a></th>
<th>{{ uploaded_file.size }}</th>
<th>Uploaded At</th>
{% endwith %}
{% endfor %}
</tr>
</table>
And the one that ties them together, dashboard:
{% extends "base.html" %}
{% load i18n %}
{% block title %}Shenkan & Associates - {% trans 'Dashboard' %}{% endblock title %}
{% block css %}
{{ block.super }}
{% endblock css %}
{% block extra_css %}
{% endblock extra_css %}
{% block ie_shim %}
{{ block.super }}
{% endblock ie_shim %}
{% block header %}
{{ block.super }}
{% endblock header %}
{% block slider %}
{% endblock slider %}
{% block page_header %}{% endblock page_header %}
{% block content %}
<!--=== Content ===-->
{% trans 'Logout' %}
<div class="container content-md">
{% include "upload_file.html" %}
</div>
<div class="container content-md">
{% include "file_list.html" %}
</div>
<!--=== End Content ===-->
{% endblock content %}
{% block footer %}
{{ block.super }}
{% endblock footer %}
{% block js %}
{{ block.super }}
{% endblock js %}
{% block ie_js %}
{{ block.super }}
{% endblock ie_js %}
{% block extra_js %}
{% endblock extra_js %}
You don't need to see base.html obviously.
If someone could help me solve this mystery it would help a ton as I am having tons of problems with this and have been stuck for days.
Thanks a bunch.
I think you should use "get_or_create" in the views.
I hope this can help: get_or_create throws Integrity Error

Categories