Get Image and intro text from wagtail page chooser - wagtail

I am incorporating a page chooser in wagtail admin, however what I want to get is the image for each item in the streamblock for the page chooser and was wanting to know if there is a way to do this. So lets say if I have a category index page I would have the following code:
{% if page.case_study %}
{% image page.case_study.image fill-50x50-c100 class="" %}
{{ page.case_study }}
{% endif %}
All I seem to get are the links which is fine, but I need the image from that case study page. My model is as follows:
class CategoryPage(Page):
"""
Detail view for a specific category
"""
introduction = RichTextField(
help_text='Text to describe the page',
blank=True)
image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
help_text='Landscape mode only; horizontal width between 1000px and 3000px.'
)
body = StreamField(
BaseStreamBlock(), verbose_name="Page body", blank=True
)
case_study = StreamField([
('Cases', blocks.PageChooserBlock()),
], blank=True,
null=True,
verbose_name='Case Studies',
)
origin = models.ForeignKey(
Country,
on_delete=models.SET_NULL,
null=True,
blank=True,
)
category_type = models.ForeignKey(
'categories.CategoryType',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
categories = ParentalManyToManyField('Category', blank=True)
content_panels = Page.content_panels + [
FieldPanel('introduction', classname="full"),
ImageChooserPanel('image'),
StreamFieldPanel('body'),
StreamFieldPanel('case_study'),
FieldPanel('origin'),
FieldPanel('category_type'),
]
search_fields = Page.search_fields + [
index.SearchField('body'),
]
parent_page_types = ['CategoriesIndexPage']
Any help would be greatly appreciated

A StreamField is a list of blocks, but when you write page.case_study.image you're trying to access it as if it's only a single item. I'd suggest you start by updating your field / block names to make this distinction clearer - case_studies is the list, and each block in it is a single case (note that conventionally block names are lower-case):
case_studies = StreamField(
[
('case', blocks.PageChooserBlock()),
],
blank=True,
null=True,
verbose_name='Case Studies',
)
In your template, you will now loop over page.case_studies. However, this will not give you the case study page objects directly - each item in the list is a 'block' object, with block_type and value properties. This is because a StreamField usually involves mixing multiple block types (rather than just the one type you've defined here) and in that situation, you need some way of finding out what block type you're working with on each iteration of the loop.
{% if page.case_studies %}
{% for block in page.case_studies %}
{# block.value now refers to the page object #}
{% endfor %}
{% endif %}
The next issue is that your PageChooserBlock currently lets you select any page type. This means that when your template is rendered, it has no way to know up-front which page type to retrieve, so (in order to avoid unnecessary database lookups) it returns it as a basic Page instance which only contains the core fields that are common to all pages, such as title and slug - consequently, your image field will not be available through block.value.image. You can get around this by using block.value.specific.image which performs the extra database lookup to retrieve the complete page data - however, a more efficient approach is to specify the page type on the PageChooserBlock (assuming you've set up a dedicated page type for your case studies):
('case', blocks.PageChooserBlock(page_type=CaseStudyPage)),
Your final template code then becomes:
{% if page.case_studies %}
{% for block in page.case_studies %}
{% image block.value.image fill-50x50-c100 class="" %}
{% endfor %}
{% endif %}

Related

Get ancestor with flag set

Trying to add context menu of nested pages. To do so, added a boolean to the page model called "root_page".
How can I find the ancestor of the current page with this set to True? The calling page may be 1 to 3 levels down from the "root page".
Tried the following but it gives 'NoneType' object has no attribute 'get_ancestors'
menu_tags.py:
def get_page_root(context, calling_page=None):
...
root = calling_page.get_ancestors().live().filter(longscrollpage__root_page=True).first()
...
model.py:
class LongScrollPage(Page):
...
include_left_menu = models.BooleanField(default=False)
root_page = models.BooleanField(default=False)
...
template.html:
...
{% if page.include_left_menu == True %}
{% get_page_root calling_page=self %}
...

Django template - compare date to today

I'm trying to make a simple IF function that checks if the date of an item is equal to today or not. Unfortunately I can't get it to work. I basically decides that the statement is false and doesn't show any content, even when it show. I am not getting any error either.
The code I use is following:
{% if item.valid_until.date == now.date %}
<div id="what_i_want_to_show">
CONTENT
</div>
{% endif %}
The content of valid_until is a DateTimeProperty from a Google App Engine app. Normally working with this in Django template doesn't cause any problems. Solutions from similar questions on SO didn't work so far. Am I missing something obvious?
UPDATE 1:
This statement runs in a loop on the result of a database query. Therefore doing the comparison in the view didn't work as far as I could see, since I would have to send the variable with each item.
There are 2 aproach on this case:
1st:
you can add a #property on model
Model:
from datetime import date
#property
def is_past_due(self):
return timezone.now() > self.valid_until # if valid until default is timezone.now else change it
Template:
{% if item.is_past_due %}
<!--In the past-->
{% else %}
{{ item.valid_until.date|date:"Y-m-d" }}
{% endif %}
2nd:
declare a today date with format on template
{% now "Y-m-d" as todays_date %}
{% if todays_date < item.valid_until.date|date:"Y-m-d" %}
<div id="what_i_want_to_show">
CONTENT
</div>
{% endif %}

Testing for presence of form instance in Jinja2

I'm using WTforms with Jinja2 and want to change my templates page title depending on whether I am creating a new instance of editing an existing form object.
This is what I wrote in the template:
{% block title %}{% if form.obj %}Edit{% else %}New{% endif %} Post{% endblock %}
What I expect to see:
if the form is filled out I expect to see "Edit Post" in the page title.
if the form is empty I expect to see "New Post" in the page title.
What I get: "New Post" in both instances.
Here is my PostHandler that is passing the form values.
def with_post(fun):
def decorate(self, post_id=None):
post = None
if post_id:
post = models.BlogPost.get_by_id(int(post_id))
if not post:
self.error(404)
return
fun(self, post)
return decorate
class PostHandler(BaseHandler):
def render_form(self, form):
self.render_to_response("edit.html", {'form': form})
#with_post
def get(self, post):
self.render_form(MyForm(obj=post))
#with_post
def post(self, post):
form = MyForm(formdata=self.request.POST, obj=post)
if post and form.validate():
form.populate_obj(post)
post.put()
post.publish()
self.render_to_response("published.html", {'post': post})
elif self.request.POST and form.validate():
post = models.BlogPost()
post.title = form.title.data
post.body = form.body.data
post.tags = form.tags.data
post.publish()
self.render_to_response("published.html", {'post': post})
else:
self.render_to_response('edit.html', {'form':form})
In short, all I'm trying to do is test whether the form is filled, and change my page title "New Post" or "Edit Post" accordingly.
While Form accepts a obj argument, it doesn't actually store it, it just uses that obj to fill in any blanks that formdata didn't provide. So when you ask Jinja2 {% if form.obj %} it's always going to be False, because there is never a obj property (unless you have a field that happens to be called obj of course).
If you're editing a post, you'll have an id to work with so you know which post to update in the database, so where are you currently storing that? Assuming you store it as a hidden field, you could just do:
{% if form.id.data == None %}Must be a New form {% endif %}
If you wanted to check if the entire form was empty, you could access the form.data dictionary, and make sure all the entries are None, although you need to be careful, because I know that FileField returns a u'None' instead of a real None, so you'd have to double check what Fields you care about.

database design for user/reviewer in django database model

i am kinda new to database design, but i would like to create three tables "User" and "Review" and "Topic" for a database in django.
I will try to explain it in detail here:
For example, I have User, Topic and Review models in models.py. one user can only write one review for one topic from other users.
let's say: Mike, John, Peter are the three Users.
Mike posted "Hello World" topic. John can only write one review for the topic "Hello World", Peter can also write one review for the same. John and Peter can not post another review for the same topic(they can only modify it). If Mike post another topic, John and Peter can post another review for the new topic. the same rule apply to other users.
please if you could, could you please provide some sample code for this issue? thanks a lot.
If you are trying to figure out how to set up your models.py, visit the django documentation, and look at Writing your first app (https://docs.djangoproject.com/en/dev/intro/tutorial01/). It goes from start to finish writing your first application and you will learn how the system works.
If you wanted more specifics for the paradigm of your case, here's what I would do. I would probably handle this in the view/template and submit/edit the review with Dajaxice calls to the database. If a review by the current user exists, it will show the data, if it doesn't it will be a blank entry that will use Dajax to submit the content. In the python method that the Dajax calls, you would try to find a review, and if one exists while attempting to add a new one, something went wrong and you can handle the error, otherwise it is saved for all to see.
For example, in models.py:
class User(models.Model):
name = models.CharField(max_length=128)
def __unicode__(self):
return self.name
class Review(models.Model):
title = models.CharField(max_length=64)
message = models.TextField()
topic = models.ForeignKey(Topic)
user = models.ForeignKey(User)
def __unicode__(self):
return self.title
class Topic
title = models.CharField(max_length=64)
message = models.TextField()
user = models.ForeignKey()
def __unicode__(self):
return self.title
in views.py:
class Post(models.Model): # This is defined a model, but not part of the data layer, it really is view code.
topic = None
your_review = None
other_reviews = None
def __unicode__(self):
return ''
def GetDetails(request):
posts = () # to be returned to and looped by the Template.
topics = Topic.objects.all().order_by('-posting_date') # posting_date descending.
for t in topics:
post = Post()
post.topic = t
post.your_review = Review.objects.filter(topic__id=t.id, user__id=<current_user_id>)
post.other_reviews = Review.objects.filter(topic__id=t.id, ~Q(user__id=<current_user_id>)
# Append to the posts array.
posts.append(post)
return render_to_response('index.htm', {'posts': posts}, context_instance=RequestContext(request))
in your index.htm:
{% if posts %}
{% for p in posts %}
<div>
<div class="title">{{ p.topic.title }}</div>
<div class="message">{{ p.topic.message }}</div>
<div class="other_reviews">
{% if p.other_reviews %}
{% for r in p.other_reviews %}
<div class="review_title">{{ r.title }}</div>
<div class="review_message">{{ r.message }}</div>
{% endfor %}
{% endif %}
<div>
<input type="text" value="{% if p.your_review %}{{ p.your_review.title }}{% endif %}">
</div>
<div>
<textarea>{% if p.your_review %}{{ p.your_review.message }}{% endif %}</textarea>
</div>
</div>
</div>
{% endfor %}
{% endif %}

Guestbook application

I made a very minor mod to the GqlQuery to retrieve only specified records using the
'where' keyword. The output, however, displays all entries from the guestbook db!
(I need to filter the data by author)
Guestbook5_datastore code:
#greetings = db.GqlQuery("SELECT * FROM Greeting ORDER BY date DESC LIMIT 10")
greetings = db.GqlQuery("SELECT * FROM Greeting where greeting.author='mike'")
index.html code:
{% for greeting in greetings %}
{% if greeting.author %}
<b>{{ greeting.author.nickname }}</b> wrote:
{% else %}
An Anonymous person wrote:
{% endif %}
<blockquote>{{ greeting.content|escape }}</blockquote>
{% endfor %}
Your author property is not a string, so I don't think you can do
greeting.author='mike'
I'm surprised that you wouldn't get an error telling you that though, rather than it returning them all!
You're attempting to filter based on a property of another entity, which would require a join. This isn't supported in App Engine.

Resources