Wagtail Version 1.0 Launched including Streamfield Feature!

Read more about it here : https://wagtail.io/blog/wagtail-10/ Exciting times in the CMS world. This version also contains an API built in. Hope to make some more blog posts very soon specifically on Streamfield, I have loads of ideas for using it in our current work, to make layout and integration of external information easier for our authors. For a good explanation of what the Streamfield is for check out https://torchbox.com/blog/streamfield-solves-content-management-conundrum/ which includes a video demo too!

Advertisements

My first DjangoCon

Screen Shot 2015-06-03 at 20.36.58DjangoCon Europe 2015 was my first DjangoCon, held in Cardiff City Hall and Cardiff University, luckily Cardiff is where I live so the traveling was easy!

There were some great talks and I learnt a lot, it’s given me a whole array of things I need to look into now.

Highlights for me were Baptiste Mispelon’s adventures in Djangoland, the tales of someone living a much bolder and exciting life than me 😦 Also Peter Finch the poet gave a very entertaining talk on Cardiff, which was a nice way to end the opening day’s morning talks.

After lunch we got to experience the 24-hour free #django emergency hotline, with the legendary doismellburning, apollo13 and MarkusH, who enacted it live in the room, I haven’t seen anything like this before and was more interesting than just a normal talk, plus it made me realise there is a lot more Django support and help out there that I haven’t known about.

Erik Romijn’s talk on Django security was very good, and I need to try his Online Django Security Checker on a few websites, it’s at https://www.ponycheckup.com/ check it out!

Ola Sendecka, the key note speaker on Tuesday gave a well presented talk on the dangers that rabbit holes present to the programmer, Stefan Foulis’ talk on Docker was interesting for someone who has so far only used Vagrant, Stefan talked about the advantages of Docker, and gave a thorough talk with many examples.

In the afternoon on Tuesday Kat Stevens presented her talk  ‘The Full Stack Octopus’, all about being the only developer in a company, this really made me appreciate getting to work in a team, I really admire Kat she has to cover a lot of areas in her job.

Loek Van Gent gave one of my favourite talks entitled  ‘True beauty is on the inside, but users are shallow’, all about front end development. There was a lot of talk at the conference generally about separating out the front end of app or sites, using things like Ember, the slides of his talk can be found here http://www.slideshare.net/LoekvanGent/shallow

Wednesday’s highlights for me were Ludvig Wadenstein’s talk ‘Better web applications through user testing’, this talk made me realise how we could be doing more regular user testing and the benefits, also he outlined a relatively straight forward way to do this on a monthly basis. Although the ‘Testing Chamber’ sounds a little frightening, check out his presentation slides here http://ludw.se/~ludw/user_testing.pdf

The after lunch CMS panel talked about the state of the CMS in Django, with Iacopo Spalletti and Tom Dyson from the django CMS and Wagtail teams, this was too short for my liking I would have liked more CMS talk, but that’s just because CMSes are my thing, and obviously I’m already a big Wagtail fan!

Ana Balica’s ‘Demystifying mixins with Django’ was really good, even during the talk It made me think of something I’d recently worked on that could benefit from mixins, and though I have used them, maybe I could use them a bit more actually.

DjangoCon 2015 was a great event and these are just some of my personal highlights!

 

A list of Wagtail’s Streamfield Icons

So in this example:

class TwitterBlock(blocks.StructBlock):
    twitter_box_username = blocks.CharBlock(required=True)
    twitter_box_widget_id = blocks.CharBlock(required=True)
    twitter_box_tweet_limit = blocks.CharBlock(required=True,max_length=2)
   

    class Meta:
        template = 'appname/blocks/twitter.html'
        icon = 'cogs'
        label = 'Twitter Widget'

Replace the ‘icon’ value with one from below.

wagtail
wagtail-inverse
cogs
doc-empty-inverse
doc-empty
edit
arrow-up
arrow-down
search
cross
folder-open-1
folder-inverse
mail
arrows-up-down
locked
unlocked
arrow-right
doc-full / file-text-alt
image / picture
doc-full-inverse
folder
plus
tag
folder-open-inverse
cog
tick
user
arrow-left
tick-inverse
plus-inverse
snippet
bold
italic
undo
repeat
list-ol
list-ul
link
radio-full
radio-empty
arrow-up-big
arrow-down-big
group
media
horizontalrule
password
download
order
grip
home
order-down
order-up
bin
spinner
pick
redirect
view
no-view
collapse-up
collapse-down
help
warning
success
date
time
form
site
placeholder
pilcrow
title
code
openquote

Adding a Twitter Widget for Wagtail’s new StreamField

Wagtail‘s latest feature is called the ‘StreamField’, currently only available in the 1.0 Beta 1 version, but clearly a bit of a Django CMS game changer.

It allows the editor to assemble a flow of page content of varying types (‘Blocks’ defined by the developer) in any order / combination they wish, kind of a Sir Trevor / Tumblr style affair.  It comes with a good selection of default block types, but it’s easy to repurpose these to create things like a Twitter Widget block.

In this guide I’m going to add a StreamField to an existing page class, which allows the user to add headers, paragraphs, images and a Twitter Widget to the page in any iteration they choose. The Twitter Widget I’m talking about is the one you can generate from your twitter settings by going to https://twitter.com/settings/widgets when logged in.

The user will be able to specify their twitter username, widget ID and a limit to how many latest tweets to display. The same principle could be applied for also adding Soundcloud, Instagram or whatever widgets, especially useful if you want a social media rich page, which has your content mixed in with feed content from the social media sites you use.

ss1

I like things laid out in easy clear steps, I’m a bit slow I guess 😦 so here goes… (Bare in mind the code may change, but this works for the current incarnation)

First off, add this class to your models.py

At the top I had to add (modify the edit_handlers Panels as you need, but you will obviously need the StreamFieldPanel for this)

from wagtail.wagtailcore.fields import StreamField
from wagtail.wagtailcore import blocks
from wagtail.wagtailimages.blocks import ImageChooserBlock
from wagtail.wagtailadmin.edit_handlers import FieldPanel, FieldRowPanel,MultiFieldPanel, \
 InlinePanel, PageChooserPanel, StreamFieldPanel

then

class TwitterBlock(blocks.StructBlock):
    twitter_box_username = blocks.CharBlock(required=True)
    twitter_box_widget_id = blocks.CharBlock(required=True)
    twitter_box_tweet_limit = blocks.CharBlock(required=True,max_length=2)

    class Meta:
        template = 'yourapp/blocks/twitter.html'
        icon = 'cogs'
        label = 'Twitter Widget'

We’re just making our own class based on the StructBlock (one of the built in types) and setting up the fields we need for our widget. The other thing I like is you can specify a template for your widget at this point, and choose an Icon, I’ve gone with a Cog, but there are plenty of others. The Label is useful to tell the users what it is once it’s part of the interface.

Next go to your page class where you want the StreamField to appear, and add the code below with other existing fields.

class ArticlePage(Page):

....
content = StreamField([
        ('heading', blocks.CharBlock(classname="full title")),
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock()),
        ('twitter', TwitterBlock()),
    ],null=True,blank=True)

....

This sets up our content field which can have a Header, Paragraph,Image or twitter widget.

 Then, so it appears in the admin, add it to the correct panel


....
ArticlePage.content_panels = [
    FieldPanel('title', classname="full title"),
    StreamFieldPanel('content'),

.....

At this point you’ll need to create and apply migrations for the new field. (If you’re replacing a body field with a Streamfield, you might want to think about adding a new field as we’ve done here rather than changing the existing body field. This will allow your editors to migrate the main page content from the old rich text field into the new StreamField one)


./manage.py makemigrations
python manage.py migrate

The final step is to obviously take care of the templating part!

You’ll need to add a new folder called ‘blocks’ in the template folder of your app, in the location we referenced earlier when defining the block, then create twitter.html as below.


<div class="twitter-widget">

{% if self.twitter_box_username %}

    <a class="twitter-timeline" href="https://twitter.com/{{ self.twitter_box_username }}" data-widget-id="{{ self.twitter_box_widget_id }}" data-tweet-limit="{{ self.twitter_box_tweet_limit }}">Tweets by {{ self.twitter_box_username}}</a>
    <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>

{% endif %}

</div>

Then of course the main template for your page that has the new StreamField content will need to be changed. Add this code below whereever you want the content StreamField to appear. There a few ways of outputting the blocks, I’ve chosen one from the Wagtail examples where I get a bit more control, for more examples go to http://docs.wagtail.io/en/v1.0b1/pages/streamfield.html#template-rendering


 <article>
 {% for block in self.content %}
     {% if block.block_type == 'heading' %}
         <h1>{{ block.value }}</h1>
     {% elif block.block_type == 'image' %}
         {% image block.value width-400 %}
     {% else %}
        <section class="block-{{ block.block_type }}">
            {{ block }}
        </section>
     {% endif %}
 {% endfor %}
 </article>

Hopefully this might help someone, let me know of any mistakes.

Caching JSON calls in Rails with Memcached and Dalli

I found lots of examples on how to use low level rails caching but they were basic, and it didn’t explain what config lines to change or how to implement it. Hopefully here’s a fuller example of caching some remote JSON. The requirement was that footer links were set in one place (our main site), then other secondary sites to use the same footer links from one source. We had our main django/wagtail site produce some JSON for our footer links, but obviously if our other sites just consume that JSON on every page request that’s going to be a lot of traffic. Hence the need for caching, if the secondary sites just grab the JSON once a day, then that’s going to be preferable.

Thanks to Ryan Tyler and Neil Williams for some of the code and their advice.

First of install Memcached, there are plenty of tutorials online for this, we won’t go into it here. We assume you’re running memcached with rails, not on a separate server.

Next install the Dalli Gem, add a line like gem “dalli”, “2.7.0” to your gemfile, we locked to a specific version that worked with our rails 3.2 version. Then bundle install, Dalli will allow us to interact with Memcached.

Add these lines to your production.rb config file, to test in development run in production mode, I’ve been told it’s best not to have caching stuff in the development environment config :

config.action_controller.perform_caching = true
config.cache_store = :dalli_store

Next setup some code in your application_controller.rb (Add some exception notification if required)
At the beginning add the code below, especially the before_filter to call the JSON caching code:

require ‘net/http’
require ‘json’

before_filter :main_footer

then the bit that does all the work:

def main_footer
Rails.cache.fetch("main_footer_json", :expires_in => 24.hours) do
begin
json = []
url = "http://www.yourothersite.com/some.json"
uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Get.new(url)
response = http.request(request)
json = JSON.parse(response.body)
json.to_json
rescue
json = []
json.to_json
end
end
@main_footer = Rails.cache.read("main_footer_json")

end

If it can’t find the remote JSON, it returns an empty JSON string. The JSON is kept in an instance variable that can be accessed in our footer template below.

Then in our _footer.html.erb we add this code to display the JSON as footer links :

<ul class="nav navbar-nav">
<% JSON.parse(@main_footer).each do |foot_main_link| %>
<li><a href="<%= foot_main_link[‘link’] %>">
<%= foot_main_link[‘title’] %></a></li>
<% end %>
</ul>

Upgrading Wagtail to use Django 1.7 locally using vagrant

These are the steps I followed to upgrade an existing Wagtail 0.8.3 which was using Django 1.6 to use 1.7. It wasn’t that obvious to me so I’ve detailed the steps here.

This assumes you’re using Vagrant locally, to test things, I cloned a new local version of our site in case it went wrong, which it did a few times before I got it right.

The steps may differ depending on how you set things up, and if you’re using additional/different Python modules etc

Firslty make these changes in the wagtail project :

Edit base.txt in requirements, and remove south, set django to 1.7, set taggit to use 0.12.0

Edit base.py in settings (and any other settings files) and remove south from installed apps.

Delete all the old south numbered migrations but leave the migration folder and the __init__.py file (Migrations are now handled with Django,and will be remade from scratch, so you don’t need the old south ones)

Also it’s worth checking through the Wagtail release notes to make sure you’ve checked the upgrade considerations related to Django 1.7, they can be found here : http://docs.wagtail.io/en/latest/releases/index.html

then to get it running locally using vagrant:

Go to your vagrant folder for the project,  do vagrant destroy if you’re not working from scratch.

Ignore this warning when it appears at various points :
RemovedInDjango18Warning: `MP_NodeManager.get_query_set` method should be renamed `get_queryset`.
class MP_NodeManager(models.Manager):

vagrant up
vagrant ssh
cd /vagrant

(next 3 steps assume you have existing data for your local vagrant in a sql file, skip otherwise)

dropdb -Upostgres <dbname>

createdb -Upostgres <dbname>

psql -Upostgres foxtail -f<dbname>.sql

./manage.py makemigrations
./manage.py migrate –fake

That’s it, these steps worked for me, hopefully they’ll be some help.

For more info on Django 1.7 and migrations – https://docs.djangoproject.com/en/1.7/topics/migrations/#upgrading-from-south