Is there a good way to render the enumeration of a queryset into two div columns?
Using 960 grid, I've got something to the effect of...
<div class="container_16">
<div class="grid_8 alpha"></div>
<div class="grid_8 omega"></div>
</div>
In Django, one model needs to have it's enumerated contents rendered in both of those columns, and preferably somewhat equally. For the moment, I've got some ugly code that in the view splits the QuerySet into 2 halves, and then each half is rendered in their respective column.
There's got to be a better way to do this, preferably using only the template rendering system?
Just for reference, here's how it "works" at the moment:
views.py
#render_to('template.html')
def main_athletics_page(request, *args, **kwargs):
sports = Sport.objects.all()
half = sports.count() / 2
return { 'sports_1' : sports[0:half], 'sports_2' : sports[half:] }
template.html
<div class="grid_8 alpha">
{% for sport in sports_1 %}
<!-- Blah blah -->
{% endfor %}
</div>
<div class="grid_8 omega">
{% for sport in sports_2 %}
<!-- Blah blah -->
{% endfor %}
</div>
I recommend using Django filters.
Django snippets provides a partitioning template filter, which you can use like:
{% load listutil %}
<div class="grid_8 alpha">
{% for sport in sports|partition:"2"|first %}
<!-- Blah Blah -->
{% endfor %}
</div>
<div class="grid_8 omega">
{% for sport in sports|partition:"2"|last %}
<!-- Blah Blah -->
{% endfor %}
</div>
This is the task of a rendering system, not view. The view should not know whether you will display 2, 3 or 4 columns in your template.
It is always better to use default Django tags.
Use default Django template tag cycle:
<table>
{% for item in items %}
{% cycle 'row' '' as row silent %}
{% if row %}<tr>{% endif %}
<td>
{{ item }}
</td>
{% if not row %}</tr>{% endif %}
{% endfor %}
</table>
It will display your list [1 2 3 4 5 6] as
1 2
3 4
5 6
By the way, Jinja2 template engine has batch and slice filters which will do the trick. I switched to jinja2 and now have none of those problems of "how to display x using poor django tags and filters"
I think you will have to make your own template tag to do splits on queries. I would do something like.
from django.template import Library, Node, TemplateSyntaxError
from restaurants.forms import MenuItemForm
class Split(Node):
def __init__(self, queryset, split_count=2, basename=None):
self.queryset_name = queryset
self.split_count = split_count
self.basename = basename if basename else queryset
def render(self, context):
qs = context[self.queryset_name]
qs_break = len(qs)/self.split_count
for x in xrange(0, self.split_count-1):
context["%s_%i"%(self.basename, x+1)] = qs[qs_break*x:qs_break*(x+1)]
context["%s_%i"%(self.basename, x+2)] = qs[qs_break*x+1:]
return ''
def split(parser, token):
"""
Call from template will be
{% split <queryset> on <count> as <name> %}
"""
tokens = token.split_contents()
if len(tokens) > 6:
raise TemplateSyntaxError("Too many Tokens")
#Do various tests to make sure it's right.
return Split(tokens[1], tokens[3], tokens[5])
split = register.tag(split)
Please note that I haven't actually tested this code, so it might fail spectacularly, but it should point you towards the right direction for getting that stuff out of your view.
Late response, pretty much 13 years later :) but I think the easiest way if you have a fixed length list is to follow the same approach you used in your example, just slice the list. But there's no need to slice on the view, you can slice on the template using the slice filter.
<div class="grid_8 alpha">
{% for sport in sports|slice:":7" %}
<!-- Blah blah -->
{% endfor %}
</div>
<div class="grid_8 omega">
{% for sport in sports|slice:"7:" %}
<!-- Blah blah -->
{% endfor %}
</div>
Note: If you don't have a fixed size list you will probably need to check out the length filter (sports|length) and find a way to divide in Django templates. But at that point is probably better to create your own filter following #notnoop response.
Here's a quick solution which uses bootstrap and needs no Django filters
<div class="row">
{% for sport in sports %}
<div class="col-md-6">
<!-- Blah Blah -->
</div>
{% endfor %}
</div>
Related
In django model I have created a multiselectfield for suppose wishlist where user can select multiple wishes from the available choices.
I am storing this data in comma separated format when it comes to displaying this on template things are pretty easy.
But, I want this data to be displayed dynamically as one row with two columns and as soon as two columns are filled and there is more data left to be displayed logic should should have the capability to create a new row and display the remaining content on the django-template.
For reference:
# models.py
class Wishlist(models.Model):
wishlist = (
('W1',"Buy a big masion"),
('W2',"Buy worlds fastest car"),
('W3',"Visit Europe"),
('W4',"Travel on bike to mountains")
)
your_wishlist = MultiSelectField(choices=wishlist)
# views.py
def index(request):
wishlist = Wishlist.objects.all()
context = {
"wishlist":wishlist
}
return render(request,'demolistingapp/index.html',context)
# index.html
{% load app_filters %}
{% block content %}
<h1>INDEX LISTING APP</h1>
{% if wishlist %}
{% for each_wish in wishlist %}
{% with each_wish.your_wishlist|split:"," as wish %}
{% for mywish in wish %}
<p>{{mywish}}</p><br>
{% endfor %}
{% endwith %}
{% endfor %}
{% endif %}
{% endblock %}
I have registered the custom filter split which returns a list.
I want the data to be displayed in terms of grid by maintaining two columns.
Sample output:
enter image description here
You could use cycle in the templates to create different class:
<p class="{% cycle row_left row_right %}">{{ mywish }}</p>
with adequate css (float/clear or inline-block;width:50%).
I would wrap your outer for loop in a HTML element with a class, then apply a grid layout to that class in your stylesheet. Wrapping each wish in a div element with a particular class will help you with further styling too.
<div class="wishlist">
{% for each_wish in wishlist %}
{% with each_wish.your_wishlist|split:"," as wish %}
{% for mywish in wish %}
<div class="wish">
<p>{{mywish}}</p>
</div>
{% endfor %}
{% endwith %}
{% endfor %}
</div>
And in your CSS:
.wishlist {
display: -ms-grid;
display: grid;
-ms-grid-columns: 1fr 1fr;
grid-template-columns: 1fr 1fr;
}
So I have this template context processor:
from cases.models import CasePage
def random_case(request):
case = CasePage.objects.live().order_by('?')
return {'random_case': case}
And in the template I do this:
{% for entry in random_case %}
{% if request.get_full_path != entry.get_url %}
{% if forloop.first %}
<a class="ajax-link project-next" href="{{ entry.get_url }}">
<div class="nav-project-title">{{ entry.title }}</div>
<div class="nav-title">next</div>
</a>
{% endif %}
{% endif %}
{% endfor %}
And this works but the problem is sometimes the object is the same as the page so nothing is displayed. It would be great if that one would be skipped in favour of the next entry. And it's also too much logic in the template for me. What would be the best way to move this logic into the context processor and make it work?
Make random_case a method of CasePage, and filter out the case with an ID equal to self.
class CasePage(Page):
# ...
def random_case(self):
return CasePage.objects.live().exclude(id=self.id).order_by('?').first()
You can then refer to this method within your template as page.random_case - bear in mind that a new random choice will be made on each call, so you probably want something like {% with page.random_case as case %}.
I'm trying to create a hierarchy view in Django, but I'm struggling to make sense of how to use QuerySets effectively.
What I'm aiming for eventually is a html page that displays courses like this:
Main Course 1 --- Child Course 1
--- Child Course 2
Main Course 2 --- Child Course 3
--- Child Course 4
Each group of courses would be wrapped in a div and styled etc.
In my view.py file I have the following:
class HierarchyView(generic.ListView):
template_name = 'curriculum/hierarchy.html'
def get_queryset(self):
return Offering.objects.all()
def get_context_data(self, **kwargs):
context = super(HierarchyView, self).get_context_data(**kwargs)
context['main'] = self.get_queryset().filter(course_type='M')
context['sub'] = self.get_queryset().filter(parent_code__in=context['main'])
return context
The Offering model is set up so that parent_code is a self-referential foreign key (i.e. any course can be a child of any other), like this:
...
parent_code = models.ForeignKey(
'self',
null=True,
blank=True,
on_delete=models.SET_NULL)
...
And in my html template I have:
{% for mainoffering in main %}
<div>
<div>{{ mainoffering.course_name }}</div>
{% for offering in sub %}
<div>{{ offering.course_name }}</div>
{% endfor %}
</div>
{% endfor %}
What this results in, however, is that all child courses appear under all main courses, regardless of whether or not they are actually children of that course, which is obviously not what I'm after.
I'm still learning the ropes in Django, and I'm struggling to find anything that explains in plain English what I need to do. Please help!
I think you would need to change your template to match each of the child courses to their parent courses. Maybe something like:
{% for mainoffering in main %}
<div>
<div>{{ mainoffering.course_name }}</div>
{% for offering in sub %}
{% if offering.parent_code == mainoffering %}
<div>{{ offering.course_name }}</div>
{% endif %}
{% endfor %}
</div>
{% endfor %}
The context['sub'] will return all of them, without any grouping, ordering etc. You can do couple of things to get the desired behavior.
You can do a prefetch related.
from django.db.models import Prefetch
offerings = Offering.objects.filter(course_type='M').prefetch_related(
Prefetch(
"courses_subset",
queryset=Offering.objects.filter(parent_code__in=offerings),
to_attr="sub"
)
)
for o in offerings:
print o.sub
You can actually make this a method in your model and create a template tag (i'd most likely use this).
method in your Offering model
def get_child_courses(self):
child_courses = Offerings.objects.filter(parent_code=self.id)
return child_courses
template tag
#register.simple_tag
def get_child_courses(course):
return course.get_child_courses()
In your template:
{% for mainoffering in main %}
<div>
<div>{{ mainoffering.course_name }}</div>
{% for offering in mainoffering|get_child_course %}
<div>{{ offering.course_name }}</div>
{% endfor %}
</div>
{% endfor %}
You can group them in your template as suggested by accraze. I'd personally go for the second option
I have few instances of model.
my model:
class Record(models.Model):
name = models.ForeignKey(Car)
image = models.ImageField(upload_to='images/')
created = models.DateTimeField(
default=timezone.now)
view:
def allrecords(request):
records = Record.objects.all().order_by('created')
return render(request, 'mycar/allrecords.html', {'records' : records})
I want show it on my website. In my template i have:
{% for record in records %}
<img src={{ record.image.url}}/>
<div>
{{record.name}}
</div>
{% endfor %}
Now i get list of my records, but i would like put the newest record to first div, next to second etc. How can i do that?
I show simple screen how i would like have that (if someone will create new record, it will go to first div and other records will change place. Is any possibility to do something like that?
edit:
<div>
{% for record in records %}
{% if forloop.counter == 1 %}
<img src={{ record.image.url}}/>
<div>
{{record.name}}
</div>
{% endif %}
{% endfor %}
</div>
<div>
{% for record in records %}
{% if forloop.counter == 2 %}
<img src={{ record.image.url}}/>
<div>
{{record.name}}
</div>
{% endif %}
{% endfor %}
</div>
.
.
# till your 5th image
You can use forloop.counter to get the iteration number and check what is the iteration the loop and handle data accordingly.
In addition you can use CSS to make the layout work as you want.
Here is the information for Django template counter
Edit :
{% for record in records %}
<div>
{% if forloop.counter == 1 %}
# Here you can get your first images
<img src={{ record.image.url}}/>
<div>
{{record.name}}
</div>
{% endif %}
</div>
<div>
{% if forloop.counter == 2 %}
# Here you can get your first images
<img src={{ record.image.url}}/>
<div>
{{record.name}}
</div>
{% endif %}
</div>
.
.
# till your 5th image
{% endfor %}
There are two ways to do this. If you want to set this option for a single view then:
def all_records(request):
records = Record.objects.all().order_by('-created')
return render(request, 'mycar/allrecords.html', {'records' : records})
You're almost correct but order_by('created') leads to asceding order while order_by('-created') leads to descending order which is what you require.
Alternatively, if you want to have this setting to apply to all views then set class Meta in your models.py which will ensure that wherever you use Record.objects.all() it returns Records in descending order of created field:
class Record(models.Model):
name = models.ForeignKey(Car)
image = models.ImageField(upload_to='images/')
created = models.DateTimeField(
default=timezone.now)
class Meta:
ordering = ('-created')
It's Django design pattern to make all logical decisions in models and views and only just plugin formatted data in templates. You shouldn't add any complex logic in templates.
I'm assuming the question means that the model might have more than 5 records. If so, a more generic solution would be
<div class='row'>
<div class='firstimage'>
<img src={{ records[0].image.url}}/>
{{record.name}}
</div>
{% for record in records %}
{% if forloop.counter > 1 %}
<div class='subsequentimage'>
<img src={{ record.image.url}}/>
{{record.name}}
</div>
{% endif %}
{% cycle "" "</div><div class='row'>" "" %}
{% endfor %}
</div>
Note the use of the 'cycle' tag to begin a new row div every third cell div.
I don't know what your CSS classes are to distinguish between rows and cells so I used 'row', 'firstimage' (which might be defined to take up twice as much width) and 'subsequentimage' as example classes.
I recommend you to use the context variables:
def all_records(request):
records = Record.objects.all().order_by('-created')
newest = records[:5]
oldest = records[5:]
return render(request, 'mycar/allrecords.html', {'newst' : newest,
'oldest': oldest })
In your template :
{% for new in newst %}
<div>what you want with news</div>
{% endfor %}
{% for old in oldest %}
<div>what you want with olds</div>
{% endfor %}
This is a hypothetical for simplicity. In my Django app I have models for Kit, KitSku, and Sku. The KitSku model associates a Sku with a Kit and also provides the quantity of that Sku in that kit. In the template I have something like:
<!-- SELECT * FROM kitsku_table WHERE kit_id = <Kit.id> -->
{% for kitsku in kit.kitsku_set.all %}
<!-- SELECT * FROM sku_table WHERE sku = <KitSku.sku> -->
<div>{{ kitsku.sku.name }}</div>
{% endfor %}
Now, the problem here is that Django is querying all of the KitSku rows and then it queries each sku within that for loop in a separate SQL query for each iteration.
Can I make the SQL query resulting from the kitsku_set.all() call perform a JOIN with the Sku model?
That first query needs to be more like:
SELECT * FROM kitsku_table k LEFT JOIN sku_table s ON (k.sku = s.sku)
WHERE k.kit_id = <Kit.id>
Do this type of logic in the view, and use select_related() to directly query the foreign keys
kitskus = Kit.objects.get(id=3).kitsku_set.select_related('sku')
return direct_to_template(request, "mytemplate.html", {'kitskus': kitskus})
Im just copying an answer for a similar question, but i think this is a much better approach.
view
newsletters = Newsletter.objects.prefetch_related('article_set').all()\
.order_by('-year', '-number')
return render_to_response('newsletter/newsletter_list.html',
{'newsletter_list': newsletters})
template
{% block content %}
{% for newsletter in newsletter_list %}
<h2>{{ newsletter.label }}</h2>
<p>Volume {{ newsletter.volume }}, Number {{ newsletter.number }}</p>
<p>{{ newsletter.article }}</p>
<ul>
{% for a in newsletter.article_set.all %}
<li>{{ a.title }}</li>
{% endfor %}
</ul>
{% endfor %}
{% endblock %}
Here is the complete explanation:
Hopes it helps
Iterating over related objects in Django: loop over query set or use one-liner select_related (or prefetch_related)