Using Modal FileField to add PDF files - python

I am making a web site for a travel agency. I am using a modal field FileField to add PDF files to more pages.
models.py
class Travel(models.Model):
docfile = models.FileField(upload_to='documents')
It works fine when uploading just one file in admin and displaying one file on the template.
file.html
<div>
<h4>{{ travel.docfile.name }}</h4>
</div>
But what if I want to upload and display more than one file per page?
I now I could loop over all files to get all files for all pages:
{% if documents %}
<ul>
{% for document in documents %}
<li>{{ document.docfile.name }}</li>
{% endfor %}
</ul>
{% else %}
<p>No documents.</p>
{% endif %}
view.py
def vacation(request):
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Travel(docfile = request.FILES['docfile'])
newdoc.save()
return HttpResponseRedirect(reverse('myapp.views.vacation'))
else:
form = DocumentForm()
documents = Travel.objects.all()
return render_to_response('myapp/file.html',{'documents': documents, 'form': form},context_instance=RequestContext(request))
But how can I upload and display more files for one page?
Thank you for your time.

there are a few ways (that I can see):
you allow up to N uploads each time: thus you just create a form with N fields to take care of it. It's not very fancy (nor the most usable UI), but it's easy to implement.
if you want to give the user the possibility to upload a "dynamic" number of files, you may implement a view that is called via ajax. This still requires your user to select one file at the time, and load it, but then you can upload several files one after the other, without reload the entire page. You may even find some reusable app, like the following, that already should implement the most of it: https://github.com/skoczen/django-ajax-uploader
you can use the HTML5 multiple attribute of the <input type="file" ... field. Even in this case there is an interesting reusable app: https://github.com/Chive/django-multiupload
Hope it helps

Related

Unable to select value in action intermediate page

I have two models User and Group.
I'm implementing an action "Change Groups" in UsersAdmin that redirects to an intermediate page with 2 MultipleChoiceFields for Groups, that I want to be used to either remove users from certain groups, add users to other groups, or do both in one go (i.e. move them).
The docs are very short about this subject, so in order to do this, I'm following this article.
Here's my form:
class ChangeUsersGroupsForm(forms.Form):
_selected_action = forms.CharField(widget=forms.MultipleHiddenInput)
from_groups = forms.ModelMultipleChoiceField(Group.objects, required=False)
to_groups = forms.ModelMultipleChoiceField(Group.objects, required=False)
My admin action:
def change_groups_action(self, request, queryset):
if 'apply' in request.POST:
from_groups = request.POST["from_groups"]
to_groups = request.POST["to_groups"]
from_groups_qs = Group.objects.filter(pk__in=from_groups).all()
to_groups_qs = Group.objects.filter(pk__in=to_groups).all()
user_ids = [u.user_id for u in queryset]
# task that will do the job of actually moving the users
change_users_groups.delay(from_groups_qs, to_groups_qs)
self.message_user(request, "Changed groups of %s users" % len(user_ids))
return HttpResponseRedirect(request.get_full_path())
form = ChangeUsersGroupsForm(initial={'_selected_action': queryset.values_list('id', flat=True)})
return render(request, "admin/change_users_groups.html", {'queryset': queryset, 'form': form})
change_groups_action.short_description = "Change Groups"
Here's my template:
<!-- users/templates/admin/change_users_groups.html -->
{% extends "admin/base_site.html" %} {% block content %}
<form action="" method="post">
{% csrf_token %}
{{ form }}
<br />
<br />
<p>The Group changes will be applied to the following users:</p>
<ul>
{{ queryset|unordered_list }}
</ul>
<input type="hidden" name="action" value="change_groups_action" />
<input type="submit" name="apply" value="Confirm" />
</form>
{% endblock %}
This is how the intermediate page renders:
First (but minor) issue is that the form fields are displayed in a row, instead of each in one row. But let's skip that for now.
The big issue is that when I select a Group, nothing happens, the Group doesn't seem to be selected.
Instead I see the following error on the browser Console:
Uncaught TypeError: django.jQuery is not a function
This error is printed every time I click on an option.
Anyone knows what's going on?
Django 2.2
Python 3.8.10
I've had this problem before, but in different situation (unrelated to Django admin). I almost always turned out to be because JQuery was not loaded or it was loaded too late in the template.
According to Django documentation:
If you want to use jQuery in your own admin JavaScript without
including a second copy, you can use the django.jQuery object on
changelist and add/edit views. Also, your own admin forms or widgets
depending on django.jQuery must specify js=['admin/js/jquery.init.js',
…] when declaring form media assets.
So that would make your form class look like:
class ChangeUsersGroupsForm(forms.Form):
_selected_action = forms.CharField(widget=forms.MultipleHiddenInput)
from_groups = forms.ModelMultipleChoiceField(Group.objects, required=False)
to_groups = forms.ModelMultipleChoiceField(Group.objects, required=False)
class Media:
js = ['admin/js/jquery.init.js']
Regarding your form field form. I suggest rendering each field separately like so:
{{ form.from_groups }}
<br/>
{{ form.to_groups }}
This seems like the simplest solution
Let me know if that helps :)

Download file stored in a model with FileField using Django

I'm planning on creating a simple file upload application in Django (incorporated in some bigger project). For this one, I'm using two links: /files/ - to list all the uploaded files from a specific user, and /files/upload/ - to display the upload form for the user to add a new file on the server.
I'm using a PostgreSQL database, and I'm planning to store all my files in a File model, that looks like this:
class File(models.Model):
content = models.FileField()
uploader = models.ForeignKey(User, on_delete=models.CASCADE)
My file upload view looks like this:
#login_required
def file_upload_view(request):
if request.method == "POST" and request.FILES['file']:
file = request.FILES['file']
File.objects.create(content=file, uploader=request.user)
return render(request, "file_upload_view.html")
else:
return render(request, "file_upload_view.html")
while my file list view looks like this:
#login_required
def file_view(request):
files = File.objects.filter(uploader = request.user)
return render(request, "file_view.html", {'files': files})
The problem is, I want my file_view() to display all files uploaded by a certain user in a template (which I already did), each file having a hyperlink to its download location. I've been trying to do this in my file list template:
{% extends 'base.html' %}
{% block content %}
<h2>Files of {{ request.user }}</h2>
<br>
{% for file in files %}
<a href="{{ file.content }}" download>{{ file.content }}</a>
{% endfor %}
{% endblock %}
but my browser is trying to download some .htm page, even if the file I've uploaded is of type .txt.
My question is: do I also need to upload the file in a certain directory, before adding it to the model? Do I need to actually add the file's URL as a new field in my model? Aren't the files automatically uploaded to my PostgreSQL database?
Any help would be greatly appreciated.
Thank you.
I guess you should show a folder to where you want to save your files like this:
content = models.FileField(upload_to='/myfiles/')
And then you can show the url ("{% url 'file.url' %}") in your html because the database actually saves the url of your file and stores the file itself in the folder you've showed in your model.

Is it possible to customize individual pages associated with the same template? dynamic URL django

I am working on a blog website and have set up dynamic URLs in Django for each blog article. Is it possible to change the layout of paragraphs and images of specific pages so that there are slight differences between each page?
Yes it is possible to have different layout for the same template rendered in Django. You can pass some variable in context to achieve this goal like this :
views.py
def detail(request, id):
object = Model.objects.get(pk=id)
context = {'layout': f"layout_for_{id}"}
return render(request, 'template.html', context)
template.html
{% if layout == 'layout_for_2' %}
Layout for 2 here
{% endif %}
You can add more condition as you want, but it can become hard to for too much {% if %} block.

Django Crispy forms not showing bootstrap/css or button

I got crispy forms working with my model, though the form looks plain and bootstrap not showing up, also there seems to be no button. Even when adding button and clicking it(it refreshes) no data has been saved to the database. I have tried many ways. What seems to be wrong? Any help would be highly appreciated.
forms.py
class PlotForm(forms.ModelForm):
helper = FormHelper()
helper.form_tag = False
helper.form_method = 'POST'
class Meta:
model = Plot
fields = '__all__'
views:
def plot_form(request):
return render(request, 'plot_form.html', {'form': PlotForm()})
the html:
{% load crispy_forms_tags %}
<form action="" method="POST">
{% crispy form %}
<input type="submit" class="btn btn-default" value="save">
First the {% csrf_token %} is missing. Second If I remember it correct you need to use {{ form|crispy }} to load the form.
And third I would recommend to use Widget Tweaks
<form method='POST' action="/" enctype='multipart/form-data'>
{% load widget_tweaks %}
{% csrf_token %}
{{ form.first_name |add_class:"customCSS1 customCSS2" }}
{{ form.second_name |add_class:"customCSS3 customCSS4" }}
</form>
{{ form.media.js }}
with this plugin you can style the form as you wish. All Css classes work. Crispy is nice but you have get into the documentation and there are always some workarounds you need to do when you want to style the form. With Widget Tweaks you can simply apply any CSS class. When you really know your way around with crispy you can do a lot but to get to that point....
I switched at some Point and now everything works like a charm
Hope that helps if not leave a comment :)
Edit
I just saw something in your views.py. You are not referencing the form correct as far as I can tell.
from appName.forms import PlotForm
def plot_form(request):
form = PlotForm(request.POST or None, request.FILES or None) #request files is only required when you want to upload a file
if form.is_valid():
instance = form.save(commit = False)
...
instance.save()
#messages.success(request, 'form was saved') #optional
context = {
'form':form,
}
return render(request, 'AppName/plot_form.html', context)
Maybe that will do the trick. You did not have a form validation and Im not sure if the "()" at {'form': PlotForm()} would break the code.
I also faced the same issue. Solved it by testing it on firefox instead of google chrome. Apparently, Chrome does not load CSS style for a few local web apps. Got it to work after following this hack. https://css-tricks.com/new-in-chrome-css-overview/

Using #processor_for with a Form in Mezzanine

I've built a Form Page in the admin of my Mezzanine project, but I'd like to populate a couple of the fields automatically, depending on where the click to the form has come from: it's a "feedback" form and I'd like to automatically add the ID of the object that the user is providing feedback on to a hidden field in the form.
I've copied the template code from mezzanine/forms/templates/pages/form.html to a custom template and it receives the dictionary I pass it from my view, but I can't work out to pass it my the form I want rendered. The #processor_for function receives request and page... but where's the form?
What should I be passing to my template to render the form?
You can use the template tag fields_for:
{% load mezzanine_tags %}
{% errors_for some_form_object %}
<form method="POST">
{% fields_for some_form_object %}
<input type="submit">
</form>

Categories

Resources