Hello I'm currently new to django/wagtail. I'm working on an about page that shows previous and current work/positions. I've made the positions streamfield blocks since the amount of experience isn't limited. Here is the code to my models.
#Create experience block
class ExperienceBlockStruct(StructBlock):
position_title = CharBlock(help_text="Enter a previous position title.")
description = CharBlock(help_text="Job description")
current_position = BooleanBlock(required=False, help_text="Check if
current position")
class Meta:
template = 'blocks/experience_block.html'
class ExperienceBlock(StreamBlock):
experience = ExperienceBlockStruct(icon="form")
And here is the page where I use the models
class About(Page):
profile_pic = "some image reduced code bc it currently works"
bio = RichTextField(blank=True)
resume = "some document reduced code bc it currently works"
experience = StreamField(ExperienceBlock())
content_panels = Page.content_panels + [
ImageChooserPanel('profile_pic'),
FieldPanel('bio'),
DocumentChooserPanel('resume'),
StreamFieldPanel('experience'),
]
Now the issue I'm having is how to render the blocks where the current_position = True in a different area than those that are not.
I've tried
templates/about.html
{% for block in page.experience %}
{% if block.current_position %}
{% include_block block %}
{% endif %}
{% endfor %}
But that doesnt render anything. I've also tried to
<div class="experience">
{% if value.current_position %}
{{ value.position_title }}
{% else %}
{{ value.position_title }}
{% endif %}
</div>
but that creates a new div for every block. What I would like to achieve is something like in blocks/experience_block.html
<div>
Current position(s): {% blocks with current_postion == True %}
</div>
<div>
Past position(s): {% blocks with current_postion == False %}
</div>
How could I go about achieving something like this?
Your first template snippet was almost correct - you just need to check block.value.current_position rather than block.current_position:
{% for block in page.experience %}
{% if block.value.current_position %}
{% include_block block %}
{% endif %}
{% endfor %}
This is because looping over page.experience gives you a series of BoundBlock objects that tell you the block_type (always 'experience' in your case) alongside the block value. See BoundBlocks and values for a more detailed explanation.
You can do the same thing in your experience_block.html template (using {% for block in value %} rather than {% for block in page.experience %}) - although note that the Meta template definition needs to go on ExperienceBlock rather than ExperienceBlockStruct, because that's the one that has access to the full list to loop over, rather than a single record.
To make things neater, I'd suggest defining a get_context method on the block, so that you're doing the data manipulation within Python code rather than inside the template...
class ExperienceBlock(StreamBlock):
experience = ExperienceBlockStruct(icon="form")
def get_context(self, value, parent_context=None):
context = super(ExperienceBlock, self).get_context(value, parent_context=parent_context)
context['current_positions'] = [block for block in value if block.value.current_position]
context['past_positions'] = [block for block in value if not block.value.current_position]
return context
class Meta:
template = 'blocks/experience_block.html'
This will make the variables current_positions and past_positions available on the template.
Related
Hi2all!
I have:
1) mongo collection:
[{_id:ObjectId("5b0d5fb624d22e1b4843c06b")
collectionName:"collection0"
collectionCaption:"caption1"}
{_id:ObjectId("5b0d5fb824d22e1b4843d4c1")
collectionName:"collection1"
collectionCaption:"caption1"}
{_id:ObjectId("5b0d5fb924d22e1b4843d74a")
collectionName:"collection2"
collectionCaption:"caption1"}
{_id:ObjectId("5b0d5fb924d22e1b4843d7b0")
collectionName:"collection3"
collectionCaption:"caption1"}]
2) flask app with the view:
def index():
a = mongo.db.collectionsNames.find()
return render_template('index.html', collectionsNames=a)
3) templates: index.html witch extend base.html.
base.html:
{% extends "bootstrap/base.html" %}
{% block content %}
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
{% for asd in collectionsNames %}
<a class="dropdown-item" href="/{{ asd["collectionName"] }}">{{ asd["collectionCaption"] }}</a>
{% endfor %}
</div>
{% block contentBase %} {% endblock %}
{% endblock %}
index.html:
{% extends "base.html" %}
{% block contentBase %}
{% for zxc in collectionsNames %}
{{ zxc["collectionName"] }}
{% endfor %}
{% endblock %}
The question is: why if base and index using the same variable of collection, index template show nothing?
But if in view code is:
def index():
a = mongo.db.collectionsNames.find()
b = mongo.db.collectionsNames.find()
return render_template('index.html', collectionsNames1=a, collectionsNames2=b)
and in templates i am using the different variables, the index template show me the data.
mongo.db.collectionsNames.find() returns a cursor: print(mongo.db.collectionsNames.find()) gives <pymongo.cursor.Cursor object at 0x7fd3854d5710>.
To make it easy (but kind of wrong) a cursor is a specific kind of instance that fetch data from the database chunk by chunk so if you want the 1 000 000 first items in db you don't actually store 1M items in RAM, you iterate 100 by 100 items (for example). Cursor handle that in a magic way.
Whatever, you cannot loop several time on the same cursor and you should never caste a cursor to list like list(cursor_instance) (because if your query ask for 1M product, doing this add all these products in RAM).
So, now, how can you handle this. Most of time I would say it's better to call the method when you need it, twice if needed.
But here you are in a Jinja environment and if I'm right calling methods within a Jinja template is impossible.
A way to do that is to use properties.
class LazyCollection(object):
#property
def collections_names(self):
return mongo.db.collectionsNames.find()
lazycoll = LazyCollection()
return render_template('index.html', collectionsNames=lazycoll)
Then, in your template:
{% for asd in collectionsNames.collections_names %}
<p>{{ asd.foo }}</p>
{% endfor %}
I'm trying to add a custom Title to my product pages using the block title tag in Django.
I have a view function to return data to a template for the product page:
def deal_by_detail(request, slug):
deal_detail = Deal.objects.filter(slug=slug)
return render(request, 'deals/deal_detail.html', {'deal_detail': deal_detail})
and URL for the deal_detail page:
url(r'^(?P<slug>.*)/$', deal_by_detail, name='deal_detail'),
In my 'deal_detail.html' page I have successfully displayed all the information from a particular product... like so:
{% for deal in deal_detail%}
{{ deal.title}}
{{ deal.price}}
{% endfor %}
However i'm having an issue with the block title. Since it comes before the aforementioned loop, i realize I can't reference the title like so:
{% block title %}{{deal.title}}{% endblock %}
I've also tried it like this:
{% block title %}{{deal_detail.title}}{% endblock %}
But that doesn't work either --nor does just {{ title }}
I also experimented with a duplicate of the other loop and that doesn't work either.
Here is what I have on the base template page that is extended from this deal_detail template:
<title>My Site - {% block title %}{% endblock title %}</title>
Just kind of stumped and not sure if I need a class based view or something I'm totally missing here. Thanks in advance.
I am currently writing a django app around a rather complex data model.
For many use cases, I need to build similar, but slightly differing templates (incl. graphviz etc..).
Now I wonder if there is a way to follow DRY and create the templates "on the fly", e.g. based on a nested tuple (of which one gets defined per use-case -> template).
Practically speaking I'd like to stop writing these:
static_template_case1.html
{% if program %}
{{ program.name }}
{% for process in program.process_set.all %}
{{ process.name }}
{% for step in process.step_set.all %}
{{ step.name }}
{% endfor %}
{% for control in process.control_set.all %}
{{ control.name }}
{% endfor %}
{% endfor %}
{% endif %}
and replace it with something in the following direction:
template_generator.py
structure_case1 = ("program"("process"("step","control")))
def onTheFlyTemplate(structure):
# iterate through structure
# build template dynamically
# return template
onTheFlyTemplate(structure_case1)
I was not able to find something similar and also don't know yet where to start generating templates on the fly, nor how to integrate it.
Within the views feels like a good starting point, (instead of loader.get_template('static_template_case1.html')).
Thanks for any hint sending me into the right direction as well as your thoughts if DRY is being "overdone" here.
Ralph
Edit
I got a step closer, doing the following:
In the view:
from django.template import Template
def templateGen():
return Template("Program: {{program.count}}")
#login_required
def test(request):
program = Program.objects.all()
t = templateGen()
c = RequestContext(request,locals())
return HttpResponse(t.render(c))
So far this is only a proof of concept. The real work will be to write a smart "templateGen()" listening to the nested tuple.
How about an inclusion tag?
#register.inclusion_tag('<your-template.html>')
def get_program(program_id):
return {'program': <some logic to return your Program object>}
Your main template:
{%load my-template-tags%}
<div id="my-div">
{%get_program program_id %}
</div>
And your template file:
{{ program.name }}
{% for process in program.process_set.all %}
{{ process.name }}
{% for step in process.step_set.all %}
{{ step.name }}
{% endfor %}
{% for control in process.control_set.all %}
{{ control.name }}
{% endfor %}
{% endfor %}
You'd have to pass in your program_id to the main template.
I am writing a django template Configuration_Detail.html and it renders correctly on the relevant urls. It does not, however, take any variables whatsoever from the view class. I had a very similar template Configuration_List.html which worked fine, though that was a ListView not a DetailView.
Configuration_Detail.html:
{% extends "base.html" %}
{% load i18n %}
{% block title %}{% trans 'MySite Database' %}{% endblock %}
{% block branding %}
<h1 id="site-name">{% trans 'MySite Database: Current Instrumentation Configuration' %}</h1>
{% endblock %}
{% block content %}
Here is some text {{name}} with a variable in the middle.
{% endblock %}
The page renders the title bar fine, but the content block becomes "Here is some text with a variable in the middle."
I believe it should be taking the variable {{ name }} from here.
views.py:
class ConfigurationDetail(DetailView):
model = Configuration
def getname(self):
name = 'debug'
return name
But it does not...
Any suggestions on how to fix this would be greatly appreciated.
Edited to add:
Models.py - Configuration:
class Configuration(models.Model):
title = models.CharField(max_length=100,unique=True,blank=False)
author = models.ForeignKey(User)
created = models.DateField("date created",auto_now_add=True)
modified = models.DateField("date modified",auto_now=True)
description = models.CharField(max_length=512)
drawing = models.ForeignKey(Drawing,blank=True,null=True)
instruments = models.ManyToManyField(Instrument)
def __unicode__(self):
return self.title
The get_context_data() method is using ctx['author'] = Configuration.author
For DetailView, an object variable is added in the context which points to the database object for which the view is being rendered. So in your template you can do:
{% block content %}
Here is some text {{ object.author.get_full_name }}
with a variable in the middle.
{% endblock %}
The get_full_name method is from the User object.
if i understood correctly you need to access a model property from within a template, but for that is sufficient to do {{ configuration.author }} without modifying the context data at all!
the DetailView puts in the context the selected model, accessible with the dot notation.
I'm trying to display an HTML table of values with about 20 columns where say staff users see one subset of columns, and non-staff users see another subset of columns. I may want to define further types of users later. Now right now I have three static header rows so the template looks like
<table>
<tr>
<th>Col A</th>
{% if user.is_staff %}<th>Col B</th>{% endif %}
...
{% if not user.is_staff %}<th>Col K</th>{% endif %}
</tr>
<tr>
<td>Col A second header</td>
{% if user.is_staff %}<td>Col B second header</td>{% endif %}
...
{% if not user.is_staff %}<td>Col K second header</td>{% endif %}</tr>
<tr><td>Col A third header</td> ... </tr>
{% for obj in object_list %}
<tr>
<td>{{ obj.col_a }}</td>
{% if user.is_staff %}<td>{{ obj.col_b }}</td>{% endif %}
...
{% if not user.is_staff %}<td>{{ obj.col_k }}</td>{% endif %}
</tr>
{% endfor %}</table>
However, I find non-DRY as every time, if I want to change if a user-type can see a column, I have to change it in 4 places. Or if I want to define multiple different classes of users, I'd have to have complicated if statements. I'd prefer something like
{% if show_col_a %}<td>{{obj.col_a }}</td>{{% endif %}
Where I can define at the top of the template (or possibly in the view) that user.is_staff can see show_col_a. Is something like this possible? I'm using a generic view (object_list). Maybe modify all users to have attributes user.show_col_a somehow and do {% if user.show_col_a %}? I'm not sure how to add boolean attributes to users.
EDIT: May want multiple users with custom views (e.g., staff_view; admin_view, unprivileged, etc.), so if statements would get unwieldy. A cell's contents is typically more complicated than {{ obj.col_b }}; tried simplifying problem to get to the point. E.g.:
<td>{% if obj.custom_address %}
{{ obj.custom_address.webprint|safe }}
{% else %}
{{ obj.business.address.webprint|safe }}
{% endif %}</td>
Also while multiple templates would work with a simple switch like:
{% if user.is_staff %}
{% include "template_staff.html" %}
{% else %}{% if user.is_admin %}
{% include "template_admin.html" %}
{% else %}
{% include "template_other.html" %}
{% endif %}
{% endif %}
I find its not DRY at all; e.g., every edit to a template has to be replicated in three template. I guess I could make a script that read generates the three templates from some super_template outside of django but its getting very inelegant.
This depends a lot on what view you have and templates.
Ways to do:
make a public template and staff template and add a simple method to change the templates on the fly for the views.
make a template tag:
{% is_staff myvar %}
tag code:
class IsStaffNode(Node):
def __init__(self, var):
self.var = var
def render(self, context):
if context['user'].is_staff():
return var.resolve(context)
return ""
#register.tag
def is_staff(parser, token):
var = parser.compile_filter(token.split_contents()[1])
return IsStaffNode(var)
Homework: make it a block tag to include the td's so that it's shown either all or none.
{% isstaff myvar %}<td>{{ myvar }}</td>{% endisstaff %}
This way is more labor intensive than 2 different templates, but if you want to try, manipulating the context (or creating a separate context for the block only) might be useful.
Make a context processor that would fill the context with some variables if the user is staff, or not if not.
Make a tag that would include the template (inherit from IncludeNode) and manipulate the context.