Django: How to implement request.session in a class based view - python

I have a hard time understanding class based views in Django. At this time I try to implement a request.session in a ListView. I try to implement the following function based code from the MdM Django Tutorial in to a ListView.
def index(request):
...
# Number of visits to this view, as counted in the session variable.
num_visits = request.session.get('num_visits', 0)
request.session['num_visits'] = num_visits + 1
context = {
'num_visits': num_visits,
}
return render(request, 'index.html', context=context)
I tried the following (and a lot of other things) but to no avail:
class ListPageView(FormMixin, ListView):
template_name = 'property_list.html'
model = Property
form_class = PropertyForm
def get(self, request, *args, **kwargs):
num_visits = request.session.get('num_visits', 0)
request.session['num_visits'] = num_visits + 1
return super().get(request, *args, **kwargs)
# ... some more code here
In my template I have:
<p>You have visited this page {{ num_visits }}{% if num_visits == 1 %} time{% else %} times{% endif %}.</p>
But the template variable renders always empty.

You still need to pass it to the context, by overriding the get_context_data method [Django-doc]:
class ListPageView(FormMixin, ListView):
template_name = 'property_list.html'
model = Property
form_class = PropertyForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['num_visits'] = self.request.session['num_visits']
return context
def get(self, request, *args, **kwargs):
num_visits = request.session.get('num_visits', 0)
request.session['num_visits'] = num_visits + 1
return super().get(request, *args, **kwargs)

I understand that this is a late answer, but I ran into the same problem, and I will try to put my two cents in and maybe my answer will help newbies like me.
Your template always has access to the {{ request }} variable, so you can simply use {{request.session.key}} without defining additional context.
I can also see that you used the {% if %} conditional for the plural, but Django has a nice filter {{ value|pluralize }}, it might be more useful for that.

Related

How to filter and paginate in ListView Django

I have a problem when I want to paginate the filter that I create with django_filter, in my template it shows me the query set and filter but paginate does not work, I would like to know why this happens and if you could help me.
I'll insert snippets of my code so you can see.
This is my views.py
PD: i have all the necesary imports.
#method_decorator(staff_member_required, name='dispatch')
class EmployeeListView(ListView):
model = Employee
paginate_by = 4
def dispatch(self, request, *args, **kwargs):
if not request.user.has_perm('employee.view_employee'):
return redirect(reverse_lazy('home'))
return super(EmployeeListView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['filter'] = EmployeeFilter(self.request.GET, queryset = self.get_queryset())
return context
filters.py
import django_filters
from .models import Employee, Accident
class EmployeeFilter(django_filters.FilterSet):
class Meta:
model = Employee
fields = {
'rutEmployee' : ['startswith']
}
You should override get_queryset.This means you have to put your filter in get_queryset like this:
#method_decorator(staff_member_required, name='dispatch')
class EmployeeListView(ListView):
model = Employee
paginate_by = 4
def dispatch(self, request, *args, **kwargs):
if not request.user.has_perm('employee.view_employee'):
return redirect(reverse_lazy('home'))
return super(EmployeeListView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['filter'] = EmployeeFilter(self.request.GET, queryset = self.get_queryset())
return context
def get_queryset(self):
queryset = super().get_queryset()
return EmployeeFilter(self.request.GET, queryset=queryset).qs
and use object_list instead of filter in employee_list.html like this:
{% for employee in object_list|dictsort:"id" reversed %}
You could also try this:
(A snippet from my source code)
class ModelListView(ListView):
model = YourModel
paginate_by = 4 # Change this if you don't intend to paginate by 4
ordering = model_field_to_order_by
# variable used to know if a match was found for the search made using django_filters
no_search_result = False
def get_queryset(self, **kwargs):
search_results = YourDjangoFiltersForm(self.request.GET, self.queryset)
self.no_search_result = True if not search_results.qs else False
# Returns the default queryset if an empty queryset is returned by the django_filters
# You could as well return just the search result's queryset if you want to
return search_results.qs.distinct() or self.model.objects.all()
def get_query_string(self):
query_string = self.request.META.get("QUERY_STRING", "")
# Get all queries excluding pages from the request's meta
validated_query_string = "&".join([x for x in re.findall(
r"(\w*=\w{1,})", query_string) if not "page=" in x])
# Avoid passing the query path to template if no search result is found using the previous query
return "&" + validated_query_string.lower() if (validated_query_string and not self.no_search_result) else ""
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# Pass to template if you want to do something whenever an empty queryset is return by django_filters
context["no_search_result"] = self.no_search_result
# This is the query string which should be appended to the current page in your template for pagination, very critical
context["query_string"] = self.get_query_string()
context['filter'] = YourDjangoFiltersForm()
return context
In your html template you need to append the querystring passed to your template from the view, a sample is shown below
{% for i in page_obj.paginator.page_range %}
{% if page_obj.number == i %}
<li class="page-item active" aria-current="page">
<span class="page-link">{{ i }}<span class="sr-only">(current)</span></span>
</li>
{% elif i > page_obj.number|add:'-5' and i < page_obj.number|add:'5' %} <li class="page-item"><a
class="page-link" href="?page={{ i }}{{ query_string }}">{{ i }}</a></li>
{% endif %}
{% endfor %}

How to get the content from my database in views.py? [Django]

I am trying to print the content fields from my database,
Here's my models.py file:
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
read_time = models.TimeField(null=True, blank=True)
view_count = models.IntegerField(default=0)
Here's my views.py file:-
class PostDetailView(DetailView):
model = Post
def get_object(self):
obj = super().get_object()
obj.view_count += 1
obj.save()
return obj
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
all_texts = {
'texts': context.content
}
print(all_texts[texts])
return context
I am trying to access all the data's from the content field from my database,
But the above way is not working, Is there any way I can access all the data's from the content field, because I have to perform some action on these fields, like calculate the read_time of any content, based on the length of it.
You do not have to override the .get_queryset(…) method [Django-doc] for that, since the object is already passed to the context. You can simply render it in the template with:
{{ object.content }}
In case you really need this in the context, you can implement this as:
class PostDetailView(DetailView):
model = Post
# …
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update(
texts=self.object.content
)
return context
In case you need all post objects, you can add these to the context:
class PostDetailView(DetailView):
model = Post
# …
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update(
texts=self.object.content,
posts=Post.objects.all()
)
return context
and render these as:
{% for post in posts %}
{{ post.content }}
{% endfor %}
It might be better to work with an F expression [Django-doc] when incrementing the view counter to avoid race conditions:
class PostDetailView(DetailView):
model = Post
def get_object(self):
obj = super().get_object()
views = obj.view_count
obj.view_count = F('view_count') + 1
obj.save()
obj.view_count = views+1
return obj
Just query all objects and loop the queryset to manipulate them according to your needs like so:
def your_view(self, **kwargs):
# Get all table entries of Model Post
queryset = Post.objects.all()
# Loop each object in the queryset
for object in queryset:
# Do some logic
print(object.content)
[...]
return (..., ...)
first import models
from . models import Post
then in your function
data=Post.objects.values('content').all()
Now data have all the values in content field
data=[{'content':first_value},{'content':second_value},..like this...]

Context is not displaying in the template when using ListView

I can't understand why nothing comes out in the html template.
class TrainersListView(ListView):
model = Profile
template_name = 'trainers.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
_list = Profile.objects.filter(city__slug=self.kwargs['slug']).order_by('id')
context['trainers'] = _list
print(len(context['trainers']) --> return 5
html
{% for trainer in trainers %}
{{ trainer.id }}
{% endfor %}
Even if I take out all the instances
_list = Profile.objects.all()
still a blank result
Am I doing everything right?
You forgot
return context
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
_list = Profile.objects.filter(city__slug=self.kwargs['slug']).order_by('id')
context['trainers'] = _list
print(len(context['trainers']) --> return 5
return context
Doc: Generic display views

Render context data to generic.DetailView

How can i render data or redirect with context data to generic.DetailView.
I have model Note
class Note(models.Model):
key = models.CharField(max_length=50, primary_key=True)
text = models.TextField()
and my view is
class ShowNote(generic.DetailView):
model = Note
template_name = 'notes/show_note.html'
def get(self, request, *args, **kwargs):
try:
self.object = self.get_object()
except Http404:
# redirect here
return render(request, 'notes/index.html', {'error': 'Note doesnt exist', })
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
url(r'^show/(?P.*)/$', views.ShowNote.as_view(), name='show_note'),
The page show the key of the note and its text also there is a button which save the text if it was changed.
def save_note(request):
key = request.POST['key']
selected_note = Note.objects.get(pk=key)
selected_note.text = request.POST['text']
selected_note.save()
//back to show_note
How can i render a data {'message' : 'note was saved successfully'} in 'notes/show_note.html' but with same primary key
You can override get_context_data method for this. Put the below method in your class based view.
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
data['message'] = 'note was saved successfully'
return data
Then in the template
{{ message }}
docs will be a good help here.
Another method would be to use messages module from django.contrib.messages.
you can use something like below in your code
def get(self, request, *args, **kwargs):
.... # your code
messages.success(request, "Note was added successfully")
then in templates
{% for message in messages%}
{{ message }}
{% endfor %}

Setting GET parameters in url in Django

I want to create a listview with various optional filters. My url should be look like this:
shop/category_6/?manufacturer_id=3
And it works well when I put this url just in to the browser form. But when i try to add this option to a template it gives me NoReverseMatch. My template:
{% for manufacturer in manufacturer_list %}
<p>{{manufacturer.1}}</p>
{% endfor %}
It is my view:
class ItemListView(ListView):
model = Item
context_object_name = 'item_list'
template_name = 'shop/item_list.html'
def get_queryset(self, **kwargs):
full_item_list=Item.objects.all()
queryset=full_item_list.filter(category__id=int(self.kwargs['category_id']))
manufacturer_id = self.request.GET.get('manufacturer_id')
if manufacturer_id:
queryset=queryset.filter(manufacturer__id=int(manufacturer_id))
return queryset
def get_context_data(self, **kwargs):
context = super(ItemListView, self).get_context_data(**kwargs)
context['category']=get_object_or_404(Category, pk=self.kwargs['category_id'])
context['manufacturer_list'] = self.get_queryset(**kwargs).values_list('manufacturer__id', 'manufacturer__name').distinct().order_by('manufacturer__name')
return context
What is my mistake?
You didn't configure GET params... May be shop/category_6/?manufacturer_id=3
{{manufacturer.1 }}

Categories