class django.views.generic.list.ListView¶
A page representing a list of objects.
While this view is executing, self.object_list will contain the list of objects
(usually, but not necessarily a queryset) that the view is operating upon.
Example views.py:
from django.utils import timezone
from django.views.generic.list import ListView
from articles.models import Article
class ArticleListView(ListView):
model = Article
paginate_by = 100 # if pagination is desired
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['now'] = timezone.now()
return context
What exactly is object_list?
Is it inherited?
DO you have to define it? I wish the documentations would have clearly explanations rather than having to look through modules and modules and still not being able to find it, thanks guys
ListView inherits BaseListView in which you can see that
self.object_list = self.get_queryset()
If you go down further into MultipleObjectMixin which is inherited by BaseListView you would see exactly how get_queryset is implemented for ListView
Start with understanding View flow, methods and attributes and build up to understanding other views
Related
I'm trying to use two views list(post_list and classification_list) in a template called blogpage. here's what I've done to solve the problem, however it didn't work:
class GenViewList(ListView):
model = Posting,Classification
template_name = 'Blog/blogpage.html'
def get_context_data(self, **kwargs):
context=super(BlogViewList,self).get_context_data(**kwargs)
context['latest_post_list']=Posting.objects.filter().order_by('-id')[:30]
context['classification_list']=Classification.objects.all().order_by('id')
return context
Any help will be appreciated!
You can just make it a TemplateView
from django.views.generic import TemplateView
class GenViewList(TemplateView):
template_name = 'Blog/blogpage.html'
def get_context_data(self, **kwargs):
context=super(BlogViewList,self).get_context_data(**kwargs)
context['latest_post_list']=Posting.objects.filter().order_by('-id')[:30]
context['classification_list']=Classification.objects.all().order_by('id')
return context
ListView doesn't work with 2 different models. You can provide your get_queryset, but in the way you construct your get_context seems you need something different like TemplateView
I was just helped by the answer found here: global name 'request' is not defined: overriding form_valid but that left me asking another question:
When do you use just request, and when do you have to use self.request - and why? The example I see in the docs https://docs.djangoproject.com/en/1.9/topics/class-based-views/generic-display/#dynamic-filtering don't use either request or self.request in the examples. Now maybe that's because it is a class based view, but I don't think I'm alone in getting the syntax and usage of cbv v generic v functional mixed up.
UPDATE
My code
from django.shortcuts import render
from django.views.generic.list import ListView
from django.views.generic.detail import DetailView
from essell.models import Code
class CodeListView(ListView):
model = Code
template_name='statute.html'
def get_context_data(self, **kwargs):
context = super(CodeListView, self).get_context_data(**kwargs)
return context
from the docs
from django.views.generic.list import ListView
from django.utils import timezone
from articles.models import Article
class ArticleListView(ListView):
model = Article
def get_context_data(self, **kwargs):
context = super(ArticleListView, self).get_context_data(**kwargs)
context['now'] = timezone.now()
return context
MY CURRENT ERRORS
TypeError at /us/constitution
cannot convert dictionary update sequence element #0 to a sequence
class CodeListView(ListView):
model = Code
template_name='statute.html'
def get_context_data(self, **kwargs):
#context = super(CodeListView, self).get_context_data(**kwargs)
context = {'object_list':object_list}
return context
NameError at /us/constitution
global name 'object_list' is not defined
from the docs: https://docs.djangoproject.com/es/1.9/topics/class-based-views/generic-display/
"This template will be rendered against a context containing a variable called object_list that contains all the publisher objects. A very simple template might look like the following:"
class CodeListView(ListView):
model = Code
template_name='statute.html'
def get_context_data(self, **kwargs):
context = super(CodeListView, self).get_context_data(**kwargs)
context = {'object_list':object_list}
return context
global name 'object_list' is not defined
Like anything else in Python, any variable you reference must already exist in your current context.
Originally, Django views were simply functions that took as their first argument the request variable. Presumably, if you're writing old-style views, whatever you define as your first variable (e.g. def view_name(request, ...)) will be where the request is provided.
If you're using class-based views, then your views are member functions, so by convention their first argument will be self, e.g. def view_name(self, request, ...). In this case, refer to the Django documentation regarding which arguments are provided to which functions depending on which view you're subclassing.
In my app I need to create products in a shop. So I have a model Shop and a model Product. I can see details about my shop in a DetailView ShopDetail. Now I need a CreateView in order to create products, but the url should be /shops/shop-id/products/create/, so I create products inside the shop. I guess it's something like
class ProductCreate(SingleObjectMixin, CreateView):
model = Product
def get_object(self, queryset=None):
return Shop.objects.get(id = self.kwargs['shop_id'])
Am I on the right track? :-D
No, you're not on the right track: the object returned by get_object should be a instance of the model; in fact, if you override get_object the model attribute becomes irrelevant.
There are a few approaches to this problem, but I would myself probably got for a single DetailView (with the Shop details), and add a form for Product to the template via the get_context_data method. The form's action attribute would, instead of being empty, point to the url to a separate CreateView which would handle the Product creation.
Alternatively you could simply display the Shop details through the get_context_data, which is simpler but mixes concerns (as the DetailView for shop is defined as a CreateView for Product).
I think you need:
from django.shortcuts import get_object_or_404
class ProductCreate(CreateView):
"""Creates a Product for a Shop."""
model = Product
def form_valid(self, form):
"""Associate the Shop with the new Product before saving."""
form.instance.shop = self.shop
return super(CustomCreateView, self).form_valid(form)
def dispatch(self, *args, **kwargs):
"""Ensure the Shop exists before creating a new Product."""
self.shop = get_object_or_404(Shop, pk=kwargs['shop_id'])
return super(ProductCreate, self).dispatch(*args, **kwargs)
def get_context_data(self, **kwargs):
"""Add current shop to the context, so we can show it on the page."""
context = super(ProductCreate, self).get_context_data(**kwargs)
context['shop'] = self.shop
return context
I hope it helps! :) You may wish to have a look at what the super-methods do.
(Disclaimer: Shameless self promotion.)
I want to create a "method_splitter" equivalent using class-based views in order to remove some hard-coding in my URL confs.
I would like to have the following URL's:
ListView: http://mysite.com/<model_name>/
DetailView: http://mysite.com/<model_name>/<field_value>/
where the query for the ListView would be:
<model_name>.objects.all()
and the queryset for the DetailView would be:
<model_name>.objects.get(<field>=<field_Value>)
Currently, my views work as a result of some hardcoding in the url conf, but I would like to find an elegant solution that can scale.
My solution does not give a 404, but displays nothing:
views.py
class ListOrDetailView(View):
def __init__(self, **kwargs):
for key, value in kwargs.iteritems():
setattr(self, key, value)
try: #If/Else instead?
def goToDetailView(self, **kwargs):
m = get_list_or_404(self.kwargs['model']) #Is this even needed?
return DetailView(model=self.kwargs['model'], slug=self.kwargs['slug'], template_name='detail.html', context_object_name='object')
except: #If/Else instead?
def goToListView(self, **kwargs):
q = get_object_or_404(self.kwargs['model'], slug=self.kwargs['slug']) #Is this even needed?
return ListView(model=self.kwargs['model'], template_name='list.html', context_object_name='object_list',)
urls.py of MyApp
url(r'^(?P<model>[\w]+)/?(?P<slug>[-_\w]+)/$', ListOrDetailView.as_view()),
As limelights said, this is a horrible design pattern; the whole point of Django is separation of models and views. If you fear that you might have to write a lot of repetitive code to cover all your different models, trust me that it's not much of an issue with class-based views. Essentially, you need to write this for each of your models you wish to expose like this:
Urls.py:
urlpatterns = patterns('',
url(r'^my_model/$', MyModelListView.as_view(), name="mymodel_list"),
url(r'^my_model/(?P<field_value>\w+)/$', MyModelDetailView.as_view(), name="mymodel_detail"),
)
views.py:
from django.views.generic import ListView, DetailView
class MyModelListView(ListView):
model = MyModel
class MyModelDetailView(DetailView):
model = MyModel
def get_queryset(self):
field_value = self.kwargs.get("field_value")
return self.model.objects.filter(field=field_value)
I constantly see myself having to add the same extra variable to the context of many of my views.
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(MyListView, self).get_context_data(**kwargs)
# Add in the house
context['house'] = self.get_object().house
return context
As I don't like repeating myself, I thought I could create a new class extending the view, and then I could base all my views on the new extended view class. The thing is, there are 4 classes of views I use: CreateView, UpdateView, ListView, and DeleteView. Do I really have to create a new class for each one of them?
Isn't there something like a Django "base" view class? Maybe a smarter way to do this?
Create a Mixin:
from django.views.generic.base import ContextMixin
class HouseMixin(ContextMixin):
def get_house(self):
# Get the house somehow
return house
def get_context_data(self, **kwargs):
ctx = super(HouseMixin, self).get_context_data(**kwargs)
ctx['house'] = self.get_house()
return ctx
Then in your other classes you'd use multiple inheritance:
class HouseEditView(HouseMixin, UpdateView):
pass
class HouseListView(HouseMixin, ListView):
pass
and so on, then all these views will have house in the context.