I want to add custom table to my model. I explored this question and found out that I should change change_list_results.html like this:
{% load render_table from django_tables2 %}
{% render_table table %}
Some details.
I implemented table as:
class MyModelTable(tables.Table):
class Meta:
model = MyModel
Then I plugged it to TableView:
class MyModelTableView(tables.SingleTableView):
table_class = MyModelTable
queryset = MyModel.objects.using('my_database').all()
template_name = "admin/change_list_results.html"
After that I redefined url in the head urls.py (that is the error):
urlpatterns = [
path(
'admin/crm/my_model/', MyModelTableView.as_view()
),
path("admin/", admin.site.urls),
]
So if i define this url like this I can't get anything except table, but if I comment it I get error:
ValueError at /admin/crm/my_model/
Expected table or queryset, not str
5 {% render_table table %}
On the admin.py just add your model:
admin.site.register(MyModelTable)
This is enough to make it display on admin
Related
First section of code works fine; it is for reference.
#Basic Model
class MyTestModel(models.Model):
record = models.CharField(max_length=100)
def __str__(self):
return self.record
#Specify verbose_name
class Meta:
verbose_name = 'UniqueNameExample'
verbose_name_plural = verbose_name
#Generic ListView.
class MyTemplateView(ListView):
model = MyTestModel
template_name = 'base.html'
context_object_name = 'model_list'
ordering = ['record']
#Python block in HTML template. So far, so good.
{% for item in model_list %}
{{ item.record }}<br>
#{{ item }} also works
{% endfor %}
I am trying to access the Model's verbose_name ('UniqueNameExample') AND the model_list in the view. I've tried registering a filter, a tag, and simple_tag.
Something like: templatetags/verbose.py
from django import template
register = template.Library()
#register.filter (or #register.tag or #register.simple_tag)
def verbose_name(obj):
#Could be verbose_name(model) or whatever input
return obj._meta.verbose_name
And then after
{% load verbose %}
in my HTML (which also works fine), I'll try something like this:
{{ object|verbose_name }}
And I'll get the error 'str' object has no attribute '_meta'. Error is the same if using a tag:
{% verbose_name object %}
Note: tags apparently worked for earlier versions, but maybe I'm using them incorrectly? Not asking to access the Model field verbose_name for "record," btw -- that's answered adequately on SO.
The one thing I've tried that gets the answer half right is if I set the following under MyTemplateView:
queryset = model._meta.verbose_name
The problem with this is it overrides the model_list, and the only result I'm left with is 'UniqueNameExample' without being able to access the record(s) I've used in the model.
I know private=True for _meta (not sure if that's relevant or worth exploring/possibly breaking), but Django admin displays the verbose_name (if set) in the list of created models, so I don't see why I can't do the same (also had a rough time tracing back exactly how it does it in the source code). Maybe it's not a generic ListView but a MixIn? Function-based?
Large(ish) db with thousands of models, each with unique verbose_name[s]; would very much like to keep it simple.
EDIT: Found a fantastic solution from Dominique Barton # https://blog.confirm.ch/accessing-models-verbose-names-django-templates/
First, create a templatags folder at the app level and populate with an init file. Next, create a template tag file. Something like verbose.py.
from django import template
register = template.Library()
#register.simple_tag
def verbose_name(value):
#Django template filter which returns the verbose name of a model.
#Note: I set my verbose_name the same as the plural, so I only need one tag.
if hasattr(value, 'model'):
value = value.model
return value._meta.verbose_name
Next, the ListView should be modified.
from django.views.generic.list import ListView as DjangoListView
from .models import MyTestModel
class ListView(DjangoListView):
#Enhanced ListView which includes the `model` in the context data,
#so that the template has access to its model class.
#Set normally
model = MyTestModel
template_name = 'base.html'
context_object_name = 'model_list'
ordering = ['record']
def get_context_data(self):
#Adds the model to the context data.
context = super(ListView, self).get_context_data()
context['model'] = self.model
return context
Don't forget to add the path to urls.py:
path('your_extension/', views.ListView.as_view(), name='base')
Lastly, load the tag and iterate through the "records" normally:
{% load verbose %}
<h1> {% verbose_name model%} </h1>
<ul style='list-style:none'>
{% for item in model_list %}
<li>{{ item }}}</a></li>
{% endfor %}
</ul>
Pagination also works as advertised.
I'm setting up a webstore manager and I'm relying on django-tables2 package to show a list of products using the SimpleTableMixin. I want to add the filter/search capability to the view. As recommended by the django-tables2 package one can rely on django-filter package to provide filtering. However, it the case of a model with many fields it becomes almost impossible to query and develop forms for those efficiently. My goal is to use django-haystack to have a single input search form as a mean to query the model instances which should be displayed in a similar table/form.
I've tried to add the SimpleTableMixin to the django-haystack package generic SearchView. However I keep on getting the following error:
TypeError at /manager/front/products/
Argument data to ProductTable is required
So far my implementation goes as follow:
view:
# ############## Products ############## (previous implementation with django-filter)
# #method_decorator(staff_member_required, name='dispatch')
# class ProductList(SingleTableMixin, FilterView):
# model = Product
# table_class = tables.ProductTable
# filterset_class = filters.ProductFilter
# template_name = 'manager/front/products/product_list.html'
############## Products ##############
#method_decorator(staff_member_required, name='dispatch')
class ProductList(SingleTableMixin, SearchView):
model = Product
table_class = tables.ProductTable
template_name = 'manager/front/products/product_list.html'
tables:
import django_tables2 as tables
from front.models import Product
class ProductTable(tables.Table):
class Meta:
model = Product
template_name = 'django_tables2/bootstrap.html'
search_indexes.py:
from haystack import indexes
from front.models import Product
class ProductIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
def get_model(self):
return Product
urls:
path('front/products',views.ProductList.as_view(),name="manage_products"),
template:
{% extends 'base_site.html' %}
{% load render_table from django_tables2 %}
{% block content_title %}Manage Products{% endblock%}
{% block content %}
<form action="" method="get" class="form form-inline">
{{ form }}
<button class="btn btn-default" type="submit">Search</button>
</form>
{% render_table table %}
{% endblock %}
How can I remove that error and provide efficient search abilities to my list views?
Are you sure that you are using haystack.generic_views.SearchView and not haystack.views.SearchView? Please notice that on https://django-haystack.readthedocs.io/en/latest/views_and_forms.html it says:
As of version 2.4 the views in haystack.views.SearchView are deprecated in favor of the new generic views in haystack.generic_views.SearchView which use the standard Django class-based views which are available in every version of Django which is supported by Haystack.
Thus if you're using haystack.views.SearchView then the get_context_data of SingleTableMixin will never be called thus no table will be put in your context (i.e table will be empty). Actually because I dislike the behavior of {% render_table %} when its parameter is empty (it behaves different than the other Django tags/filters i.e it throws an exception while the django ones silently ignore that) I usually put it inside some {% if table %} checks.
UPDATE
It seems that for whatever reason the data is not passed to the table. I am not sure why, I can't test it right now but from a quick look at the source code your implementation should have been working (considering that SearchView has a get_queryset and TableMixin uses the get_queryset to retrieve its data). In any case you try overriding some methods of TableMixin to make sure that the table is properly returned (take a look at TableMixin here: https://django-tables2.readthedocs.io/en/latest/_modules/django_tables2/views.html).
I think the most definite solution would be to just bite the bullet and override get_table yourself. So, try adding something like this to your class:
def get_table(self, **kwargs):
table_class = self.get_table_class()
# I only change this line from the original to make sure that the self.get_queryset() is called to return the data
table = table_class(data=self.get_queryset(), **kwargs)
return RequestConfig(self.request, paginate=self.get_table_pagination(table)).configure(
table
)
One idea that just popped in my mind. Is there a possibility that the get_queryset() method of SearchView returns None due to bad configuration or whatever?
I would like to have a Django detail view that shows all the models containing a foreign key for the detail view page for that foreign key value, linked from a list view.
The model ActivityID contains a list of character id's (e.g. 'F1088HW'). There are several other models, each containing the contents of a storage facility, and each item has an ActivityID. So when F1088HW is clicked on, for example, a detail page should show all the facilities that has stuff from that ActivityID.
So far I have the following view based on various other SO questions:
class ActivityDetailView(generic.DetailView):
model = ActivityID
# context_object_name = 'library_set'
# Changed to 'activity' based on comments below
# context_object_name = 'ativity'
def get_queryset(self):
pk = self.kwargs['pk']
return models.objects.filter(activityid = pk)
And ActivityID_detail.html:
{% extends "base_generic.html" %}
{% block content %}
<h1>Field Activity ID: {{ activity }} </h1>
<div style="margin-left:20px;margin-top:20px">
<h4>Libraries</h4>
<dl>
{% for library in activity.library_set.all %}
<dt>{{library}} ({{library.library_set.all.count}})</dt>
{% endfor %}
</dl>
</div>
{% endblock %}
urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('activities/', views.ActivityIDsView.as_view(), name='activities'),
path('activities/<int:pk>', views.ActivityDetailView.as_view(), name='activity-detail'),
]
But clicking on an ActivityID in the list view then returns:
AttributeError at /catalog/activities/2429
module 'django.db.models'
has no attribute 'objects'
Any idea what I'm doing wrong?
You are calling models module instead of your model.
Try:
return self.model.objects.filter(id=pk)
As Sergey has pointed out, you're calling the wrong class. But actually the whole approach makes no sense. The detail in this view is the Activity. You don't want to filter the activities by activityid, you want to get the activity by ID and then get is related libraries.
The second part of that is done by the activity.library_set.all in the template, and the first part is what the DetailView would normally do - so you don't need that get_queryset method at all. You should remove it.
I'm trying to access a url that's like
127.0.0.1:8000/posti/qNwEXBxXQdGI4KlQfoHWOA
However I can't resolve that smalluuid.
This is the error:
NoReverseMatch at /posti/ Reverse for 'detail' with arguments
'(SmallUUID('qNwEXBxXQdGI4KlQfoHWOA'),)' not found. 1 pattern(s)
tried: ['posti/(?P[0-9a-fA-F-]+)/$']
Django has issues trying to resolve it in another view that has a string like this:
from . import views
from django.conf.urls import url
app_name = 'posti'
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<slug>[0-9a-fA-F-]+)/$', views.DetailView.as_view(), name='detail'),
My DetailView is this one:
class DetailView(generic.DetailView):
model = Post
template_name = 'posti/detail.html'
slug_field = 'uuid'
def get_queryset(self):
"""
Excludes any questions that aren't published yet.
"""
return Post.objects.all()
I tried rewriting get_object but it didn't do anything. I don't understand if the regex is wrong or if my view has something wrong.
EDIT:
My template on index raised the error above and it had the following code:
{% if posti_list != null %}
<ul>
{% for post in posti_list %}
<li>{{ post.title }}</li>
{% endfor %}
</ul>
{% else %}
<p>No posts are available.</p>
{% endif %}
I added slug_url_kwarg = 'uuid' to the DetailView class and now it works BUT now I have a
AttributeError at /posti/qNwEXBxXQdGI4KlQfoHWOA/ Generic detail view
DetailView must be called with either an object pk or a slug.
When I try to access the specific post.
I added slug_url_kwarg = 'uuid' to the DetailView class and now it works BUT now I have a
AttributeError at /posti/qNwEXBxXQdGI4KlQfoHWOA/ Generic detail view DetailView must be called with either an object pk or a slug.
slug_url_kwarg must match your url regex group name (slug in your case, which is default value for slug_url_kwarg), so you shouldn't have changed it
For details look at the piece of Django source code here - https://github.com/django/django/blob/master/django/views/generic/detail.py#L8
I using Django tagging project.
That is was very stable project.
Working on django 1.3.
But i have a problem.
# in models.py
from tagging.fields import TagField
class Blog(models.Model):
title = models.CharField(max_length = 300)
content = models.TextField()
tags = TagField()
author = models.ForeignKey(User)
# in views.py
def blog_list(request):
# I Want to use select related with tags
blogs = Blog.objects.all().select_related("user", "tags") # ????
....
# in Templates
{% load tagging_tags %}
{% for blog in blogs %}
{% tags_for_object blog as tags %}
{{blog.title}}
{% for tag in tags %}
{{tag}}
{% endfor %}
{% endfor %}
django-tagging uses a generic foreign key to your model, so you can't just use select_related.
Something like this should do the trick though:
from django.contrib.contenttypes.models import ContentType
from collections import defaultdict
from tagging.models import TaggedItem
def populate_tags_for_queryset(queryset):
ctype = ContentType.objects.get_for_model(queryset.model)
tagitems = TaggedItem.objects.filter(
content_type=ctype,
object_id__in=queryset.values_list('pk', flat=True),
)
tagitems = tagitems.select_related('tag')
tags_map = defaultdict(list)
for tagitem in tagitems:
tags_map[tagitem.object_id].append(tagitem.tag)
for obj in queryset:
obj.cached_tags = tags_map[obj.pk]
I have recently encountered the same problem and solved it without a single db query. Indeed, we don't need tag id to get url. As tag name is unique and is db_index you can get url using name field instead of id. E.g.
# your_app/urls.py
url(r'tag/(?P<tag_name>[-\w]+)$', tag_detail_view, name='tag_detail')
Also, tagging TagField gives us a string with tag names, like 'python,django'. So we can write a custom template filter:
# your_app/templatetags/custom_tags.py
from django.urls import reverse
#register.filter
def make_tag_links(tags_str):
return ', '.join([u'%s' % (reverse(
'tag_detail', args=[x]), x) for x in tags_str.split(',')])
And then you can write in template:
# your_list_template.html
{% for blog in blogs %}
{{blog.title}}
{% if blog.tags %}
{{ blog.tags|make_tag_links|safe }}
{% endif %}
{% endfor %}