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.
Related
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 %}
I'm new in Django 3.0 and I'm lost in this easy question, please help me.
I have 2 models:
class Product(models.Model):
fields...
class ProductType(models.Model):
product = models.ForeignKey(Product, related_name='product_types', on_delete=models.CASCADE)
color = models.Charfield(...)
In my template, I would like to show all the related product types and their fields to a specific product:
...
{% for product in products %}
{{ product.??? }}
{% endfor %}
Here is my view:
class ProductsView(ListView):
collection = None
model = Product
paginate_by = 6
template_name = 'shop/product/list.html'
context_object_name = 'products'
def get_queryset(self):
products = Product.objects.filter(available=True)
collection_name = self.kwargs['collection_name'] if 'collection_name' in self.kwargs else None
if collection_name:
collection = get_object_or_404(Collection, name=collection_name)
products = products.filter(collection=collection)
return products
def get_context_data(self):
context = super().get_context_data(**self.kwargs)
context['notification'] = Notification.objects.all()
if 'collection_name' in self.kwargs:
context['collection'] = get_object_or_404(Collection, name=self.kwargs['collection_name'])
context['collection_name'] = self.kwargs['collection_name']
context['collections'] = Collection.objects.all()
return context
Thank you
You access the related ProductTypes through a manager that has as name the value you specify as related_name=… [Django-doc], so in this case:
{% for product in products %}
{% for type in product.product_types.all %}
{{ type.color }}
{% endfor %}
{% endfor %}
To boost efficiency, you can fetch all related ProductTypes for the elements in the queryset with .prefetch_related(…) [Django-doc]:
class ProductsView(ListView):
# …
def get_queryset(self):
products = Product.objects.prefetch_related('product_types').filter(available=True)
collection_name = self.kwargs['collection_name'] if 'collection_name' in self.kwargs else None
if collection_name:
collection = get_object_or_404(Collection, name=collection_name)
products = products.filter(collection=collection)
return products
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 !
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.
I want to use prefetch_related with Django's DetailView.
Model:
class Customer(models.Model):
name = models.CharField(
verbose_name='customer name',
max_length=100
)
# Other fields
class Packet(models.Model):
customer = models.ForeignKey(
Customer
)
# Other fields
class Credit(models.Model) :
customer = models.ForeignKey(
Customer
)
# Other fields
View:
class CustomerDetailsView(LoginRequiredMixin, DetailView):
model = Customer
http_method_names = ['get']
template_name = 'detail_templates/customer_details.html'
Templates:
{% for p in object.packet_set %}
{{ do something }}
{% endif %}
{% for p in object.credit_set %}
{{ do something }}
{% endif %}
Tried:
class CustomerDetailsView(LoginRequiredMixin, DetailView):
model = Customer
http_method_names = ['get']
template_name = 'detail_templates/customer_details.html'
def get_queryset(self):
queryset = super(CustomerDetailsView, self).get_queryset()
pk = self.kwargs.get(self.pk_url_kwarg, None)
return queryset.filter(id=pk).prefetch_related('packet_set', 'credit_set')
debug_toolbar shows no improvement.
How do I prefetch_related packet and credit
There is no sense to use prefetch_related() in the DetailView. This view loads the single master object with get() while prefetch_related() is usable for loading related objects of multiple master objects.