django migrating to 1.6 of an old app: ListView - django

I am migrating our old app to django 1.6.
Now, some views have been programmed this way:
from django.views.generic.list_detail import object_list
#render_to("items/index.html")
def index(request):
profile = request.user.get_profile()
args = clean_url_encode(request.GET.copy().urlencode())
context = {
'is_dashboard': True,
'body_id': 'dashboard',
'object_list': None,
'args':args,
'show_in_process':False
}
return context
I know that I need to use the new ListView now, but the examples and docs don't seem to tell me this particular case I have: the object_list being passed in the context.
How can I adapt this code to use the new class based generic view? Can I also just use ListView.asView() instead of 'object_list':None ?

Why are you using ListView if you don't have a object list? I think TemplateView should do the job. You just need to override get_context_data and provide custom context
class IndexView(TemplateView):
template_name = 'items/index.html'
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
profile = self.request.user.get_profile()
args = clean_url_encode(self.request.GET.copy().urlencode())
context.update({
'is_dashboard': True,
'body_id': 'dashboard',
'object_list': None,
'args':args,
'show_in_process':False
})
return context

Related

Creating Template Links for Pagination in Generic CBV

I'm just starting with Django so I have a lot of questions. I've built a Mixin to create the paginator template links for "next page" and previous page in a GCBV (ListView):
class PageLinksMixin:
page_kwarg = 'page'
def _page_urls(self, page_number):
return "?{pkw}={n}".format(pkw=self.page_kwarg, n=page_number)
def previous_page(self, page):
if page.has_previous():
return self._page_urls(page.previous_page_number())
return None
def next_page(self, page):
if page.has_next():
return self._page_urls(page.next_page_number())
return None
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
page = context.get('page_obj')
if page is not None:
context.update({'previous_page_url': self.previous_page(page),
'next_page_url': self.next_page(page)})
return context
Everything seems to work. But I can simply not see where some of the inputs for functions were created. Especially "page", "page_number".
With a CBV it would be necessary to declare:
page = paginator.page(page_number)
But in the GCBV this is apparently unnecessary which means the attribute is created somewhere and later inherited.
For the ListView there is a function def paginate_queryset but in there page is only declared in a local scope not a global scope. Can somebody explain this to me? I'm really confused and trying to figure this out for a while now.
This is the View:
class TagList(PageLinksMixin, ListView):
template_name = 'organizer/tag_list.html'
paginate_by = 5
model = Tag
The Django generic ListView has a paginator object, which is added to the context through self.get_context_data(). Here's the Django source code:
def get_context_data(self, **kwargs):
"""
Get the context for this view.
"""
queryset = kwargs.pop('object_list', self.object_list)
page_size = self.get_paginate_by(queryset)
context_object_name = self.get_context_object_name(queryset)
if page_size:
paginator, page, queryset, is_paginated = self.paginate_queryset(queryset, page_size)
context = {
'paginator': paginator,
'page_obj': page,
'is_paginated': is_paginated,
'object_list': queryset
}
else:
context = {
'paginator': None,
'page_obj': None,
'is_paginated': False,
'object_list': queryset
}
if context_object_name is not None:
context[context_object_name] = queryset
context.update(kwargs)
return super(MultipleObjectMixin, self).get_context_data(**context)
In your mixin, you first call super().get_context_data(), which populates the context with the paginator and a page object, which you set to page:
page = context.get('page_obj')
Finally you call self.previous_page(page) to fetch the actual URL and add it to the context. Nothing magic.
You could also have fetched the page_number from the kwargs and the page from the paginator instance but since it's already done for you, the page_obj in the context is the easiest way.

How to use mixins properly

Please, help to understand, why the following doesn't work for me.
So, I need display information on a page from logged in user.
In order not to retype code in every view I decided to create a mixin.
class MyMixin(object):
def my_view(self):
args = {}
args['username'] = auth.get_user(request).username
args['first_name'] = auth.get_user(request).first_name
args['last_name'] = auth.get_user(request).last_name
return args
class someview (TemplateView, LoginRequiredMixin, MyMixin):
template_name = 'index.html
But this doesn't show anything in a template.
{{ first_name }}
There are at least two ways of getting these "context variables" into your template:
Your TemplateView already includes ContextMixin. So you could simply override ContextMixin's get_context_data method for that view, like this:
class someview (TemplateView, LoginRequiredMixin):
template_name = 'index.html
def get_context_data(self, **kwargs):
context = super(someview, self).get_context_data(**kwargs)
context['username'] = self.request.user.username
context['first_name'] = self.request.user.first_name
context['last_name'] = self.request.user.last_name
return context
It seems that what you're actually looking for is a more DRY method, not necessarily a mixin. In that case, you should use write your own context_processor:
context_processors.py:
def extra_context(request):
args = {}
args['username'] = auth.get_user(request).username
args['first_name'] = auth.get_user(request).first_name
args['last_name'] = auth.get_user(request).last_name
return args
settings.py:
TEMPLATE_CONTEXT_PROCESSORS += (your_app.context_processors.extra_context,)
This second method will add these three context variables to every template in your app.

Django - Combining a datatable view and form view

I am trying to put a table from datatables and a crispy form into one view so that I can display it on a single page in django. I am completely stuck on how to combine them as i'm still a newbie.
Any help is greatly appreciated!!
Views.py ------------- Datatable below
class CustomerTable(XEditableDatatableView):
template_name = "customers.html"
model = Customer
datatable_options = {
'columns': [
("Title", 'Title'),
("Name", 'PoC', helpers.make_xeditable),
("Email", 'PoCEmail', helpers.make_xeditable),
("Location", 'Location', helpers.make_xeditable),
("DateAdded", 'DateAdded', helpers.make_xeditable),
],
'hidden_columns': ['ID'],
}
-----------Crispy Form below
def CustomerView(request):
form = CustomersForm(request.POST or None)
success = False
if request.method == 'POST':
if form.is_valid():
form.save()
form = CustomersForm()
success = True
if request.POST.get('delete'):
obj.delete()
customer_form = CustomersForm()
return render(request, 'customers.html', {'customer_form': customer_form})
urls.py ----------------
urlpatterns = patterns('',
url(r'^$', views.CustomerTable.as_view(), name='customertable'),
url(r'^$', 'ISL.views.CustomerView', name='customersform'),
)
Thanks for any help!
W
You can try to use get_context_data() method of your class like this:
def get_context_data(self, **kwargs):
context = super(CustomerTable, self).get_context_data(**kwargs)
# do your magic here <<<
# then
context['customer_form'] = customer_form
return context
First you are calling ancestor's get_context_data method via super built-in function, then you can append to context everything you want, in your example it is custom form. After this, you can access 'custom_form' in your template.
Unfortunately, i cant test it at the moment, but this method must be appended in ancestor by ContextMixin (django doc about this method: https://docs.djangoproject.com/en/1.8/ref/class-based-views/mixins-simple/).

How to render multiple objects using class based views

I try to render multiple objects using class based views but I get an error.
Here are my codes:
class AssociatedList(WizardRequiredMixin, TemplateView):
template_name = "profile/associated_accounts.html"
def get_context_data(self, **kwargs):
context = super(AssociatedList, self).get_context_data(**context)
all_envelopes = Envelope.objects.filter(
user=request.user).exclude_unallocate()
free_limit = account_limit(request, 15, all_envelopes)
facebook = FacebookProfile.user_profiles(request.user)
google = GoogleProfile.user_profiles(request.user)
twitter = TwitterProfile.user_profiles(request.user)
context.update = ({
'facebook': facebook,
'google': google,
'twitter': twitter,
'free_limit': free_limit,
})
return context
Error:
local variable 'context' referenced before assignment
I've always overridden get_context_data by calling super at the beginning of the function and then appending context -
def get_context_data(self, *args, **kwargs):
context = super(AssociatedList, self).get_context_data(*args, **kwargs)
all_envelopes = Envelope.objects.filter(
user=self.request.user).exclude_unallocate()
free_limit = account_limit(self.request, 15, all_envelopes),
facebook = FacebookProfile.user_profiles(self.request.user),
google = GoogleProfile.user_profiles(self.request.user),
twitter = TwitterProfile.user_profiles(self.request.user),
context.update({
'facebook': facebook,
'google': google,
'twitter': twitter,
'free_limit': free_limit,
})
return context
This is the pattern used in the docs here.
UPDATE
The error you've just added suggests an error with your class. It sounds like you need to define either a queryset attribute or a model attribute.
The ListView class that you're inheriting from requires you to either define the model that the View returns (ie YourModel.objects.all()). Or else the specific queryset to be returned (eg YourModel.objects.filter(your_field=some_variable)).
Because this is a ListView, you need to tell it what you are going to list with either a model or queryset. You don't want to use a ListView in this case since you are overriding get_context_data so you should probably use a TemplateView or something similar.
Try something like this:
class AssociatedList(WizardRequiredMixin, ListView):
template_name = "profile/associated_accounts.html"
model = Envelope
def get_queryset(self):
return Envelope.objects.filter(user=self.request.user).exclude_unallocate()
def get_context_data(self, **kwargs):
context = super(AssociatedList, self).get_context_data(**kwargs)
context.update({
'facebook': FacebookProfile.user_profiles(self.request.user),
'google': GoogleProfile.user_profiles(self.request.user),
'twitter': TwitterProfile.user_profiles(self.request.user),
'free_limit': account_limit(self.request, 15, context['envelope_list']),
})
return context
You don't need model having queryset, but it is good practice to define it.
In template use object_list or envelope_list instead of all_envelopes and you should be good to go.
P.S. http://ccbv.co.uk/ good source of knowledge about CBV.

django: how do I migrate this view to class-based generic view equivalent?

I have trouble transforming the following code into the new django 1.3 class-based generic view format. Specifically, I don't understand how I can pass the 'extra_context' to the class based view. Can someone help me transform this code to the new notation? or post a link to a good example? I've read the docs but the example is very flimsy.
def return_event_list_object(request, username, queryset, entries_per_page, param1, param2):
...
...
return object_list(request, queryset = queryset,
template_name = 'myapp/list_events.html',
paginate_by = int(entries_per_page),
template_object_name = 'event',
extra_context = {'param1': param1,
'param2': param2, } )
I appreciate your input!
The extra_context section of the docs explains how to add items to the context:
Class-based views don't provide an extra_context argument. Instead, you subclass the view, overriding get_context_data(). For example:
In your case, try:
class MyListView(ListView):
def get_context_data(self, **kwargs):
context = super(MyListView, self).get_context_data(**kwargs)
context.update({
'param1': kwargs['param2'],
'param2': kwargs['param1']
})
return context