Can I have multiple lists in a Django generic.ListView? - django

As a Django beginner I'm working on the the tutorial provided by django docs at https://docs.djangoproject.com/en/1.5/intro/tutorial04/
In it they demonstrate a list of multiple polls that are listed using a query by publication date. Could I add another list to be also used in the template to be used as well. Example Displaying a list of latest polls by date and another by alphabetical order on the same page.
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_poll_list'
def get_queryset(self):
"""Return the last five published polls."""
return Poll.objects.order_by('-pub_date')[:5]

Absolutely, you'll just need to write your own 'get_context_data' method that will retrieve those values and then they will be available in the view. Something like:
def get_context_data(self, *args, **kwargs):
context = super(IndexView, self).get_context_data(*args, **kwargs)
context['alphabetical_poll_list'] = Poll.objects.order_by('name')[:5]
return context
With this both {{ latest_poll_list }} and {{ alphabetical_poll_list }} would be available in your template.

Related

using CKEditor with Django and an inlineformset_factory with empty_form

When I render the empty form it is not attaching any media to it. i.e. the CKEditor is not displayed. The element looks like it is missing the css/js - it's like it doesn't get set up properly.
Note : the other sections are displayed correctly.
Where to start? Problem with Django's Empty Form method? Problem with CKEditor? Me :)
<div class="container">
<button type ="button" class="btn-info btn-lg" id="add_section">Add Section</button>
{{form.media }}
{{form|crispy }}
{{sections.media}}
<div>
{{sections.empty_form}}
</div>
<div id = 'section_management'> {{ sections.management_form }} </div>
{% for section in sections %}
{{ section|crispy }}
{% endfor %}
<button class="btn btn-info ml-2" type="submit">Update</button>
Cancel
</div>
Here's my Forms
class SectionForm(forms.ModelForm):
content = RichTextFormField()
class Meta:
model = Section
fields = ('content',)
empty_permitted=True
def __init__(self, *args, **kwargs):
print('section form called')
super().__init__(*args, **kwargs)
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ('title','category','span')
def __init__(self, *args, **kwargs):
self.is_superuser = kwargs.pop('is_superuser', None)
super().__init__(*args, **kwargs)
if self.is_superuser == False:
self.fields.pop("span")
view code
class ArticleUpdateView(LoginRequiredMixin,UserPassesTestMixin,UpdateView):
template_name = 'articles/ArticleUpdate.html'
form_class = ArticleForm
model = Article
SectionFormSet = inlineformset_factory(Article, Section, form=SectionForm, extra=0, can_delete=False, fields=('content',))
#if i always pass back at least 1 extra section form, I can grab the html for it in Jquery */
#if i do not pass back extra=0 how would i get the html in jquery for the extra form?
def test_func(self):
article = self.get_object()
if self.request.user == article.author or self.request.user.is_superuser :
return True
else:
return False
def get_context_data(self, **kwargs):
print('get context data called update view')
'''
section_form
'''
context = super().get_context_data(**kwargs)
if self.request.POST:
context['sections'] = self.SectionFormSet(self.request.POST,instance=self.object)
else:
context['sections'] = self.SectionFormSet(instance=self.object)
return context
def get_section_form(self): #we know we can access this in the template
return SectionForm()
def save_sections(self):
print('save sections called update view')
try:
context = self.get_context_data()
section_form = context['sections']
if section_form.is_valid():
# section_form.instance = self.object #if im passing instance in the factory, do I need it here to?
section_form.save()
except Exception as e:
print('failed to save section: ' + str(e))
def form_valid(self, form):
print('form valid called update view')
form.instance.author = self.request.user
response = super().form_valid(form) #save article form
self.save_sections()
return response
def get_success_url(self):
return reverse_lazy('index')
Basically, what I've done so far to overcome this problem is by accessing the form directly from the template, bypassing the inlineFormSet to get an empty form....(hope that makes sense).
I go directly to the view :
{{view.get_section_form}}
with this method in the view
def get_section_form(self): #we know we can access this in the template
return SectionForm()
I have subsequently found out I can do this in the template as well :
{{sections.media}}
{{sections.form}}
The above also passes an empty form - with the media filled in- as long as you pass the model form into the factory to start of with.
These are work-arounds for me currently, but would appreciate a proper answer as to why empty_form doesn't work properly.
My further investigation into this was basically comparing what is returned via accessing the formset to return an empty form, or using the modelForm directly.
Django docs :
empty_formĀ¶
BaseFormSet provides an additional attribute empty_form which returns a form instance with a prefix of __prefix__ for easier use in dynamic forms with JavaScript.
If you replace prefix on the generated html -- everything works. No idea why. You can replace it with anything, i.e. prefix1
at which point CKEditor starts to display the formset correctly.

Django pagination in TemplateView?

Django pagination in TemplateView?
Hello, I am trying to figure out how I can access multiple models in a Class View and paginate at the same time.
My Outcome after reading, DjangoDoc and Stackoverflow:
ListView - I simply can use paginate_by= but I can load only one Model.
TemplateView - I can load many models but can not use paginate_by=?
For example three Models: Chicken, Cows and Cats (and I want to display on my page the last 3 entries of each model). All Models have a model field called entry date.
class HomeIndex(TemplateView):
template_name = 'home.html'
paginate_by = 3 # --- something like that
def get_context_data(self, **kwargs):
context = super(HomeIndex, self).get_context_data(**kwargs)
context['chickens'] = Chicken.objects.order_by('-entry_date'')
context['cows'] = Cows.objects.order_by('-entry_date'')
context['cats'] = Cats.objects.order_by('-entry_date')
return context
Or maybe I can add something to objects.order_by('-entry_date', < pagination? >).
Thanks
Django QuerySet has built-in results slicing.
Cows.objects.order_by('-entry_date'')[offset:limit]
For the last 3 entries, offset is 0 and the limit 3
Cows.objects.order_by('-entry_date'')[0:3]
or the same can be written in a more pythonic way
Cows.objects.order_by('-entry_date'')[:3]
To get last 3 cows, cats and chicken, following code will work.
def get_context_data(self, **kwargs):
context = super(HomeIndex, self).get_context_data(**kwargs)
context['chickens'] = Chicken.objects.order_by('-entry_date')[:3]
context['cows'] = Cows.objects.order_by('-entry_date')[:3]
context['cats'] = Cats.objects.order_by('-entry_date')[:3]
return context
References:
Limiting Queries link
If you want to use ListView you still can, by chaining the querysets in get_queryset(self) and paginating that (read this answer to see the chain and sorted explained). This way you can use the default simple pagination.
from itertools import chain
from operator import attrgetter
class HomeIndex(ListView):
template_name = 'home.html'
paginate_by = 3 # --- somehting like that
def get_queryset(self):
chicken_list = Chicken.objects.all()
cow_list = Cows.objects.all()
cat_list = Cats.objects.all()
result_list = sorted(
chain(chicken_list, cow_list, cat_list),
key=attrgetter('entry_date'),
reverse=True)
return result_list
Then in your template:
{% for data in object_list %}
{{ data }}
{% endfor %}
And you can use the pagination as shown here.

Django: How can I reverse objects with DetailView?

I have used generic views.Every Episode is connected to a certain season through ForeignKey. In views.py I have these:
class SeasonList(generic.ListView):
template_name = 'episodes/episodes.html'
context_object_name = 'all_seasons'
def get_queryset(self):
return reversed(Season.objects.all())
# Here I need to sort the episodes
class SeasonDetails(generic.DetailView):
model = Season
template_name = 'episodes/season_details.html'
In the list view I used reversed() to show the latest season first. Similarly, In the detail view, I want the episodes to appear in the descending order because the latest episode should appear at the top of the page.
In my html I have accessed the episodes list using season.episode_set.all
{% for episode in season.episode_set.all %}
<!-- the tags to show the list -->
{% endfor %}
Is there any way how I can reverse the episode list?
You could order by id and use descendant - or ascendant depending on your need
Season.objects.all().order("id") # Ascendant
Season.objects.all().order("-id") # Decendant
Or reverse() will be ok to reverse the queryset whatever your filter.
Season.objects.all().reverse()
To sort the episodes, you can use something like this -
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['episodes'] = Episodes.objects.all().order_by('-date_posted')
return context

Revert objects on site instead of admin using django simple history

I have employed django simple history package on the admin site to be able to track and revert to previous versions of the object of the model. I am designing a web form that allows users to change instances of the model object using model form on django and would like to allow the users to view and revert to previous versions. Also to allow them to see what are the changes compared to the current version.
With the code below I am able to get the list of historical records on my template under histoire.
class CompanyDetailView(LoginRequiredMixin,generic.DetailView):
model = Company
def get_context_data(self, **kwargs):
context = super(CompanyDetailView, self).get_context_data(**kwargs)
company_instance = self.object
context['histoire'] = company_instance.history.all()
return context
In my template,
<p>
Previous versions:
{% for item in histoire %}
<li>
{{ item }} submitted by {{ item.history_user }} {{
item.history_object }}
</li>
{% endfor %}
</p>
But ideally I want item.history_object to be a link that users can view the previous object and be able to revert if desired.
I did something similar by adding HistoricForm to my model forms.
class MyModelForm(HistoricForm, ModelForm):
...
HistoricForm takes and extra history_id kwarg.
If history_id is provided HistoricForm swaps the ModelForm instance with the historic_instance (what your instance looked like at the time of history_id). This way your form will show the historic version of your object.
class HistoricForm(object):
def __init__(self, *args, **kwargs):
self.history_id = kwargs.pop('history_id', None)
instance = kwargs.get('instance')
if instance and self.history_id:
kwargs['instance'] = self.get_historic_instance(instance)
super(HistoricForm, self).__init__(*args, **kwargs)
def get_historic_instance(self, instance):
model = self._meta.model
queryset = getattr(model, 'history').model.objects
historic_instance = queryset.get(**{
model._meta.pk.attname: instance.pk,
'history_id': self.history_id,
}).instance
return historic_instance
If history_id is not provided the ModelForm works as usual.
You can revert by showing the historic instance and save (this way you will post your historic data).

Making Django forms return forms.instance in templates

I have complex user-defined permissions for my users. But to make thinks simpler, let's imagine there's only read-only or write permissions for each user.
I am using Django forms to edit and save model objects. And my goal is to render <input> in the Django HTML template for those users who have the permission to edit a given model instance, and a hard-coded data (without <input> tag) if the user has only read-only permission.
Currently, I have the following code in my Django template to achieve this:
{%if user.has_permission_to_edit %}
{{my_form.my_field}}
{% else %}
{{my_form.instance.my_field}}
{% endif %}
And here's my_form:
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
for field_name, field in self.fields.items():
field.widget.attrs['class'] = 'form-control input-sm'
if field.required == True:
field.widget.attrs['required'] = ''
class Meta:
model = MyModel
fields = ('my_field',)
The problem with the code in the template is that I have to use multiple {% if %}{% else %} blocks. I am relatively new to Django, and I know that there is plethora of advanced tools making Django code potentially super DRY, so I want to asked you guys, what is the most DRY method to organize what I described in the template. Specifically, is there any way to make Django forms return instance values based on some condition specified inside the form definition? Or do I have to some used-defined tag ? Or maybe some totally different architecture is used to achieve such goals?
For what I understood form your question, you want to pass the instance of the data fetched from your data-source.
from .forms import MyForm
from django.shortcuts import render
assuming you have created a forms.py file at the views.py level.
Fetching data from data-source(Detail is the model in below example)
detail_instance = Detail.objects.get(user=request.user.id)
reg_form = MyForm(instance=detail_instance or None)
# In case of edit scenario you can pass in the post params to the form as well
reg_form = MyForm(request.POST, instance=detail_instance)
# Or form with uploads
reg_form = MyForm(request.POST, request.FILES, instance=detail_instance)
Now once we have data inside our reg_form parameter we can pass it in the template
return render(request, 'applicant/register.html', { 'my_form' : reg_form})
Do whatever you wish, with you my_form variable in the template.
BASED ON THE UPDATED QUESTION
You can pass parameter to the init function of a form
reg_form = MyForm(exist = exist, some_param = param_value, instance=detail_instance or None)
After passing the param, the param can be fetched and processed in the init function of the form
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
exist = kwargs.pop('exist', None)
pk_reg = kwargs.pop('param', None)
super(MyForm, self).__init__(*args, **kwargs)
#do some stuff with you custom params here
if exist == True or pk_reg:
self.fields['username'].widget.attrs['readonly'] = True
The above approach has an alternative as well for having separate forms for separate permission and calling the appropriate form based on the user permissions.