I have the following in urls.py:
url(r'frameworkslist/(?P<pk>\d+)$', DetailView.as_view(queryset=Category.objects.all().order_by("id"), template_name='home/subcategory.html'))
And in my html template:
{% extends "Layout.html" %}
{% block content %}
{{ subcategory.Name }}
{% for item in object_list %}
<h5> {{ item.Name }} </h5>
{% endfor %}
Back to framework list
{% endblock %}
I was wondering why object_list was empty but when I changed my urls to the following, it worked. (returned the ListView template instead of DetailView):
url(r'frameworkslist/(?P<pk>\d+)$', ListView.as_view(queryset=Subcategory.objects.all().order_by("id"), template_name='home/subcategory.html'))
Also to note, it seems like passing the variable "model=Subcategory" to the DetailView would return the first record (or what should be a single model) of the Subcategory table and pass that to the Template. So my question is two-part, the second being: How do you a collection of objects to DetailView Template?
DetailView is used for viewing a single object, and ListView is for, well, a list of objects. Usually the pattern is you have a ListView which has links to many individual things (like products), and then each product is viewed through a DetailView.
ListView is the one that automatically populates the object_list variable with your queryset.
In case of DetailView, it takes the queryset you provide and then uses the variables in the url to get() a single one. In your example that would be Subcategory.objects.all().order_by("id").get(pk={some number}). This is then given to the template in the object variable by default.
Lots more info on class based views here.
As a side note, you should be using the {% url %} tag in your templates rather than hard coding the urls.
Related
I am new to Django and in need of some help. I have my database entries being displayed on a template (6 entries). I am aware that to sort your database objects, you need to write Model_Name.objects.all().order_by('Choose_Sort_Option') in the views.py. I would like to make this function a link in that same template. So far, the only thing I've been able to come up with is making a new view, new url pattern, and new template just to display that one new sort. I don't want have to make 30 pages just for sorting. I am thinking that I need to assign that line of code above to a variable(see product d and product_a below). Then i would call that variable in the template, but I'm not sure how to replace the existing list of database items. In short: How do I create clickable links to sort my database objects all within the same template. This is already done in the admin. Thank you!
my views.py:
from django.http import Http404, HttpRequest
from django.contrib import messages
from vendor_db.models import Itemo
from django.db.models import Q
from django.shortcuts import HttpResponse, HttpResponseRedirect,
render_to_response, render, redirect, get_object_or_404
def index(request):
return render(request, 'vendor_db/index.html')
def vendors(request):
items = Itemo.objects.all()
var_2 = request.GET.get("q")
product_d = Itemo.objects.all().order_by('-Product_Name')
product_a = Itemo.objects.all().order_by('Product_Name')
if var_2:
items = items.filter(Vendor_Name__icontains=var_2)
context = {'items': items,
'product_d' :product_d,
'product_a' :product_a,
}
return render(request, 'vendor_db/vendors.html', context)
my template (it is already displaying all of my database objects in an arbitrary order):
{% extends "base.html" %}
{% block content %}
{% load static %}
<h2>All Vendors</h2>
<h3>Search</h3><div>
<form method='GET' action=''>
<input type='text' name="q" placeholder='Search Vendors'/>
<input type='submit' value='Search'/>
<form action="{% url 'vendors' %}">
<input type="submit" value="Reset">
</form>
</form>
<br>
{% if items %}
{% for stuff in items %}
<a href="{% url 'vendors_detail' stuff.id %}">
{{ stuff.Product_Name|capfirst }}<div></div>
</a>
{% endfor %}
{% else %}
<p>No Results Found</p>
{% endif %}
<h3>Sort by:</h3>
Product Name
{% endblock %}
EDIT
While I am sure the 2 answers below would the do the trick, I was unable to comprehend (my own fault). I places my database entries in a table and used a javascript library to change the sort order just by clicking the column header. You can see the question I asked and the code here
In your model.py class use
Class XYZ()
...................
...................
class Meta:
ordering = [
"your_first_field_name",
"your_second_field_name",
........................
]
You could use a form with a variable as you said, pass the form back to the same view and get the sort option from that and create the sorted query for your product items dynamically or using if statements. Then rerender the page. This means only one view would be needed.
Or you may be interested in an app called django-tables2, it gives you the ability to render a nice table which can then be sorted as you wish by clicking on arrows.
I'm working on a simple blog app in Django, and i'm having trouble figuring out how to dynamically generate the five most recent posts in a side bar. Each of my views are class based and they extend a generic template, each view maps to one template which I believe is the correct way to do it. I've looked for a way to do this using template tags, but it seems Django doesn't like you to put any logic inside of your templates.
The problem I believe is that I want this to exist within my base.html because I want the recent posts to be displayed site-wide, is a view even supposed to map to your base.html or does that cause problems, i'm pretty new with this. I don't know how to approach this, whether i'm supposed to create a new view for base.html or if I should use my template tags, or if I should extend an existing view(but if I do that it won't be site wide?).
I essentially want the following(they're ordered in reverse chronological order)
{% for post in post_list[:4] %}
{{ post.title }}
{% endfor %}
You can use a template tag. More specifically, an inclusion tag is what you need. This allows you to insert a rendered snippet anywhere inside your template via a small view-like piece of code.
For example, create a templatetags/blog_tags.py file (it's important that you create the templatetags folder within your app; Django searches for them here by default) in your blog app and add the following:
from django import template
register = template.Library()
#register.inclusion_tag('blog/snippets/recent_posts.html')
def render_recent_blogposts():
return {
# This is just an example query, your actual models may vary
'post_list': BlogPost.objects.all().order_by("published_on")[:4]
}
now create a blog/snippets/recent_posts.html template (it can be anywhere as long as it mathecs the #register.inclusion_tag(...) above.):
<ul>
{% for post in post_list %}
<li> {{ post.title }}</li>
...
{% endfor %}
</ul>
finally, in your original template, you can now render your template tags:
<aside>
{% load blog_tags %}
{% render_recent_blogposts %}
</aside>
I'm trying to build a website that has products and categories.
When you are on the page of a product, you can click a button to see a list of all the categories it falls under.
You can click another button, that appears on all pages, to see a list of all the categories overall.
In the html page see_all_categories, I wrote a simple block like this:
{% extends 'base.html' %}
{% load staticfiles %}
{% block content%}
{{Category.all}}
{% endblock content %}
I expect to see a messy printout of all the categories but I don't. It doesn't return an error, but it produces nothing, other than the base.html.
What am I doing wrong?
You want to display a list of the categories. I assume your Category model owns an attribute named "title" which is the representation of your Category.
If you're using Django template engine or Jinja2, you can make a for loop inside your template like this :
{% for cat in Category.objects.all %}
{{ cat.title }}
{% endfor %}
As a troubleshooting, I'd suggest you didn't pass your Category model to your template, that is not done automatically. You have to add your model to the context before rendering the template.
As mentionned in the comments, here is doc for template rendering with Django templates.
Django Template Guide
To add your model to the context you can follow this guide.
I don't intend to help you further because I lack of information and that may vary a LOT according to your settings. (Class Based views ? Function based views ? What kind of template are you using... And so on)
I figured out the solution after many long annoying hours of trying everything. I feel dumb but I want to spare the next guy the massive pain in the two-pack.
This is what I did:
In the Views.py, I changed the view function for this page FROM this:
def view_all_categories(request):
context = {'Category' : Category}
return render(request, 'store/see_all_categories.html', context)
TO this
def view_all_categories(request):
all_cats = Category.objects.all().order_by('id')
context = {'all_categories' : all_cats}
return render(request, 'store/see_all_categories.html', context)
and in the page see_all_categories.html itself, I changed it (from the question) TO this:
{% extends 'base.html' %}
{% load staticfiles %}
{% block content%}
{% for cat in all_categories %}
<p>{{ cat.name }}</p>
{% endfor %}
{% endblock content %}
And now it works!!
I have a variable:
m2m_links = mymodel._meta.many_to_many
This has all of the fields that are m2m in a particular model. I want the template to display the names of the models that are linked. Within my view, I can list the tables like so:
for f in m2m_links:
print f.related.parent_model
But in my template, if I try
{% for table in m2m_links %}
{{ table.related.parent_model }}<br>
{% endfor %}
I get an error: "Caught DoesNotExist while rendering"
How do I get the names of the tables to render in the template? And a further question, how do I get just the name, not something like
<class 'myapp.models.ModelName'>
which is what I have showing in my terminal from the "print" statement.
There's no reason, based on the template code you've provided that you should be getting that error. Most likely, there's something else going on in your template that is causing that.
As for your second question, the way to get a class' name is:
some_class.__name__
However, the Django template engine will not allow you to use underscored properties in the template, so your best bet is to prepare a proper list in your view:
linked_models = [m2m.related.parent_model.__name__ for m2m in mymodel._meta.many_to_many]
Then, just loop through that in your template and the all the work is already done.
UPDATE (based on comment)
You do it mostly the same way, though you have a couple of choices.
You can do a list of tuples and unpack it in the template:
linked_models = [(m2m.related.parent_model.__name__, m2m.related.parent_model._meta.verbose_name) for m2m in mymodel._meta.many_to_many]
Then, in your template:
{% for class_name, verbose_name in linked_models %}
{{ class_name }} {{ verbose_name }}
{% endfor %}
Create a list of dictionaries and reference the keys in the template:
linked_models = [{'class_name': m2m.related.parent_model.__name__, 'verbose_name': m2m.related.parent_model._meta.verbose_name} for m2m in mymodel._meta.many_to_many]
Them, in your template:
{% for model in linked_models %}
{{ model.class_name }} {{ model.verbose_name }}
{% endfor %}
So the title is a bit obtuse, I know, but I couldn't think of a more succinct way to state it. Here's the issue:
I've created two proxy models for "user types", both inheriting from django.contrib.auth.User. Each has a custom manager limiting the queryset to items belonging to a particular Group. Specifically, there's a PressUser which is any user belonging to the "Press" group and StaffUser which is any user in any other group than "Press".
The issue is that when I add 'groups' to list_filters on my StaffUsers modeladmin, the resulting filter options are every group available, including "Press", and not just groups available to StaffUsers.
I've research a bit online and came up with a custom filterspec that should produce the behavior I want, but the problem is that the User model's 'groups' attribute is actually a related_name applied from the Group model. As a result, I can't attach my filterspec to 'groups' in my proxy model.
Is there any other way to apply the filterspec? Alternatively, is there a better approach to filtering the items returned by the default filterspec?
So, I was able to solve my own problem. For those that might run into a similar situation, here are the steps:
The approach I took is to modify the change_list.html template and manually filter out the items I didn't want to be included. There's quite a number of changes to make, though.
First, add a changelist_view method to your ModelAdmin:
# myproject/account/admin.py
class StaffUserAdmin(models.ModelAdmin):
...
def changelist_view(self, request, extra_context=None):
groups = Group.objects.exclude(name__in=['Press',]).values_list('name')
extra_context = {
'groups': [x[0] for x in groups],
}
return super(StaffUserAdmin, self).changelist_view(request,
extra_context=extra_context)
Basically, all we're doing here is passing in the filtered list of Groups we want to use into the context for the template.
Second, create a change_list.html template for your app.
# myproject/templates/admin/auth/staffuser/change_list.html
{% extends "admin/change_list.html" %}
{% load admin_list %}
{% load i18n %}
{% load account_admin %}
{% block filters %}
{% if cl.has_filters %}
<div id="changelist-filter">
<h2>{% trans 'Filter' %}</h2>
{% for spec in cl.filter_specs %}
{% ifequal spec.title 'group' %}
{% admin_list_group_filter cl spec groups %}
{% else %}
{% admin_list_filter cl spec %}
{% endifequal %}
{% endfor %}
</div>
{% endif %}
{% endblock filters %}
This one deserves a little explanation. First, the template tag loads: admin_list is used for the default Django template tag responsible for rendering the filters, admin_list_filter, i18n is used for trans, and account_admin is for my custom template tag (discussed in a sec), admin_list_group_filter.
The variable spec.title holds the title of the field that's being filtered on. Since I'm trying to alter how the Groups filter is displayed, I'm checking if it equals 'groups'. If it does, then I use my custom template tag, otherwise, it falls back to the default Django template tag.
Third, we create the template tag. I basically just copied the default Django template tag and made the necessary modifications.
# myproject/account/templatetags/account_admin.py
from django.template import Library
register = Library()
def admin_list_group_filter(cl, spec, groups):
return {'title': spec.title, 'choices' : list(spec.choices(cl)), 'groups': groups }
admin_list_group_filter = register.inclusion_tag('admin/auth/group_filter.html')(admin_list_group_filter)
The only things that I've changed here are adding a new argument to the method called 'groups' so I can pass in my filtered list of groups from before, as well as adding a new key to the dictionary to pass that list into the context for the template tag. I've also changed the template the tag uses to a new one that we're about to create now.
Fourth, create the template for the template tag.
# myproject/templates/admin/auth/group_filter.html
{% load i18n %}
<h3>{% blocktrans with title as filter_title %} By {{ filter_title }} {% endblocktrans %}</h3>
<ul>
{% for choice in choices %}
{% if choice.display in groups %}
<li{% if choice.selected %} class="selected"{% endif %}>
{{ choice.display }}</li>
{% endif %}
{% endfor %}
</ul>
No big surprises here. All we're doing is putting all the pieces together. Each choice is a dictionary with all the values needed to construct the filter link. Specifically, choice.display holds the actual name of the instance that will be filtered by. Obviously enough, I've set up a check to see if this value is in my filtered list of groups I want to show, and only render the link if it is.
So, it's a bit involved but works remarkably well. Just like that, you have a list of filters that is exactly what you want instead of the default ones generated by Django.
I'm going to tell you off the bat that I've never done this before myself, so take it with a grain of salt.
What I'd suggest would be to override get_changelist on your ModelAdmin, to return a custom ChangeList class, which you can define somewhere in your admin module.
Your custom ChangeList class would simply override get_filters, so you can map your custom FilterSpec for the group field.
Another thing that might interest you are patches from the feature request ticket for specifying custom filter specs. The latest patch doesn't work for Django 1.3rc1 yet, although #bendavis78 recently posted that he's working on a new one, but depending on your version of Django it may apply cleanly.
It looks like it barely missed the cut to get included into the 1.3 milestone, so I figure it's going to make it into the trunk as soon as work beings on Django 1.4.