Django class-based ListView vs function-based list_view - django

So I'm trying to wrap my head around the following issue. The Class-based view for customer_list works like a charm, it show a list of all customers residing in the customer tabel. However, the function based view, which should do exactly the same returns empty.
Maybe there's something I'm missing but I just want to know how both views differ and when to use what.
If there's someone who could explain what I'm missing?
urls.py
from django.conf.urls import url, include
from . import views
# Function based view
url(r'^all_customers/$', views.customer_list, name='all_customers'),
# Class based view
url(r'^all_customers/$', views.customer_list.as_view(),name='all_customers'),
views.py
from django.views import generic
from django.shortcuts import render
from .models import Customer
# Function based view
def customer_list(request):
customers = Customer.objects.all()
return render(request, 'all_customers.html', {'customers': customers})
# Class based view
class CustomerList(generic.ListView):
model = Customer
template_name = 'all_customers.html'
models.py
from django.db import models
class Customer(models.Model):
first_name = models.CharField(max_length=200)
prefix = models.CharField(max_length=8, null=True, blank=True)
last_name = models.CharField(max_length=200)
And this is a section of the template
{% for customer in customer_list %}
<tr>
<td>{{customer.pk}}</td>
<td>
{% if customer.prefix %}
{{customer.first_name}} {{customer.prefix}} {{customer.last_name}}
{% else %}
{{customer.first_name}} {{customer.last_name}}
{% endif %}
</td>
</tr>
{% endfor %}

your import is wrong in urls.py
you should use from .views import customer_list

Related

How to Load multiple Model to single template in Django 2

How to load multiple models in one template in Django 2?
i've read Django Pass Multiple Models to one Template but, i'm lost in the answer and there is no specific answer there to get that view into the template,
Im trying to pass 3 models in views to template, which is Info, Slogan & Berita.
My views.py
from django.shortcuts import render
from django.http import HttpResponse
from .models import Artikel, Berita, Data, Galeri, Info, Slogan, Profil
from django.views.generic import ListView, DetailView
def home(request):
extra_context : {"info" : Info.objects.all(),
"slogan": Slogan.objects.all(),
"berita": Berita.objects.all(),
#and so on for all the desired models...
}
return render (request, 'blog/home.html')
class HomeListView(ListView):
context_object_name = 'slogan'
template_name = 'blog/home.html'
queryset = Info.objects.all()
def get_context_data(self, **kwargs):
context = super(HomeListView, self).get_context_data(**kwargs)
context['slogan'] = Slogan.objects.all()
context['berita'] = Berita.objects.all()
# And so on for more models
return context
my template home.html looks like:
{% for slogans in slogan %}
<p>{{ slogans.konten }}</p>
<p>{{ slogans.author }}</p>
{% endfor %}
my url urls.py looks like:
from blog.views import HomeListView
urlpatterns = [
url(r'^$', HomeListView.as_view(), name="home"),
]
and page source on browser shows nothing.
Im stuck on this, i found at least 3 similar result on this site but not works for me.

How do I filter tables with Django generic views?

I am trying to create a table view with pagination, sorting, and filtering, using the most common/standard/recommended approach for Django 1.6. This seems to be Generic Class-Based Views and django-tables2. I've found at least two different examples of how to add filtering, one with django-filters and crispy-forms and the other with django_filters, but neither includes a complete working example. When I follow either approach, I get stuck filling in the missing material. Using the crispy approach from Nicolas Kuttler, I have:
models.py
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
tables.py
import django_tables2 as dt2
from .models import Author
class AuthorTable(dt2.Table):
class Meta:
model = Author
If I understand correctly, PagedFilteredTableView is a generic class from which I then subclass AuthorView, as opposed to the other example, in which FilteredSingleTableView is, I think, supposed to be understood as something like, if Author was the table, AuthorFilteredSingleTableView.
views.py
from .tables import AuthorTable
from .models import Author
from django_tables2 import SingleTableView
class PagedFilteredTableView(SingleTableView):
"""
Generic class from http://kuttler.eu/post/using-django-tables2-filters-crispy-forms-together/
which should probably be in a utility file
"""
filter_class = None
formhelper_class = None
context_filter_name = 'filter'
def get_queryset(self, **kwargs):
qs = super(PagedFilteredTableView, self).get_queryset()
self.filter = self.filter_class(self.request.GET, queryset=qs)
self.filter.form.helper = self.formhelper_class()
return self.filter.qs
def get_table(self, **kwargs):
table = super(PagedFilteredTableView, self).get_table()
RequestConfig(self.request, paginate={'page': self.kwargs['page'],
"per_page": self.paginate_by}).configure(table)
return table
def get_context_data(self, **kwargs):
context = super(PagedFilteredTableView, self).get_context_data()
context[self.context_filter_name] = self.filter
return context
class AuthorTableView(PagedFilteredTableView):
model = Author
table_class = AuthorTable
paginate_by = 30
filter_class = AuthorFilter
formhelper_class = AuthorFilterFormHelper
This is, aside from the template, all of the example code from the source, and manage.py is complaining that AuthorFilter isn't defined, so I guess that goes into ... maybe filters.py?
filters.py
import django_filters as df
from .models import Author
class AuthorFilter(df.FilterSet):
class Meta:
model = Author
And, back in views.py, from .filters import AuthorFilter.
And now AuthorFilterFormHelper isn't defined, and it's not clear if that's something I should explicitly define (how?), as implied by the line formhelper_class = FooFilterFormHelper, or if that should actually be done automatically somehow, as implied by self.filter.form.helper = self.formhelper_class(). And we still haven't gotten to urls.py or the template. Please help fill in the blanks, or indicate a better path to go down to add filtering to a generic class-based view.
With trial and error and some suggestions from Nicolas Kuttler, I was able to get his example working. I butchered a little bit of his example but this does seem to be close to the least amount of code necessary in Django to have a generic class-based list view page with sorting, filtering (and thus search), and pagination, and I don't think it violates (too m)any Django coding practices. Here is all of the code required:
models.py (no changes from the question)
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
And a slight change to the tables.py in the question:
import django_tables2 as dt2
from .models import Author
class AuthorTable(dt2.Table):
class Meta:
model = Author
attrs = {"class": "paleblue"}
per_page = 30
filters.py (no changes from the question)
import django_filters as df
from .models import Author
class AuthorFilter(df.FilterSet):
class Meta:
model = Author
forms.py. I couldn't figure out how/where to get the form submit button out of django_filters in combination with everything else, so this code suppresses the form wrapper tags from crispy, and then we provide that HTML in the template, which is probably the kludgiest part of this.
from django import forms
from .models import Author
from crispy_forms.helper import FormHelper
class AuthorListFormHelper(FormHelper):
model = Author
form_tag = False
I moved the helper function out of views.py to a separate file and had to remove the pagination code to prevent an error (though pagination still works). So,
utils.py
from django_tables2 import SingleTableView
from django_tables2.config import RequestConfig
class PagedFilteredTableView(SingleTableView):
filter_class = None
formhelper_class = None
context_filter_name = 'filter'
def get_queryset(self, **kwargs):
qs = super(PagedFilteredTableView, self).get_queryset()
self.filter = self.filter_class(self.request.GET, queryset=qs)
self.filter.form.helper = self.formhelper_class()
return self.filter.qs
def get_context_data(self, **kwargs):
context = super(PagedFilteredTableView, self).get_context_data()
context[self.context_filter_name] = self.filter
return context
Instead of the views.py in the question, this views.py:
from .models import Author
from .tables import AuthorTable
from .filters import AuthorListFilter
from .forms import AuthorListFormHelper
from utils import PagedFilteredTableView
class AuthorList(PagedFilteredTableView):
model = Author
table_class = AuthorTable
filter_class = AuthorListFilter
formhelper_class = AuthorListFormHelper
And this template:
author_list.html (the name is not explicitly specified anywhere because it's implied by the model in the view, I think)
{% extends "base.html" %}
{% load render_table from django_tables2 %}
{% load crispy_forms_tags %}
{% block content %}
{% render_table table %}
<hr/>
<form action="" method="get">
{% crispy filter.form filter.form.helper %}
<input type="submit" value="Filter"/>
</form>
{% endblock content %}
And, for completeness, a line in urls.py:
...
url(r'^$', views.AuthorTableList.as_view(), name='author_table'),
...
And you must have the packages django-tables2, django-filters, and crispy-forms installed and configured as per their instructions. The one thing that tripped me up was that I somehow, on the first attempt, missed that I needed this in settings.py:
CRISPY_TEMPLATE_PACK = 'bootstrap'
All of this is for Django 1.6.
Your answer help me, so i edited your code to include the submit button from crispy-forms
forms.py. I couldn't figure out how/where to get the form submit button out of django_filters in combination with everything else, so this code suppresses the form wrapper tags from crispy, and then we provide that HTML in the template, which is probably the kludgiest part of this.
forms.py (UPDATED)
from django import forms
from .models import Author
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, ButtonHolder, Submit
class AuthorListFormHelper(FormHelper):
model = Author
form_tag = False
# Adding a Filter Button
layout = Layout('name', ButtonHolder(
Submit('submit', 'Filter', css_class='button white right')
))
author-list.html (UPDATED)
{% extends "base.html" %}
{% load render_table from django_tables2 %}
{% load crispy_forms_tags %}
{% block content %}
{% render_table table %}
<hr/>
<form action="" method="get">
{% crispy filter.form filter.form.helper %}
</form>
{% endblock content %}

Django Class Based View CreateView url resolution

I am fairly new to CBV and am trying to make sense of it. I copied the following example from the django doc page:
https://docs.djangoproject.com/en/dev/topics/class-based-views/generic-editing/
models.py
from django.core.urlresolvers import reverse
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=200)
def get_absolute_url(self):
return reverse('author-detail', kwargs={'pk': self.pk})
views.py
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.core.urlresolvers import reverse_lazy
from myapp.models import Author
class AuthorCreate(CreateView):
model = Author
fields = ['name']
class AuthorUpdate(UpdateView):
model = Author
fields = ['name']
class AuthorDelete(DeleteView):
model = Author
success_url = reverse_lazy('author-list')
urls.py
from django.conf.urls import patterns, url
from myapp.views import AuthorCreate, AuthorUpdate, AuthorDelete
urlpatterns = patterns('',
# ...
url(r'author/add/$', AuthorCreate.as_view(), name='author_add'),
url(r'author/(?P<pk>\d+)/$', AuthorUpdate.as_view(), name='author_update'),
url(r'author/(?P<pk>\d+)/delete/$', AuthorDelete.as_view(), name='author_delete'),
)
At author/add/ I indeed get the form, but when I enter the string I get the following error:
Reverse for 'author-detail' with arguments '()' and keyword arguments '{'pk': 3}' not found.
It seems like the new entry has been added to the database, but it could not resolve the URL for the next view?
So I am puzzled, what is this get_absolute_url() object's method supposed to do, how does it work (I could not grasp it from the django doc) and how do I fix the issue?
Thanks.
EDIT 1: added the template:
author_form.html:
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Create" />
</form>
By default, when a new model is created django will redirect you to a models absolute url as returned by the get_absolute_url method. In your example you would need to add a url with the name author-detail that accepts a pk keyword argument.
urlpatterns = patterns('',
# ...
url(r'author/(?P<pk>\d+)/$', AuthorDetail.as_view(), name='author-detail'),
)
Note the name of the url matches the name of the view in the get_absolute_url method.
Use it in your templates:
{% for author in authors %}
{{author.name}}
{% endfor %}

How to show a category list made by generic views in multiple pages?

I'm trying to build my own Blog app with Django 1.6. I've generated a category list by generic views like this:
urls.py
url(r'^categories/?$', views.ListView.as_view(model=Category), name='categories'),
category_list.html
<h3>Categories</h3>
{% for category in object_list %}
<ul>
<li>{{ category.title }}</li>
</ul>
{% endfor %}
all categories are now listed at /categories.
My problem is when I add it to base.html or index.html file, output changes to article.title not category.title How can I add this Category list to other pages such as Index or Article?
Here's my complete views.py file:
views.py
from django.shortcuts import get_object_or_404, render
from django.views.generic import ListView, DetailView
from blog.models import Article, Category
class IndexView(ListView):
template_name = 'blog/index.html'
context_object_name = 'latest_article_list'
def get_queryset(self):
return Article.objects.order_by('-pub_date')[:10]
class ArticleView(DetailView):
model = Article
template_name = 'blog/article.html'
It renders article.title because object_list points into article view context, you cannot include isolated view into another view.
I think the cleanest way would be to create a mixin class for categories context and add it to each view, that needs to render it.
Something like this:
class CategoryMixin(object):
def get_categories(self):
return Category.objects.all()
def get_context_data(self, **kwargs):
context = super(CategoryMixin, self).get_context_data(**kwargs)
context['categories'] = self.get_categories()
return context
then add it to the view class:
class IndexView(CategoryMixin, ListView):
...
and also include category_list.html inside each template, passing the context variable (this way you have isolated variable names):
{% include "category_list.html" with object_list=categories only %}

How to render a POST and make it show up on another page

I'm trying to create a marketplace website similar to craigslist.
I created a form according to the Django tutorial "Working with forms", but I don't know how to render information I got from the POST forms.
I want to make information(subject,price...etc) that I got from POST show up on another page like this. http://bakersfield.craigslist.org/atq/3375938126.html and, I want the "Subject"(please look at form.py) of this product(eg.1960 French Chair) to show up on another page like this. http://bakersfield.craigslist.org/ata/ }
Can I get some advice to handle submitted information?
Here's present codes. I'll appreciate all your answers and helps.
<-! Here's my codes -->
◆forms.py
from django import forms
class SellForm(forms.Form):
subject = forms.CharField(max_length=100)
price = forms.CharField(max_length=100)
condition = forms.CharField(max_length=100)
email = forms.EmailField()
body = forms.TextField()
◆views.py
from django.shortcuts import render, render_to_response
from django.http import HttpResponseRedirect
from site1.forms import SellForm
def sell(request):
if request.method =="POST":
form =SellForm(request.POST)
if form.is_valid():
subject = form.cleaned_data['subject']
price = form.cleaned_data['price']
condition = form.cleaned_data['condition']
email = form.cleaned_data['email']
body = form.cleaned_data['body']
return HttpResponseRedirect('/books/')
else:
form=SellForm()
render(request, 'sell.html',{'form':form,})
◆urls.py
from django.conf.urls import patterns, include, url
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^sechand/$','site1.views.sell'),
url(r'^admin/', include(admin.site.urls)),
)
◆sell.html
<form action = "/sell/" method = "post">{% csrf_token%}
{{ form.as_p }}
<input type = "submit" value="Submit" />
</form>
I assume you have a Sell model/table in your db(where you store the users' "sells"), otherwise it wouldn't make any sense. This means you can save yourself some time and use a ModelForm,
instead of a simple Form. A model form takes a database table and produces an html form for it.
forms.py
from django.forms import ModelForm
from yourapp.models import Sell
class SellForm(ModelForm):
class Meta:
model = Sell
In your views.py you need one more view that displays the Sells that your users have
posted for others to see. You also need an html template that this view will render with context about each Sell.
sell_display.html
{% extends 'some_base_template_of_your_site.html' %}
{% block content %}
<div id="sell">
<h3> {{ sell.subject }}</h3>
<p> {{ sell.condition }}</p>
<p> {{ sell.body }}</p>
<!-- the rest of the fields.. -->
</div>
{% endblock %}
We also need a new url entry for the displaying of a specific Sell
urls.py
from django.conf.urls import patterns, include, url
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
# Changed `sell` view to `sell_create`
url(r'^sechand/$','site1.views.sell_create'),
# We also add the detail displaying view of a Sell here
url(r'^sechand/(\d+)/$','site1.views.sell_detail'),
url(r'^admin/', include(admin.site.urls)),
)
views.py
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response, get_object_or_404
from yourapp.models import Sell
from yourapp.forms import SellForm
def sell_detail(request, pk):
sell = get_object_or_404(Sell, pk=int(pk))
return render_to_response('sell_display.html', {'sell':sell})
def sell_create(request):
context = {}
if request.method == 'POST':
form = SellForm(request.POST)
if form.is_valid():
# The benefit of the ModelForm is that it knows how to create an instance of its underlying Model on your database.
new_sell = form.save() # ModelForm.save() return the newly created Sell.
# We immediately redirect the user to the new Sell's display page
return HttpResponseRedict('/sechand/%d/' % new_sell.pk)
else:
form = SellForm() # On GET request, instantiate an empty form to fill in.
context['form'] = form
return render_to_response('sell.html', context)
This is enough to get you going I think. There are patterns to make these things more modular and better, but I don't want to flood you with too much information, since you are a django beginner.