Pagination in Django TemplateView - django

I have a template where I want to display a large list of data, (received from external API rather than DB).
Although I'm aware this is easily done is ListView, however as I'm not pulling data from the database, TemplateView seems the best choice but what would the best way to display the list of data and paginate it?
Currently I have:
View
class QuotesResultsView(TemplateView):
template_name = 'site/quotes.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['quotes'] = self.request.session['quotes']['data']
return context
Template:
....
<div class="about">
{% for quote in quotes %}
<h3>Supplier:{{ quote.supplierName }}</h3>
<div>
<p>Annual Cost: {{ quote.newSpend }}</p>
<p>Savings: {{ quote.newSavings }}</p>
</div>
<button class="btn btn-cta-primary">Proceed</button>
{% endfor %}
</div><!--//about-->

You still can inherit ListView and override get_queryset() method, which must return an iterable. This will enable you to use pagination as usually
class QuotesResultsView(ListView):
template_name = 'site/quotes.html'
paginate_by = settings.QUOTES_PER_PAGE
context_object_name = 'quotes'
def get_queryset(self):
# Looks like your data is already an iterable
# if not convert it to iterable and return
return self.request.session['quotes']['data']
Don't forget to set QUOTES_PER_PAGE in your settings and import it in your views.py
Then in template your can use standard pagination snippet from docs.

Related

Django: get data from tables and display together using ListView

I want to display data from 2 tables (and more in the future), but something doesnt work in my code.
my views.py:
**imports**
def home(request):
context = {'users': Person.object.all(),
'emails': Email.object.all()
}
return render(request,'app/home.html',context)
class PersonListView(ListView):
model = Person
template_name = 'app/home.html'
context_object_name = 'users'
and in my home.html
{% extends "app/base.html" %}
{% block content %}
{% for user in users %}
Displaying user attributes works fine
{% endfor %}
Here should be emails
{% for email in emails %}
This displaying doesnt work
{% endfor %}
{% endbock content %}
So, displaying users works without any problem, but cant display anything form emails, but if I do it in shell, everything works well
A ListView [Django-doc] is designed to display only one queryset at a time. If you need to pass extra querysets, you can override the get_context_data(..) method [Django-doc]:
class PersonListView(ListView):
model = Person
template_name = 'app/home.html'
context_object_name = 'users'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update(emails=Email.objects.all())
return context
Here we thus pass an extra variable emails to the template rendering engine. Note however that this queryset will not be paginated (or at least not without adding some pagination yourself).
my models.py:
Note that those are views, you need to write these in views.py.

The "post" method in Django

I created three files:
2- view.py :
class AddTeamView(View):
def get (self, request):
form = TeamForm()
context = {'form': form}
return render(request, 'add_team.html', context)
1-forms.py:
class TeamForm(forms.Form):
name = forms.CharField( max_length='100')
details = forms.CharField(max_length='250')
3-add_team.html:
-here there is another file called "base.html"
{% extends 'base.html' %}
{% block title %}
add team
{% endblock %}
{% block content %}
<form action="/add_team/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
{% endblock %}
and i went to cmd and entered the server "python manage.py runserver"
it appeared on the browser:
"This page isn’t working
If the problem continues, contact the site owner.
HTTP ERROR 405"
A view can support methods like GET, POST, PUT, etc. given the corresponding method exists, so the view should have a .get(..), .post(..), .put(..), etc. function.
Here you only implemented a def get(self, request), and so POST requests are not allowed.
Based on the data you however show, this looks like the typical usecase of a CreateView [Django-doc]. The idea of these views is to encapsulate common scenario's such that by overriding a few attributes, one creates a view that is tailored towards a specific case, like:
class AddTeamView(CreateView):
form_class = TeamForm
template_name = 'add_team.html'
success_url = '/some/success_url'
The TeamForm should probably be a ModelForm, or at least a Form where you override the .save(..) function to properly save your data to the database, since right now, the form is not doing anything (well it receives the data, but after validation, it throws it away).
You might want to override the form_valid(..) function in case you do not want to redirect to the success_url. Furthermore it is very common that the success_url is resolved lazily from a given view name, like:
class AddTeamView(CreateView):
form_class = TeamForm
template_name = 'add_team.html'
success_url = reverse_lazy('view_name')
So, we don’t need to do a conditional to check if the request is a POST or if it’s a GET:
Your views.py:
from django.views.generic import View
class AddTeamView(View):
def post(self, request):
form = TeamForm(request.POST)
if form.is_valid():
new_tm = TeamModel(name=form.cleaned_data['name'], details=form.cleaned_data['details'])
new_tm.save()
return redirect('team_list')
return render(request, 'add_team.html', {'form': form})
def get(self, request):
form = TeamForm()
return render(request, 'add_team.html', {'form': form})
Hope this help you...

Save post data from a form with listview

i have a listview of form:
class MatchsView(ListView):
model = Match2x1
template_name = 'matchs.html'
and the template render this:
{% for match in object_list %}
<form action="/apostar/" method="post">{% csrf_token %}
<p><input type="radio" name="{{match.id}}" value="{{match.team_a}}">{{match.team_a}}</input></p>
<input type="submit" value="Apostar"></input>
</form>
{% endfor %}
as you can see each form has two fields, i need to save in DB the values that the user choose, with FormView its easy, but since this time is a ListView im a little bit lose to save in DB from a form, i know that i have to create a view that handles the form, but really i dont know how to create the view that handles the post data of each form. For example lets says that i need to save the post data in a model called FormsMatchs, how can i do it?
i was trying with this:
class FormView(FormView):
form_class = FormMatch
success_url = '/'
template_name = 'matchs.html'
def post(self, request, *args, **kwargs):
hola = Country.objects.create(name=request.POST)
but is saving this:
<QueryDict: {u'csrfmiddlewaretoken': [u'tCIQuGlSXKJL0R5eo9R5w09ldeBt7zNW'], u'5': [u'River']}>
Your best bet if you want to have a simple list of a given model but also accept a Form is to use a FormView, and override get_context_data(self, **kwargs) to pass a queryset into the context, like so:
def get_context_data(self, **kwargs):
context = super(MatchsView, self).get_context_data()
context['object_list'] = Match2x1.objects.all()
return context
However, you can also use a FormMixin with a ListView, see an example here.

Django: Can class-based views accept two forms at a time?

If I have two forms:
class ContactForm(forms.Form):
name = forms.CharField()
message = forms.CharField(widget=forms.Textarea)
class SocialForm(forms.Form):
name = forms.CharField()
message = forms.CharField(widget=forms.Textarea)
and wanted to use a class based view, and send both forms to the template, is that even possible?
class TestView(FormView):
template_name = 'contact.html'
form_class = ContactForm
It seems the FormView can only accept one form at a time.
In function based view though I can easily send two forms to my template and retrieve the content of both within the request.POST back.
variables = {'contact_form':contact_form, 'social_form':social_form }
return render(request, 'discussion.html', variables)
Is this a limitation of using class based view (generic views)?
Many Thanks
Here's a scaleable solution. My starting point was this gist,
https://gist.github.com/michelts/1029336
i've enhanced that solution so that multiple forms can be displayed, but either all or an individual can be submitted
https://gist.github.com/jamesbrobb/748c47f46b9bd224b07f
and this is an example usage
class SignupLoginView(MultiFormsView):
template_name = 'public/my_login_signup_template.html'
form_classes = {'login': LoginForm,
'signup': SignupForm}
success_url = 'my/success/url'
def get_login_initial(self):
return {'email':'dave#dave.com'}
def get_signup_initial(self):
return {'email':'dave#dave.com'}
def get_context_data(self, **kwargs):
context = super(SignupLoginView, self).get_context_data(**kwargs)
context.update({"some_context_value": 'blah blah blah',
"some_other_context_value": 'blah'})
return context
def login_form_valid(self, form):
return form.login(self.request, redirect_url=self.get_success_url())
def signup_form_valid(self, form):
user = form.save(self.request)
return form.signup(self.request, user, self.get_success_url())
and the template looks like this
<form class="login" method="POST" action="{% url 'my_view' %}">
{% csrf_token %}
{{ forms.login.as_p }}
<button name='action' value='login' type="submit">Sign in</button>
</form>
<form class="signup" method="POST" action="{% url 'my_view' %}">
{% csrf_token %}
{{ forms.signup.as_p }}
<button name='action' value='signup' type="submit">Sign up</button>
</form>
An important thing to note on the template are the submit buttons. They have to have their 'name' attribute set to 'action' and their 'value' attribute must match the name given to the form in the 'form_classes' dict. This is used to determine which individual form has been submitted.
By default, class-based views only support a single form per view. But there are other ways to accomplish what you need. But again, this cannot handle both forms at the same time. This will also work with most of the class-based views as well as regular forms.
views.py
class MyClassView(UpdateView):
template_name = 'page.html'
form_class = myform1
second_form_class = myform2
success_url = '/'
def get_context_data(self, **kwargs):
context = super(MyClassView, self).get_context_data(**kwargs)
if 'form' not in context:
context['form'] = self.form_class(request=self.request)
if 'form2' not in context:
context['form2'] = self.second_form_class(request=self.request)
return context
def get_object(self):
return get_object_or_404(Model, pk=self.request.session['value_here'])
def form_invalid(self, **kwargs):
return self.render_to_response(self.get_context_data(**kwargs))
def post(self, request, *args, **kwargs):
self.object = self.get_object()
if 'form' in request.POST:
form_class = self.get_form_class()
form_name = 'form'
else:
form_class = self.second_form_class
form_name = 'form2'
form = self.get_form(form_class)
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(**{form_name: form})
template
<form method="post">
{% csrf_token %}
.........
<input type="submit" name="form" value="Submit" />
</form>
<form method="post">
{% csrf_token %}
.........
<input type="submit" name="form2" value="Submit" />
</form>
Its is possible for one class-based view to accept two forms at a time.
view.py
class TestView(FormView):
template_name = 'contact.html'
def get(self, request, *args, **kwargs):
contact_form = ContactForm()
contact_form.prefix = 'contact_form'
social_form = SocialForm()
social_form.prefix = 'social_form'
# Use RequestContext instead of render_to_response from 3.0
return self.render_to_response(self.get_context_data({'contact_form': contact_form, 'social_form': social_form}))
def post(self, request, *args, **kwargs):
contact_form = ContactForm(self.request.POST, prefix='contact_form')
social_form = SocialForm(self.request.POST, prefix='social_form ')
if contact_form.is_valid() and social_form.is_valid():
### do something
return HttpResponseRedirect(>>> redirect url <<<)
else:
return self.form_invalid(contact_form,social_form , **kwargs)
def form_invalid(self, contact_form, social_form, **kwargs):
contact_form.prefix='contact_form'
social_form.prefix='social_form'
return self.render_to_response(self.get_context_data({'contact_form': contact_form, 'social_form': social_form}))
forms.py
from django import forms
from models import Social, Contact
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit, Button, Layout, Field, Div
from crispy_forms.bootstrap import (FormActions)
class ContactForm(forms.ModelForm):
class Meta:
model = Contact
helper = FormHelper()
helper.form_tag = False
class SocialForm(forms.Form):
class Meta:
model = Social
helper = FormHelper()
helper.form_tag = False
HTML
Take one outer form class and set action as TestView Url
{% load crispy_forms_tags %}
<form action="/testview/" method="post">
<!----- render your forms here -->
{% crispy contact_form %}
{% crispy social_form%}
<input type='submit' value="Save" />
</form>
Good Luck
I have used a following generic view based on TemplateView:
def merge_dicts(x, y):
"""
Given two dicts, merge them into a new dict as a shallow copy.
"""
z = x.copy()
z.update(y)
return z
class MultipleFormView(TemplateView):
"""
View mixin that handles multiple forms / formsets.
After the successful data is inserted ``self.process_forms`` is called.
"""
form_classes = {}
def get_context_data(self, **kwargs):
context = super(MultipleFormView, self).get_context_data(**kwargs)
forms_initialized = {name: form(prefix=name)
for name, form in self.form_classes.items()}
return merge_dicts(context, forms_initialized)
def post(self, request):
forms_initialized = {
name: form(prefix=name, data=request.POST)
for name, form in self.form_classes.items()}
valid = all([form_class.is_valid()
for form_class in forms_initialized.values()])
if valid:
return self.process_forms(forms_initialized)
else:
context = merge_dicts(self.get_context_data(), forms_initialized)
return self.render_to_response(context)
def process_forms(self, form_instances):
raise NotImplemented
This has the advantage that it is reusable and all the validation is done on the forms themselves.
It is then used as follows:
class AddSource(MultipleFormView):
"""
Custom view for processing source form and seed formset
"""
template_name = 'add_source.html'
form_classes = {
'source_form': forms.SourceForm,
'seed_formset': forms.SeedFormset,
}
def process_forms(self, form_instances):
pass # saving forms etc
It is not a limitation of class-based views. Generic FormView just is not designed to accept two forms (well, it's generic). You can subclass it or write your own class-based view to accept two forms.
Use django-superform
This is a pretty neat way to thread a composed form as a single object to outside callers, such as the Django class based views.
from django_superform import FormField, SuperForm
class MyClassForm(SuperForm):
form1 = FormField(FormClass1)
form2 = FormField(FormClass2)
In the view, you can use form_class = MyClassForm
In the form __init__() method, you can access the forms using: self.forms['form1']
There is also a SuperModelForm and ModelFormField for model-forms.
In the template, you can access the form fields using: {{ form.form1.field }}. I would recommend aliasing the form using {% with form1=form.form1 %} to avoid rereading/reconstructing the form all the time.
Resembles #james answer (I had a similar starting point), but it doesn't need to receive a form name via POST data. Instead, it uses autogenerated prefixes to determine which form(s) received POST data, assign the data, validate these forms, and finally send them to the appropriate form_valid method. If there is only 1 bound form it sends that single form, else it sends a {"name": bound_form_instance} dictionary.
It is compatible with forms.Form or other "form behaving" classes that can be assigned a prefix (ex. django formsets), but haven't made a ModelForm variant yet, tho you could use a model form with this View (see edit below). It can handle forms in different tags, multiple forms in one tag, or a combination of both.
The code is hosted on github (https://github.com/AlexECX/django_MultiFormView). There are some usage guidelines and a little demo covering some use cases. The goal was to have a class that feels as close as possible like the FormView.
Here is an example with a simple use case:
views.py
class MultipleFormsDemoView(MultiFormView):
template_name = "app_name/demo.html"
initials = {
"contactform": {"message": "some initial data"}
}
form_classes = [
ContactForm,
("better_name", SubscriptionForm),
]
# The order is important! and you need to provide an
# url for every form_class.
success_urls = [
reverse_lazy("app_name:contact_view"),
reverse_lazy("app_name:subcribe_view"),
]
# Or, if it is the same url:
#success_url = reverse_lazy("app_name:some_view")
def get_contactform_initial(self, form_name):
initial = super().get_initial(form_name)
# Some logic here? I just wanted to show it could be done,
# initial data is assigned automatically from self.initials anyway
return initial
def contactform_form_valid(self, form):
title = form.cleaned_data.get('title')
print(title)
return super().form_valid(form)
def better_name_form_valid(self, form):
email = form.cleaned_data.get('email')
print(email)
if "Somebody once told me the world" is "gonna roll me":
return super().form_valid(form)
else:
return HttpResponse("Somebody once told me the world is gonna roll me")
template.html
{% extends "base.html" %}
{% block content %}
<form method="post">
{% csrf_token %}
{{ forms.better_name }}
<input type="submit" value="Subscribe">
</form>
<form method="post">
{% csrf_token %}
{{ forms.contactform }}
<input type="submit" value="Send">
</form>
{% endblock content %}
EDIT - about ModelForms
Welp, after looking into ModelFormView I realised it wouldn't be that easy to create a MultiModelFormView, I would probably need to rewrite SingleObjectMixin as well. In the mean time, you can use a ModelForm as long as you add an 'instance' keyword argument with a model instance.
def get_bookform_form_kwargs(self, form_name):
kwargs = super().get_form_kwargs(form_name)
kwargs['instance'] = Book.objects.get(title="I'm Batman")
return kwargs

Django inlineformsetfactory - What is it good for?

Sorry for a newbie question but...
Can someone shed some light on what is the use case for inlineformset_factory?
I have followed example from Django documentation:
#Models
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
author = models.ForeignKey(Author)
title = models.CharField(max_length=100)
#View
def jojo(request):
BookFormSet = inlineformset_factory(Author, Book)
author = Author.objects.get(name=u'Mike Royko')
formset = BookFormSet(instance=author)
return render_to_response('jojo.html', {
'formset': formset,
})
#jojo.html
<form action="" method="POST">
<table>
{{ formset }}
</table>
<input type="submit" value="Submit" />
</form>
But it only displays book fields.
My understanding was that formset would display Book form with inline Author form just
like Django Admin. On top of that I can't easily pass initial values to formset?
Then how is it better then using two separate AuthorForm and BookForm?
Or am i missing something obvious?
inlineformset_factory only provides multiple forms for the nested elements, you need a separate form at the top if you want a form for the main model.
Here is an example of a working inlineformset_factory with the main form embedded at the top:
views.py
from django.shortcuts import get_object_or_404, render_to_response
from django.forms.models import inlineformset_factory
from django.http import HttpResponseRedirect
from django.template import RequestContext
from App_name.models import * #E.g. Main, Nested, MainForm, etc.
. . .
#login_required
def Some_view(request, main_id=None, redirect_notice=None):
#login stuff . . .
c = {}
c.update(csrf(request))
c.update({'redirect_notice':redirect_notice})#Redirect notice is an optional argument I use to send user certain notifications, unrelated to this inlineformset_factory example, but useful.
#Intialization --- The start of the view specific functions
NestedFormset = inlineformset_factory(Main, Nested, can_delete=False, )
main = None
if main_id :
main = Main.objects.get(id=main_id)#get_object_or_404 is also an option
# Save new/edited Forms
if request.method == 'POST':
main_form = MainForm(request.POST, instance=main, prefix='mains')
formset = NestedFormset(request.POST, request.FILES, instance=main, prefix='nesteds')
if main_form.is_valid() and formset.is_valid():
r = main_form.save(commit=False)
#do stuff, e.g. setting any values excluded in the MainForm
formset.save()
r.save()
return HttpResponseRedirect('/Home_url/')
else:
main_form = MainForm(instance=main, prefix='mains') #initial can be used in the MainForm here like normal.
formset = NestedFormset(instance=main, prefix='nesteds')
c.update({'main_form':main_form, 'formset':formset, 'realm':realm, 'main_id':main_id})
return render_to_response('App_name/Main_nesteds.html', c, context_instance=RequestContext(request))
template.html
{% if main_form %}
<form action="." method="POST">{% csrf_token %}
{{ formset.management_form }}
<table>
{{main_form.as_table}}
{% for form in formset.forms %}
<table>{{ form }}</table>
{% endfor %}
</table>
<p><input type="submit" name="submit" value="Submit" class="button"></p>
</form>
{% endif %}
The beauty of the inlineformset_factory (and modelformset_factory) is the ability to create multiple model instances from a single form. If you were to simply 'use two separate forms' the id's of the form's fields would trample each other.
The formset_factory functions know how many extra form(sets) you need (via the extra argument) and sets the id's of the fields accordingly.
inlineformset_factory creates a list of forms.
This can be used when the same form needs to be repeated at the page, for example:
Upload multiple photo's with a description.
Invite multiple members by email
Fill a calendar grid per hour
Fill a list of books for the author.
With some JavaScript code, you can add a "add another row" functionality as well.