Use get_queryset method with tables2 - django

Right now, this code works to render my template as an html table, but I would like to render it as tables2. How to do that, while keeping the "get_queryset" bit in the views.py file is giving me trouble.
urls.py
path('proforma/<int:pk>/', ProformaDetailView.as_view(), name='proforma-detail')
views.py
class ProformaDetailView(ListView):
template_name = "blog/proforma_detail.html"
context_object_name = 'proforma'
def get_queryset(self):
queryset = Proforma.objects.filter(proforma_id=self.kwargs['pk'])
return queryset
tables.py | Not currently being used
class ProformaDetailTable(tables.Table):
class Meta:
model = Proforma
template_name = "django_tables2/bootstrap.html"
fields = ('proforma_id','time_stamp','base_price','lot_cost','permit_cost','hard_cost')

A SingleTableView [readthedocs.io] is a ListView with some extra logic to render the table. You thus can implement this as:
from django_tables2 import SingleTableView
class ProformaDetailView(SingleTableView):
table_class = ProformaDetailTable
template_name = 'blog/proforma_detail.html'
context_object_name = 'proforma'
def get_queryset(self, *args, **kwargs):
return Proforma.objects.filter(
proforma_id=self.kwargs['pk']
)
In the template, you can then {% render table %}:
<!-- blog/proforma_detail.html -->
{% load render_table from django_tables2 %}
…
{% render_table table %}

Related

Django-tables2 with SingleTableMixin fails to retrieve table or queryset

Receiving error "Expected table or queryset, not str"
Trying to display queryset as table. But unable to.
Views.py
class EntriesTableView(LoginRequiredMixin, SingleTableMixin):
table_class = entryTable
table_data = Entries.objects.annotate(row_number=models.Window(
expression=RowNumber(),
order_by=[models.F('id').desc()],
)).order_by('row_number')
# model = Entries
context_table_name = "table"
paginate_by = 15
def get_template_names(self):
if self.request.htmx:
template_name = "partials/entries_list.html"
else:
template_name = "entries.html"
return template_name
relative tables.py
class entryTable(tables.Table):
class Meta:
model = Entries
template_name = 'django_tables2/bootstrap.html'
template(entries.html)
...............
{% include "partials/accomodations_list.html" %}
............
template (partials/entries_list.html) Error is highlighted in the below template on line 2.
{% load render_table from django_tables2 %}
{% render_table table %}
Django version - 4.1.3
Django-tables2 version - 2.5.1
And the model Entries do have data.

How do I pass an additional object to a Django form so I can render some fields in the template?

I have a form in Django where site visitors can submit "gear" to be included in a person's set of gear. Here's the URL to the change form:
# urls.py
path('person/<slug:slug>/gear/submit/', GearSubmitView.as_view(), name='forms/submit_gear'),
You can see that the person for whom the gear is being submitted is represented by a slug in the URL.
Here's the first part of the CreateView:
# views.py
class GearSubmitView(LoginRequiredMixin, CreateView):
"""Allows for third-party submissions for a pro's gear collection."""
template_name = 'forms/submit_gear.html'
form_class = GearSubmitForm
success_message = 'Success: Submission added.'
And the form:
# forms.py
class GearSubmitForm(forms.ModelForm):
class Meta:
model = PersonProduct
fields = ['product', 'version', 'setup_note', 'usage_start_date', 'evidence_link', 'evidence_text']
where PersonProduct is a junction table between my Person and Product models.
And the template:
# submit_gear.html
{% extends '_base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<h2 id="content-header">Submit Gear For {{ person.full_name }}</h2>
{% crispy form %}
</div>
{% endblock content %}
where you can see what I'm trying to do. I want to insert the name of the person represented by the slug in the URL in the form template.
How can I do it?
You can override get_context_data method in your views.py as mentioned in FormMixin.
class GearSubmitView(LoginRequiredMixin, CreateView):
"""Allows for third-party submissions for a pro's gear collection."""
template_name = 'forms/submit_gear.html'
form_class = GearSubmitForm
success_message = 'Success: Submission added.'
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
person = Person.objects.filter(slug=self.kwargs.get("slug")).first()
data['full_name'] = person.full_name if person else ""
return data
You can change the variable name with full_name in the html file. You can also pass whole instance if you need, I just minimize the data sending from view to html. I didn't run the code block above but it should something like this.
Here you can use the get_context_data method inside of your createview it will look like :
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['user'] = self.request.user
return context
and on your template you will add :
{{user}}
Hope it help you out !

Django modelManager - can not see instances/objects in View/listview - no erroer

I am new to django. I am trying to display with a models.Manager only the published=True instances. In the terminal no error comes. What I am doing wrong? I have a feeling it has something to do with my view.
Any help would be highly appreciated.
models.py
from django.db import models
# Create your models here.
class BlogPostManager(models.Manager):
use_for_related_fields = True
def freetosee(self, **kwargs):
return self.filter(published=True, **kwargs)
class Post(models.Model):
NOT_RATED = 0
RATED_G = 1
RATED_PG = 2
RATED_R = 3
RATINGS =(
(NOT_RATED, 'NR-Not Rated'),
(RATED_G, 'G - General Audience'),
(RATED_PG, 'Parental'),
(RATED_R, 'Restriced'),
)
title = models.CharField(max_length=140)
body = models.TextField()
published = models.BooleanField(default=False)
rating = models.IntegerField(
choices=RATINGS,
default=NOT_RATED,
)
objects = BlogPostManager()
def __str__(self):
return self.title
views.py
from django.shortcuts import render
# Create your views here.
from django.views.generic import DetailView, ListView
from .models import Post
class PostListView(ListView):
model = Post
context_object_name = 'posts'
template_name = 'postlist.html'
template
{% extends "base.html" %}
{% block content %}
{% for post in posts.objects.freetosee %}
{{ post.title }} - {{ post.body }}
{% endfor %}
{% endblock %}
urls.py
from django.urls import path, include
from django.views.generic import TemplateView
from .views import PostListView
app_name = 'blog'
urlpatterns = [
path('', TemplateView.as_view(template_name='home.html'), name='home'),
path('list/', PostListView.as_view(), name='post-list'),
]
I expect to see all models instances in the ListView with published=True
That's not how it works. posts is a queryset, it doesn't have an objects attribute. You need to call that in the view:
class PostListView(ListView):
queryset = Post.objects.freetosee()
context_object_name = 'posts'
template_name = 'postlist.html'
and then in your template just do {% for post in posts %}
Since you're using 2.1
As per django 2.0 deprecation docs use_for_related_fields = True is removed
You'll have to use base_manager_name in model Meta. like this:
class Post(models.Model):
# your fields here
objects = BlogPostManager()
class Meta:
base_manager_name = 'objects'
As suggested above in comments, when you have context_object_name set you don't have to do posts.objects.freetouse
Change your template to:
{% extends "base.html" %}
{% block content %}
{% for post in posts %}
{{ post.title }} - {{ post.body }}
{% endfor %}
{% endblock %}
from docs: the ListView has a get_queryset() method we can override. Previously, it has just been returning the value of the queryset attribute, but now we can add more logic.
This means you can do
class PostListView(ListView):
queryset = Post.objects.freetosee()
context_object_name = 'posts'
template_name = 'postlist.html'
and you can also do:
class PostListView(ListView):
context_object_name = 'posts'
template_name = 'postlist.html'
def get_queryset(self):
# this method can be used to apply as many filters as you want
# Just a quick example,
# filter_id = self.request.GET.get('filter_by')
# if filter_id:
# return Post.objects.filter(id=filter_id)
return Post.objects.freetosee()
NOTE: Please understand Views are there to handle all data and pass it to templates. You make managers to keep your custom queries methods in one place. So it's one place for one kind of thing also your template should not make any query request unless it's super necessary. templates are just to display. If you want filters in templates use template tags. That will keep your code clean and readable.

Django object not count

when user adds items, number of items should appear in the dashboard but my code does not count however it is only showing zero here what i have tried so far.
views.py
class ItemListView(LoginRequiredMixin, ListView):
model = Item
template_name = 'item/items.html'
context_object_name = 'items'
ordering = ['-created_at', '-updated_at']
def get_queryset(self):
return super(ItemListView, self).get_queryset().filter(author=self.request.user)
class ItemCountView(LoginRequiredMixin, ListView):
model = Item
template_name = 'dashboard/dash.html'
context_object_name = 'items'
def get_queryset(self):
return Item.objects.filter(author=self.request.user)
in templates dash.html
when it is {{ items.count }} it does not count but if {{ items|length }} it shows zero. Please show me where i am making mistake.
use count() in queryset
def get_queryset(self):
return Item.objects.filter(author=self.request.user).count()
Then in templates use {{ items }}
Update:
class ItemListView(LoginRequiredMixin, View):
template_name = 'item/items.html'
login_url = 'accounts/login/'
def get(self, request, *args, **kwargs):
total_item = Item.objects.filter(author=self.request.user).count()
context = {'total_item': total_item}
return render(request, self.template_name, context)
In template use {{ total_item }}
Update 2:
class ItemCountView(LoginRequiredMixin, ListView):
model = Item
template_name = 'dashboard/dash.html'
context_object_name = 'items'
def get_queryset(self):
return Item.objects.all().values('item_name').annotate(total=Count('item_name'))
Then in template use
{% for item in items %}
{{ item.item_name }} {{ item.total }}
{% endfor %}
You can define property in Item model:
class Item(models.Model):
title = models.CharField(max_length=100)
#property
def count(self):
self.__class__.objects.filter(author=self.request.user).count()
No changes in templates required. Also queryset in view does not needed anymore

Cannot resolve keyword 'name' into field using get_queryset

Sorry is this is basic but I'm new at this
I am attempting to take the captured group from a url in the template, (the primary key of the story model) and then use that to filter the correct posts from the Post datbase, which it has a one(story) to many(Post) relationship with. I based the code of the docs: https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-display/#dynamic-filtering
But when I run it, I get the error:
FieldError at /story/1/
Cannot resolve keyword 'name' into field. Choices are: body, id, title, work, work_id
My code:
#views
from django.shortcuts import get_object_or_404, render
from django.views.generic import ListView, DetailView
from . models import Post, Story
class StoryListView(ListView):
model = Story
template_name = 'home.html'
class PostListView(ListView):
template_name = 'story_overview.html'
def get_queryset(self):
self.work_id=get_object_or_404(Post, name=self.kwargs['pk'])
return Post.objects.filter(work_id=self.work_id)
#urls
from django.urls import path
from . import views
urlpatterns = [
path('', views.StoryListView.as_view(), name='home'),
path('story/<int:pk>/', views.PostListView.as_view(), name='story_overview'),
]
#templates/home.html
{% extends 'base.html' %}
{% block content %}
{% for post in object_list %}
<h2>{{ post.title }}</h2>
<p>{{ post.description }}</p>
{% endfor %}
{% endblock content %}
#models
from django.db import models
class Story(models.Model):
title = models.CharField(max_length=200)
description = models.CharField(max_length=1500, default= "Description")
def __str__(self):
return self.title
class Post(models.Model):
title = models.CharField(max_length=200, default= "Title")
work = models.ForeignKey(Story,on_delete=models.CASCADE,)
body = models.TextField()
def __str__(self):
return self.title
If you filter using work_id then you should use the integer.
def get_queryset(self):
return Post.objects.filter(work_id=self.kwargs['pk'])
If you want to fetch the Story instance then you should call get_object_or_404 with Story, and filter on the pk field instead of name:
def get_queryset(self):
self.work=get_object_or_404(Story, pk=self.kwargs['pk'])
return Post.objects.filter(work=self.work)
Note I've renamed work_id to work because it's a model instance, not an id.
You are trying to get object from Post() model using name field. Is name field present in your Post() model?
Instead of doing this
self.work_id=get_object_or_404(Post, name=self.kwargs['pk'])
Do this
self.work_id=get_object_or_404(Post, id=self.kwargs['pk'])
Hope it helps!