<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Matthew Daly&#039;s Blog</title>
	<atom:link href="http://www.matthewdaly.co.uk/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.matthewdaly.co.uk</link>
	<description>I&#039;m a web developer in East Anglia. This is my blog...</description>
	<lastBuildDate>Mon, 23 Apr 2012 20:55:40 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Yet another tutorial for building a blog using Python and Django &#8211; part 5</title>
		<link>http://www.matthewdaly.co.uk/2012/04/23/yet-another-tutorial-for-building-a-blog-using-python-and-django-part-5/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=yet-another-tutorial-for-building-a-blog-using-python-and-django-part-5</link>
		<comments>http://www.matthewdaly.co.uk/2012/04/23/yet-another-tutorial-for-building-a-blog-using-python-and-django-part-5/#comments</comments>
		<pubDate>Mon, 23 Apr 2012 20:55:40 +0000</pubDate>
		<dc:creator>Matthew</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[web development]]></category>

		<guid isPermaLink="false">http://www.matthewdaly.co.uk/?p=801</guid>
		<description><![CDATA[In this instalment I&#8217;ll be showing you how we can make our blogging engine a little nicer to look at by adding some CSS and images, as well as expanding on Django&#8217;s templating system. First of all, let&#8217;s add some CSS to our blog. When developing a web app with Django, you should place static [...]]]></description>
			<content:encoded><![CDATA[<p>In this instalment I&#8217;ll be showing you how we can make our blogging engine a little nicer to look at by adding some CSS and images, as well as expanding on Django&#8217;s templating system.</p>
<p>First of all, let&#8217;s add some CSS to our blog. When developing a web app with Django, you should place static files such as stylesheets and images in a folder inside your app (not project) folder called static. My project is called blog, and my app is called blogengine, so all my static content should go in blog/blogengine/static/. Here&#8217;s the stylesheet, which I&#8217;ve saved as style.css:</p>
<p><pre><code>body {
&nbsp;&nbsp;&nbsp;&nbsp;background-color: #f0f0f0;
&nbsp;&nbsp;&nbsp;&nbsp;font-family: Arial, Helvetica, sans-serif;
}

#main {
&nbsp;&nbsp;&nbsp;&nbsp;width: 800px;
&nbsp;&nbsp;&nbsp;&nbsp;height: 100%;
&nbsp;&nbsp;&nbsp;&nbsp;margin: 50px auto;
}

ul#pageList {
&nbsp;&nbsp;&nbsp;&nbsp;margin: 0px;
&nbsp;&nbsp;&nbsp;&nbsp;padding: 10px 0px 10px 0px;
}

ul#pageList li {
&nbsp;&nbsp;&nbsp;&nbsp;display: inline;
&nbsp;&nbsp;&nbsp;&nbsp;margin-right: 10px;
&nbsp;&nbsp;&nbsp;&nbsp;font-size: 18px;
}

.post, .page {
&nbsp;&nbsp;&nbsp;&nbsp;width: 600px;
&nbsp;&nbsp;&nbsp;&nbsp;padding: 20px;
&nbsp;&nbsp;&nbsp;&nbsp;margin-bottom: 20px;
&nbsp;&nbsp;&nbsp;&nbsp;background-color: #ffffff;
}
</code></pre></p>
<p>In the same folder, I have a PNG icon for an RSS feed, and if you had some JavaScript files you wanted to use (such as a copy of jQuery), you would put them here too. Note that there&#8217;s nothing to stop you creating subfolders within /static, and in fact I would recommend you do so for any future project so you can separate out images, CSS and JavaScript easily.</p>
<p>With that done, we now need to change our templates to make use of this CSS. Here&#8217;s what header.html should look like:<br />
<pre><code>&lt;html&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;head&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;title&gt;My Django Blog&lt;/title&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/static/style.css&quot; /&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/head&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;body&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;div id=&quot;main&quot;&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;My Django Blog&lt;/h1&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/feeds/posts/&quot;&gt;&lt;img src=&quot;/static/rss.png&quot; width=&quot;50px&quot; height=&quot;50px&quot;&gt;&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ul id=&quot;pageList&quot;&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;li&gt;&lt;a href=&quot;/&quot;&gt;Home&lt;/a&gt;&lt;/li&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% load flatpages %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% get_flatpages as flatpages %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% for flatpage in flatpages %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;li&gt;&lt;a href=&quot;{{ flatpage.url }}&quot;&gt;{{ flatpage.title }}&lt;/a&gt;&lt;/li&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endfor %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/ul&gt;</code></pre></p>
<p>Next, here&#8217;s footer.html:<br />
<pre><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/div&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/body&gt;
&lt;/html&gt;</code></pre></p>
<p>Now here&#8217;s category.html:<br />
<pre><code>{% include &#039;header.html&#039; %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% load comments %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;Posts for {{ category.title }}&lt;/h1&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if posts %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% for post in posts %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;div class=&quot;post&quot;&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;&lt;a href=&quot;{{ post.get_absolute_url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/h1&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ post.text }}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% get_comment_count for post as comment_count %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h3&gt;Comments: {{ comment_count }}&lt;/h3&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/div&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endfor %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;br /&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page.has_previous %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page.previous_page_number }}/&quot;&gt;Previous Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page.has_next %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page.next_page_number }}/&quot;&gt;Next Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% else %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;p&gt;No posts matched&lt;/p&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
{% include &#039;footer.html&#039; %}</code></pre></p>
<p>Then, posts.html:</p>
<p><pre><code>{% include &#039;header.html&#039; %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% load comments %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if posts %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% for post in posts %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;div class=&quot;post&quot;&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;&lt;a href=&quot;{{ post.get_absolute_url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/h1&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ post.text }}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% get_comment_count for post as comment_count %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h3&gt;Comments: {{ comment_count }}&lt;/h3&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/div&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endfor %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;br /&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page.has_previous %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page.previous_page_number }}/&quot;&gt;Previous Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page.has_next %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page.next_page_number }}/&quot;&gt;Next Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% else %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;div class=&quot;post&quot;&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;p&gt;No posts matched&lt;/p&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/div&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
{% include &#039;footer.html&#039; %}</code></pre><br />
Here&#8217;s single.html:<br />
<pre><code>{% include &#039;header.html&#039; %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% load comments %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% for post in posts %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;div class=&quot;post&quot;&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;&lt;a href=&quot;{{ post.get_absolute_url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/h1&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h3&gt;{{ post.pub_date }}&lt;/h3&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ post.text }}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h3&gt;By {{ post.author.first_name }} {{ post.author.last_name }}&lt;/h3&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h3&gt;Categories: {% for category in post.categories.all %} {{ category.title }} {% endfor %}&lt;/h3&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% get_comment_count for post as comment_count %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h3&gt;Comments: {{ comment_count }}&lt;/h3&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ol&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% get_comment_list for post as comments %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% for comment in comments %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;li&gt;{{ comment }}&lt;/li&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/ol&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endfor %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% render_comment_form for post %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endfor %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/div&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;br /&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page.has_previous %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page.previous_page_number }}/&quot;&gt;Previous Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page.has_next %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page.next_page_number }}/&quot;&gt;Next Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
{% include &#039;footer.html&#039; %}</code></pre><br />
And finally, flatpages/default.html:<br />
<pre><code>{% include &#039;header.html&#039; %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;div class=&quot;page&quot;&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;{{ flatpage.title }}&lt;/h1&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ flatpage.content }}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/div&gt;
{% include &#039;footer.html&#039; %}</code></pre></p>
<p>Phew! There&#8217;s quite a lot there, so you may wish to grab these files from the GitHub repository rather than enter them yourself.</p>
<p>Now, all of the references to the CSS or image file need to refer to the /static folder under the root of the web server. Here&#8217;s the reference to our stylesheet:<br />
<code>&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/static/style.css&quot; /&gt;</code><br />
And here&#8217;s where we get the image:<br />
<code>&lt;a href=&quot;/feeds/posts/&quot;&gt;&lt;img src=&quot;/static/rss.png&quot; width=&quot;50px&quot; height=&quot;50px&quot;&gt;&lt;/a&gt;</code><br />
All of our static files can be referenced via the /static folder by default, without needing to set up a rule to cover them in urls.py.</p>
<p>One other point worth noting is that we&#8217;ve added some code to the header to display links to all of the flat pages. This particular snippet of code in header.html is noteworthy:<br />
<pre><code>{% load flatpages %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% get_flatpages as flatpages %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% for flatpage in flatpages %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;li&gt;&lt;a href=&quot;{{ flatpage.url }}&quot;&gt;{{ flatpage.title }}&lt;/a&gt;&lt;/li&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endfor %}</code></pre></p>
<p>Here, we first of all load the flat pages. Then we retrieve them, and loop through each of them. For each page, we create a new list item containing a link to the flat page, with the text being the flat page&#8217;s title. Note that we&#8217;re just referring to each flatpage object&#8217;s attributes here. Then we end the for loop.</p>
<p>The only problem with this is that all of the pages except the flat pages are handled by the blogengine application, not the flatpages one, so we can&#8217;t get the values for the flat pages, so we need to amend blogengine/views.py. Open it and add the following line near the top:<br />
<code>from django.contrib.flatpages.models import FlatPage</code><br />
Now, nowhere else in the view is the FlatPage application needed, but it&#8217;s required in the template, so by importing it here we make it available in the template.</p>
<p>With that done, our Django-powered blog is beginning to look a bit more presentable, so I&#8217;ll leave it to you to style it however you wish, using this as a starting point. The blog is now pretty much feature-complete, however there&#8217;s one more thing I&#8217;d like to demonstrate before we finish up, namely generic views.</p>
<p>As you may have gathered by now, Django uses slightly different terminology to many other web development frameworks. Although it can be considered an MVC (Model-View-Controller) framework like many others, it&#8217;s generally referred to as an MTV (Model-Template-View) framework, with views containing the logic needed to present the data. While Django ships with a number of built-in applications to do certain repetitive tasks easily, not every task lends itself well to being handled by one generic application. However, these tasks may still require something similar be implemented over and over again, and that&#8217;s what generic views are for.</p>
<p>We don&#8217;t yet have a list of all of the available categories, so let&#8217;s use a generic view to do that. In urls.py, add the following lines at the top:<br />
<pre><code>from django.views.generic import ListView
from blogengine.models import Category</code></pre><br />
Then, add the following lines at the top of the section for categories:<br />
<pre><code>&nbsp;&nbsp;&nbsp;&nbsp;url(r&#039;^categories/?$&#039;, ListView.as_view(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;model=Category,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)),</code></pre><br />
Then, go into your templates folder and create a new folder in there called blogengine (or whatever you&#8217;re calling your blog application). In there, create a new file called category_list.html and enter the following code in it:<br />
<pre><code>{% include &#039;header.html&#039; %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% for category in object_list %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h3&gt;{{ category.title }}&lt;/h3&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;p&gt;{{ category.description }}&lt;/p&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endfor %}
{% include &#039;footer.html&#039; %}</code></pre><br />
Now, ensure the development server is running, and go to http://127.0.0.1:8000/categories/, and you should see a list of your categories.</p>
<p>Now, you didn&#8217;t write a view for this at all. Instead, this is handled by a generic view. In urls.py, we imported the ListView generic view, which is nothing more than a list of objects. We then import the Category model. Then, we define a URLconf that maps the categories/ url to ListView, which displays a list of all the Category objects. The template used is determined automatically, and we create that template as normal. Note that in the template we refer to object_list &#8211; this demonstrates that we&#8217;re referring to the objects passed through generically, and in theory this same template could display any objects with attributes called title and description.</p>
<p>Now, it probably won&#8217;t have escaped your notice that a blog is effectively a list of posts, so can&#8217;t we use a generic view to display them? Well, yes we can! So why don&#8217;t we cut down on the amount of code we need to maintain and use a generic view, rather than writing our own view?</p>
<p>Go into blogengine/views.py and delete the getPosts function in its entirety. Next, go into urls.py and delete the part that deals with showing the posts (the two lines just under the Home page comment), and replace them with this:<br />
<pre><code>&nbsp;&nbsp;&nbsp;&nbsp;url(r&#039;^(?P&lt;page&gt;\d+)?/?$&#039;, ListView.as_view(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;model=Post,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;paginate_by=5,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)),</code></pre><br />
Note here that we specify how many items we paginate by. The ListView generic view supports pagination, making it ideally suited for any list of objects that may be spread across multiple pages &#8211; you just import the model you want and pass it through in the model parameter.</p>
<p>We also need to import the Post object in urls.py. Amend the line where you imported the Category model as follows:<br />
<code>from blogengine.models import Category, Post</code><br />
Then go into your templates directory and create a new subdirectory called blogengine. Move posts.html into there and rename it post_list.html, then amend it to look like the following:<br />
<pre><code>{% include &#039;header.html&#039; %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% load comments %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if object_list %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% for post in object_list %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;div class=&quot;post&quot;&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;&lt;a href=&quot;{{ post.get_absolute_url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/h1&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ post.text }}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% get_comment_count for post as comment_count %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h3&gt;Comments: {{ comment_count }}&lt;/h3&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/div&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endfor %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;br /&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page_obj.has_previous %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page_obj.previous_page_number }}/&quot;&gt;Previous Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page_obj.has_next %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page_obj.next_page_number }}/&quot;&gt;Next Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% else %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;div class=&quot;post&quot;&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;p&gt;No posts matched&lt;/p&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/div&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
{% include &#039;footer.html&#039; %}</code></pre><br />
The changes here are minimal, just adjusting the names of objects to what the generic view uses. Now, if you refresh the browser, you should be able to see your blog posts, only now they&#8217;re being handled by Django&#8217;s ListView generic view. But they&#8217;re in the wrong order, so go into blogengine/models.py and add this code to the Post model:<br />
<pre><code>&nbsp;&nbsp;&nbsp;&nbsp;class Meta:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ordering = [&quot;-pub_date&quot;]</code></pre><br />
This defines the order the Post objects should be in the model, rather than the view. If you now refresh the browser, they should be in the right order.</p>
<p>A ListView is only one of the generic views available in Django. There are others that are useful under other circumstances, but they&#8217;re beyond the scope of this tutorial, so I suggest that if you&#8217;re interested, you take the time to learn more about them on your own. They can save you a lot of time and effort if used well.</p>
<p>Sadly, that brings this series of tutorials to an end. I hope you&#8217;ve enjoyed learning about Django, and I hope you&#8217;ll be inspired to build something cool with it! As always, the code is available on <a href="https://github.com/matthewbdaly/Django-Tutorial-Blog">GitHub</a>, so feel free to download it, use it as the basis for your own projects, or whatever else you&#8217;d like to do with it.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.matthewdaly.co.uk/2012/04/23/yet-another-tutorial-for-building-a-blog-using-python-and-django-part-5/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Yet another tutorial for building a blog using Python and Django &#8211; part 4</title>
		<link>http://www.matthewdaly.co.uk/2012/03/29/yet-another-tutorial-for-building-a-blog-using-python-and-django-part-4/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=yet-another-tutorial-for-building-a-blog-using-python-and-django-part-4</link>
		<comments>http://www.matthewdaly.co.uk/2012/03/29/yet-another-tutorial-for-building-a-blog-using-python-and-django-part-4/#comments</comments>
		<pubDate>Thu, 29 Mar 2012 20:29:59 +0000</pubDate>
		<dc:creator>Matthew</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[web development]]></category>

		<guid isPermaLink="false">http://www.matthewdaly.co.uk/?p=780</guid>
		<description><![CDATA[Welcome back! In this tutorial we&#8217;ll continue extending our Django-powered blogging engine. We&#8217;ll add the capability to assign blog posts to categories, and comment on posts. We&#8217;ll also generate an RSS feed for our blog posts. Categories are somewhat tougher to implement than most of what we&#8217;ve done beforehand. One category can be assigned to [...]]]></description>
			<content:encoded><![CDATA[<p>Welcome back! In this tutorial we&#8217;ll continue extending our Django-powered blogging engine. We&#8217;ll add the capability to assign blog posts to categories, and comment on posts. We&#8217;ll also generate an RSS feed for our blog posts.</p>
<p>Categories are somewhat tougher to implement than most of what we&#8217;ve done beforehand. One category can be assigned to many blog posts, and many categories can be assigned to one blog post, so this relationship is described as a &#8220;many to many relationship&#8221; when drawing up the database structure. What it means is that you can&#8217;t directly map categories onto posts and vice verse &#8211; you have to create an intermediate database table for the relationship between posts and categories.</p>
<p>Here&#8217;s what your models.py should look like:</p>
<p><pre><code>from django.db import models 
from django.contrib.auth.models import User 
 
# Create your models here. 
class Category(models.Model): 
&nbsp;&nbsp;&nbsp;&nbsp;title = models.CharField(max_length=200) 
&nbsp;&nbsp;&nbsp;&nbsp;slug = models.SlugField(max_length=40, unique=True) 
&nbsp;&nbsp;&nbsp;&nbsp;description = models.TextField() 
 
&nbsp;&nbsp;&nbsp;&nbsp;class Meta: 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;verbose_name_plural = &quot;Categories&quot; 
 
&nbsp;&nbsp;&nbsp;&nbsp;def __unicode__(self): 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return self.title

&nbsp;&nbsp;&nbsp;&nbsp;def get_absolute_url(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return &quot;/categories/%s/&quot; % self.slug

class Post(models.Model):
&nbsp;&nbsp;&nbsp;&nbsp;title = models.CharField(max_length=200)
&nbsp;&nbsp;&nbsp;&nbsp;pub_date = models.DateTimeField()
&nbsp;&nbsp;&nbsp;&nbsp;text = models.TextField()
&nbsp;&nbsp;&nbsp;&nbsp;slug = models.SlugField(max_length=40, unique=True)
&nbsp;&nbsp;&nbsp;&nbsp;author = models.ForeignKey(User)
&nbsp;&nbsp;&nbsp;&nbsp;categories = models.ManyToManyField(Category, blank=True, null=True, through=&#039;CategoryToPost&#039;)

&nbsp;&nbsp;&nbsp;&nbsp;def __unicode__(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return self.title

&nbsp;&nbsp;&nbsp;&nbsp;def get_absolute_url(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return &quot;/%s/%s/%s/&quot; % (self.pub_date.year, self.pub_date.month, self.slug)

class CategoryToPost(models.Model):
&nbsp;&nbsp;&nbsp;&nbsp;post = models.ForeignKey(Post)
&nbsp;&nbsp;&nbsp;&nbsp;category = models.ForeignKey(Category)</code></pre></p>
<p>We&#8217;re adding quite a bit of new code here. First of all we&#8217;re defining a new model called Category. Each category has a title, a description, and a slug (so we can have a dedicated page for each category). As usual, we define methods for __unicode__ and get_absolute_url, but also note the class Meta. Here we&#8217;re defining some metadata for the class (ie, data about the data). The only thing we do here is essentially telling the admin interface that the plural of Category is not &#8220;Categorys&#8221; but &#8220;Categories&#8221;.</p>
<p>Then, in Post we add an additional field called Category, which we define as a ManyToManyField. Note the parameters passed through &#8211; we&#8217;re saying here that a post need not be assigned a category, and that CategoryToPost should be used as an intermediate table to link posts to categories.</p>
<p>Finally, we define the aforementioned CategoryToPost model, which has two fields, post and category. Both of these are foreign keys, mapping to a blog post and a category respectively. By creating entries in this table, a link can be created between a post and a category.</p>
<p>With our model changed, it&#8217;s time to update our admin.py as well:</p>
<p><pre><code>import models
from django.contrib import admin
from django.contrib.auth.models import User

class CategoryAdmin(admin.ModelAdmin):
&nbsp;&nbsp;&nbsp;&nbsp;prepopulated_fields = {&quot;slug&quot;: (&quot;title&quot;,)}

class CategoryToPostInline(admin.TabularInline):
&nbsp;&nbsp;&nbsp;&nbsp;model = models.CategoryToPost
&nbsp;&nbsp;&nbsp;&nbsp;extra = 1 

class PostAdmin(admin.ModelAdmin):
&nbsp;&nbsp;&nbsp;&nbsp;prepopulated_fields = {&quot;slug&quot;: (&quot;title&quot;,)}
&nbsp;&nbsp;&nbsp;&nbsp;exclude = (&#039;author&#039;,)
&nbsp;&nbsp;&nbsp;&nbsp;inlines = [CategoryToPostInline]

&nbsp;&nbsp;&nbsp;&nbsp;def save_model(self, request, obj, form, change):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj.author = request.user
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj.save()

admin.site.register(models.Post, PostAdmin)
admin.site.register(models.Category, CategoryAdmin)</code></pre></p>
<p>Here we define a new class called CategoryAdmin, which details how we&#8217;re changing the admin interface for Category from the defaults generated from the fields provided. The only change we make here is that we prepopulate the slug field from the title, much like we did with blog posts.</p>
<p>Next, we define an inline for the relationships between categories and post, called CategoryToPostInline. This is a new concept &#8211; essentially it means that the category to post relationships can be defined in another model&#8217;s admin interface. We define the model this applies to, and that by default we will only add one additional field for adding categories when writing or editing a post (though users can add as many as they wish, or none). Note that the model this is based on is admin.TabularInline &#8211; this represents a tabular layout. If you prefer, you can use an alternative layout by using StackedInline instead.</p>
<p>Then, in PostAdmin we add our newly declared CategoryToPostInline to the PostAdmin class as an inline. Finally, at the bottom we register Category with the admin interface, so we can create and manage categories easily.</p>
<p>With that done, it&#8217;s time to edit our views.py:</p>
<p><pre><code># Create your views here.
from django.shortcuts import render_to_response
from django.core.paginator import Paginator, EmptyPage
from blogengine.models import Post, Category

def getPosts(request, selected_page=1):
&nbsp;&nbsp;&nbsp;&nbsp;# Get all blog posts
&nbsp;&nbsp;&nbsp;&nbsp;posts = Post.objects.all().order_by(&#039;-pub_date&#039;)

&nbsp;&nbsp;&nbsp;&nbsp;# Add pagination
&nbsp;&nbsp;&nbsp;&nbsp;pages = Paginator(posts, 5)

&nbsp;&nbsp;&nbsp;&nbsp;# Get the specified page
&nbsp;&nbsp;&nbsp;&nbsp;try:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;returned_page = pages.page(selected_page)
&nbsp;&nbsp;&nbsp;&nbsp;except EmptyPage:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;returned_page = pages.page(pages.num_pages)

&nbsp;&nbsp;&nbsp;&nbsp;# Display all the posts
&nbsp;&nbsp;&nbsp;&nbsp;return render_to_response(&#039;posts.html&#039;, { &#039;posts&#039;:returned_page.object_list, &#039;page&#039;:returned_page})

def getPost(request, postSlug):
&nbsp;&nbsp;&nbsp;&nbsp;# Get specified post
&nbsp;&nbsp;&nbsp;&nbsp;post = Post.objects.filter(slug=postSlug)

&nbsp;&nbsp;&nbsp;&nbsp;# Display specified post
&nbsp;&nbsp;&nbsp;&nbsp;return render_to_response(&#039;single.html&#039;, { &#039;posts&#039;:post})

def getCategory(request, categorySlug, selected_page=1):
&nbsp;&nbsp;&nbsp;&nbsp;# Get specified category
&nbsp;&nbsp;&nbsp;&nbsp;posts = Post.objects.all().order_by(&#039;-pub_date&#039;)
&nbsp;&nbsp;&nbsp;&nbsp;category_posts = []
&nbsp;&nbsp;&nbsp;&nbsp;for post in posts:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if post.categories.filter(slug=categorySlug):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;category_posts.append(post)

&nbsp;&nbsp;&nbsp;&nbsp;# Add pagination
&nbsp;&nbsp;&nbsp;&nbsp;pages = Paginator(category_posts, 5)

&nbsp;&nbsp;&nbsp;&nbsp;# Get the category
&nbsp;&nbsp;&nbsp;&nbsp;category = Category.objects.filter(slug=categorySlug)[0]

&nbsp;&nbsp;&nbsp;&nbsp;# Get the specified page
&nbsp;&nbsp;&nbsp;&nbsp;try:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;returned_page = pages.page(selected_page)
&nbsp;&nbsp;&nbsp;&nbsp;except EmptyPage:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;returned_page = pages.page(pages.num_pages)

&nbsp;&nbsp;&nbsp;&nbsp;# Display all the posts
&nbsp;&nbsp;&nbsp;&nbsp;return render_to_response(&#039;category.html&#039;, { &#039;posts&#039;: returned_page.object_list, &#039;page&#039;: returned_page, &#039;category&#039;: category})</code></pre></p>
<p>Here we import the Category model as well as the Post model. Then, the only additional change we need to make is to add a brand new getCategory view function. Note that this is quite similar to the getPosts function &#8211; we set up pagination in the same way, and rather than get all the posts, we get just those in the specified category. Also note that we&#8217;re using the template category.html rather than posts.html here, and we pass through category as well as posts and page when we return the render_to_response.</p>
<p>The next change we need to make is adding category.html. Go into your template directory and save the code below as category.html:</p>
<p><pre><code>{% include &#039;header.html&#039; %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;Posts for {{ category.title }}&lt;/h1&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if posts %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% for post in posts %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;&lt;a href=&quot;{{ post.get_absolute_url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/h1&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ post.text }}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endfor %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;br /&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page.has_previous %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page.previous_page_number }}/&quot;&gt;Previous Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page.has_next %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page.next_page_number }}/&quot;&gt;Next Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% else %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;p&gt;No posts matched&lt;/p&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
{% include &#039;footer.html&#039; %}</code></pre></p>
<p>With our template in place, the last step is to add an appropriate URLconf. Edit urls.py to look like this:</p>
<p><pre><code>from django.conf.urls.defaults import patterns, include, url 

# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns(&#039;&#039;,
&nbsp;&nbsp;&nbsp;&nbsp;# Examples:
&nbsp;&nbsp;&nbsp;&nbsp;# url(r&#039;^$&#039;, &#039;blog.views.home&#039;, name=&#039;home&#039;),
&nbsp;&nbsp;&nbsp;&nbsp;# url(r&#039;^blog/&#039;, include(&#039;blog.foo.urls&#039;)),

&nbsp;&nbsp;&nbsp;&nbsp;# Uncomment the admin/doc line below to enable admin documentation:
&nbsp;&nbsp;&nbsp;&nbsp;# url(r&#039;^admin/doc/&#039;, include(&#039;django.contrib.admindocs.urls&#039;)),

&nbsp;&nbsp;&nbsp;&nbsp;# Uncomment the next line to enable the admin:
&nbsp;&nbsp;&nbsp;&nbsp;url(r&#039;^admin/&#039;, include(admin.site.urls)),

&nbsp;&nbsp;&nbsp;&nbsp;# Home page
&nbsp;&nbsp;&nbsp;&nbsp;url(r&#039;^$&#039;, &#039;blogengine.views.getPosts&#039;),
&nbsp;&nbsp;&nbsp;&nbsp;url(r&#039;^(?P&lt;selected_page&gt;\d+)/?$&#039;, &#039;blogengine.views.getPosts&#039;),

&nbsp;&nbsp;&nbsp;&nbsp;# Blog posts
&nbsp;&nbsp;&nbsp;&nbsp;url(r&#039;^\d{4}/\d{1,2}/(?P&lt;postSlug&gt;[-a-zA-Z0-9]+)/?$&#039;, &#039;blogengine.views.getPost&#039;),

&nbsp;&nbsp;&nbsp;&nbsp;# Categories
&nbsp;&nbsp;&nbsp;&nbsp;url(r&#039;^categories/(?P&lt;categorySlug&gt;\w+)/?$&#039;, &#039;blogengine.views.getCategory&#039;),
&nbsp;&nbsp;&nbsp;&nbsp;url(r&#039;^categories/(?P&lt;categorySlug&gt;\w+)/(?P&lt;selected_page&gt;\d+)/?$&#039;, &#039;blogengine.views.getCategory&#039;),
&nbsp;&nbsp;&nbsp;&nbsp;# Flat pages
&nbsp;&nbsp;&nbsp;&nbsp;url(r&#039;&#039;, include(&#039;django.contrib.flatpages.urls&#039;)),
)</code></pre></p>
<p>Now, if you run python manage.py syncdb again, the category system should be up and running.</p>
<p>The next step is to add the facility to handle comments. Again, Django has its own application built in for handling comments, so go into setings.py and enter the following under INSTALLED_APPS:</p>
<p><code>&nbsp;&nbsp;&nbsp;&nbsp; &#039;django.contrib.comments&#039;,</code></p>
<p>Then run python manage.py syncdb again to generate the appropriate database tables. You&#8217;ll also need to amend urls.py to provide a dedicated URL for comments:</p>
<p><pre><code>&nbsp;&nbsp;&nbsp;&nbsp;# Comments
&nbsp;&nbsp;&nbsp;&nbsp;url(r&#039;^comments/&#039;, include(&#039;django.contrib.comments.urls&#039;)),</code></pre></p>
<p>Place this before the URLconf for the flat pages.</p>
<p>Comments can be attached to any type of content, but we only want to attach them to blog posts, and they should only be visible in the single post template. But first of all, let&#8217;s add a comment count to posts in posts.html and category.html. Replace posts.html with this:</p>
<p><pre><code>{% include &#039;header.html&#039; %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% load comments %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if posts %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% for post in posts %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;&lt;a href=&quot;{{ post.get_absolute_url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/h1&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ post.text }}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% get_comment_count for post as comment_count %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h3&gt;Comments: {{ comment_count }}&lt;/h3&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endfor %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;br /&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page.has_previous %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page.previous_page_number }}/&quot;&gt;Previous Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page.has_next %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page.next_page_number }}/&quot;&gt;Next Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% else %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;p&gt;No posts matched&lt;/p&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
{% include &#039;footer.html&#039; %}</code></pre></p>
<p>And replace category.html with this:</p>
<p><pre><code>{% include &#039;header.html&#039; %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% load comments %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;Posts for {{ category.title }}&lt;/h1&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if posts %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% for post in posts %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;&lt;a href=&quot;{{ post.get_absolute_url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/h1&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ post.text }}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% get_comment_count for post as comment_count %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h3&gt;Comments: {{ comment_count }}&lt;/h3&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endfor %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;br /&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page.has_previous %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page.previous_page_number }}/&quot;&gt;Previous Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page.has_next %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page.next_page_number }}/&quot;&gt;Next Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% else %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;p&gt;No posts matched&lt;/p&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
{% include &#039;footer.html&#039; %}</code></pre></p>
<p>The only significant changes here are that at the top we load comments, and underneath the post text we get the comment count for each post as the variable comment_count, then we display it underneath.</p>
<p>Now, we want to go further with our single post template. As well as a comment count, we want to add the actual comments themselves. Finally, we need a form for adding comments &#8211; in theory you can use the admin interface for doing this, but it&#8217;s very unlikely you&#8217;d want to do so. Open up single.html and edit it to look like this:</p>
<p><pre><code>{% include &#039;header.html&#039; %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% load comments %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% for post in posts %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;&lt;a href=&quot;{{ post.get_absolute_url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/h1&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h3&gt;{{ post.pub_date }}&lt;/h3&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ post.text }}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h3&gt;By {{ post.author.first_name }} {{ post.author.last_name }}&lt;/h3&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h3&gt;Categories: {% for category in post.categories.all %} {{ category.title }} {% endfor %}&lt;/h3&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% get_comment_count for post as comment_count %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h3&gt;Comments: {{ comment_count }}&lt;/h3&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ol&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% get_comment_list for post as comments %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% for comment in comments %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;li&gt;{{ comment }}&lt;/li&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/ol&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endfor %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% render_comment_form for post %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endfor %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;br /&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page.has_previous %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page.previous_page_number }}/&quot;&gt;Previous Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page.has_next %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page.next_page_number }}/&quot;&gt;Next Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
{% include &#039;footer.html&#039; %}</code></pre></p>
<p>This includes the same changes as the other two templates, so we load comments and display the comment count. Afterwards, we get the comment list for this post as comments, and then loop through the comments, showing them in an ordered list. Afterwards, we then use render_comment_form to show the default comment form for this post. If you&#8217;d prefer to create your own comment form, you can use get_comment_form instead to get a form object you can use in the template.</p>
<p>You&#8217;ll also need to make some minor changes to the view to get the form working. Save single.html and open blogengine/views.py and add the following line of code to your import statements:</p>
<p><code>from django.template import RequestContext</code></p>
<p>Then, amend the final line of the getPost function as follows:</p>
<p><code>&nbsp;&nbsp;&nbsp;&nbsp;return render_to_response(&#039;single.html&#039;, { &#039;posts&#039;:post}, context_instanc&nbsp;&nbsp;&nbsp;&nbsp;e=RequestContext(request))</code></p>
<p>The reason this needs to be changed is that the comment form includes the {% csrf_token %} tag, which requires information from the request object, and in order to do so rather than the default context, you need to pass through a RequestContext object instead, but don&#8217;t worry too much about the details.</p>
<p>If you now ensure the development server is running and visit a blog post, you should now see that you can post comments. If you want to enhance this very basic comment form, take a look at the excellent documentation on the Django website. Alternatively, there are a number of third-party comment services, such as Disqus and IntenseDebate that can handle comments for you and just require you to paste a snippet of code into whatever template you want to enable comments on, and these may be more convenient.</p>
<p>Finally for this lesson, as promised, we&#8217;ll implement our RSS feed. Again, there&#8217;s an application bundled with Django that will do this &#8211; the syndication framework. Open settings.py and paste the following line in at the bottom of your INSTALLED_APPS:</p>
<p><code>&nbsp;&nbsp;&nbsp;&nbsp; &#039;django.contrib.syndication&#039;,</code></p>
<p>Save the file and run python manage.py syncdb to add the appropriate tables to your database. Then, we need to add a URLconf for the RSS feed. We&#8217;ll allow a consistent naming scheme for RSS feeds, so this will be /feeds/posts, and if you wanted to you could add /feeds/comments, for instance. Add this to you urls.py, before the url for flat pages:</p>
<p><pre><code>&nbsp;&nbsp;&nbsp;&nbsp;# RSS feeds
&nbsp;&nbsp;&nbsp;&nbsp;url(r&#039;^feeds/posts/$&#039;, PostsFeed()),</code></pre></p>
<p>We&#8217;ll also need to tell urls.py where to find PostsFeed(). In this case, we&#8217;re going to put it in the view, so add this import line near the top:</p>
<p><code>from blogengine.views import PostsFeed</code></p>
<p>Now open blogengine/views.py and add the following line to the import statements at the top:</p>
<p><code>from django.contrib.syndication.views import Feed</code></p>
<p>Then add the following class declaration to the bottom:</p>
<p><pre><code>class PostsFeed(Feed):
&nbsp;&nbsp;&nbsp;&nbsp;title = &quot;My Django Blog posts&quot;
&nbsp;&nbsp;&nbsp;&nbsp;link = &quot;feeds/posts/&quot;
&nbsp;&nbsp;&nbsp;&nbsp;description = &quot;Posts from My Django Blog&quot;

&nbsp;&nbsp;&nbsp;&nbsp;def items(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return Post.objects.order_by(&#039;-pub_date&#039;)[:5]

&nbsp;&nbsp;&nbsp;&nbsp;def item_title(self, item):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return item.title

&nbsp;&nbsp;&nbsp;&nbsp;def item_description(self, item):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return item.text</code></pre></p>
<p>This is pretty simple. We import the Feed class from thew views provided by the syndication framework, then we base PostsFeed on Feed. We set the title, the link for the feed, and a description for the feed. Then we get the last 5 Post objects in reverse chronological order, and we define each item&#8217;s title as the post title. and each item&#8217;s description as the text of the post. From here&#8217; it&#8217;s pretty easy to see how you could create feeds based on comments, or pretty much any other object that might exist in the database.</p>
<p>And with that done, our blogging engine is pretty-much feature-complete. We have blog posts with comments, categories, an RSS feed, and flat pages, but the look and feel of the site definitely needs some attention. Next time, we&#8217;ll make our blogging engine look a little nicer. Once again, the code is available on GitHub in case you find that more convenient.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.matthewdaly.co.uk/2012/03/29/yet-another-tutorial-for-building-a-blog-using-python-and-django-part-4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Yet another tutorial for building a blog using Python and Django &#8211; Part 3</title>
		<link>http://www.matthewdaly.co.uk/2012/03/24/yet-another-tutorial-for-building-a-blog-using-python-and-django-part-3/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=yet-another-tutorial-for-building-a-blog-using-python-and-django-part-3</link>
		<comments>http://www.matthewdaly.co.uk/2012/03/24/yet-another-tutorial-for-building-a-blog-using-python-and-django-part-3/#comments</comments>
		<pubDate>Sat, 24 Mar 2012 18:23:25 +0000</pubDate>
		<dc:creator>Matthew</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[web development]]></category>

		<guid isPermaLink="false">http://www.matthewdaly.co.uk/?p=758</guid>
		<description><![CDATA[Welcome back! In this installment, we&#8217;ll make some changes to our URL structure for blog posts, we&#8217;ll add support for multiple authors and static pages, and we&#8217;ll add some more templates. First of all, our URL structure. The existing structure works fine, but it would be better if we included a representation of the date [...]]]></description>
			<content:encoded><![CDATA[<p>Welcome back! In this installment, we&#8217;ll make some changes to our URL structure for blog posts, we&#8217;ll add support for multiple authors and static pages, and we&#8217;ll add some more templates.</p>
<p>First of all, our URL structure. The existing structure works fine, but it would be better if we included a representation of the date of publication. If you&#8217;re familiar with WordPress, you&#8217;ll know it offers several different URL forms, one of which is the post name alone as we&#8217;re using here, and another of which is the year, month and name. We&#8217;ll use the latter of these URL schemes with our blogging engine.</p>
<p>This seems like a good opportunity to introduce the interactive Python shell that comes with Django. Make sure you have a few dummy posts set up, then in the main project directory (blog/), enter the following command:</p>
<p><code>python manage.py shell</code></p>
<p>This will start up an interactive Python shell which you can use to interact with your Post objects. Now, the first step is to import your Post model:</p>
<p><code>&gt;&gt;&gt; from blogengine.models import Post</code></p>
<p>We now have access to our Post objects &#8211; let&#8217;s take a look at them:</p>
<p><pre><code>&gt;&gt;&gt; Post.objects.all()
[&lt;Post: My first blog post&gt;, &lt;Post: My second blog post&gt;, &lt;Post: My third post&gt;, &lt;Post: My fourth post&gt;, &lt;Post: My fifth post&gt;, &lt;Post: My sixth post&gt;]</code></pre></p>
<p>You may have completely different post objects, or a different number of them, but that&#8217;s fine. Remember we set __unicode_(self) to return self.title? Here we see that each blog post is represented by its title. Now let&#8217;s get one of our Post objects:</p>
<p><pre><code>&gt;&gt;&gt; p = Post.objects.get(pk=1)
&gt;&gt;&gt; p
&lt;Post: My first blog post&gt;</code></pre><br />
In the first line above, we get the Post object with the primary key of 1, and store a reference to it as p. We then demonstrate that it is, indeed, one of our blog posts by outputting its title.</p>
<p>If you&#8217;re not familiar with relational database theory, a primary key is a value in a database table that refers uniquely to one entry in the table, so that if you refer to an entry by its primary key, you can be sure you&#8217;re getting the correct value. By default, Django models generate a field called id in addition to the ones you define, which is set as the primary key, and this is set to auto-increment, so for instance, every time you add an additional blog post, it gets the next number as its id. Here, we just want to get access to a single blog post object, so we just enter 1 as the primary key in order to get the earliest blog post.</p>
<p>Next, we get the publication date:<br />
<pre><code>&gt;&gt;&gt; p.pub_date
datetime.datetime(2012, 3, 19, 12, 11, 10)</code></pre></p>
<p>This returns a datetime.datetime object. If you look at the documentation for Python&#8217;s datetime module, you&#8217;ll notice that it has attributes called day, month and year. Here&#8217;s how we can use these to get the information we want:</p>
<p><pre><code>&gt;&gt;&gt; p.pub_date.month
3
&gt;&gt;&gt; p.pub_date.day
19
&gt;&gt;&gt; p.pub_date.month
3
&gt;&gt;&gt; p.pub_date.year
2012</code></pre></p>
<p>It&#8217;s that simple &#8211; we just refer to the attribute we want to retrieve. So, it should now be pretty easy to understand how we can get the date for each blog post.</p>
<p>Exit your Python shell with Ctrl-D and head back into the blogengine/ folder. Then open models.py in your text editor and add the following method to the bottom of your Post class:</p>
<p><pre><code>&nbsp;&nbsp;&nbsp;&nbsp;def get_absolute_url(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return &quot;/%s/%s/%s/&quot; % (self.pub_date.year, self.pub_date.month, self.slug)</code></pre></p>
<p>Now, you haven&#8217;t seen get_absolute_url before. Every time you create a model in Django, you should really create a get_absolute_url method for it. In essence, it defines a single, canonical URL for that object, whether it&#8217;s a blog post, a user, or what have you. By creating one method that defines the structure for the URL for this type of object and referring to it elsewhere, we only need to change it in one place if we want to make any changes to how we determine the URL for that type of object.</p>
<p>What we do here is we define the URL as being /year/month/slug/. If you want, you can quite easily make it include the day as well like this:</p>
<p><pre><code>&nbsp;&nbsp;&nbsp;&nbsp;def get_absolute_url(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return &quot;/%s/%s/%s/%s/&quot; % (self.pub_date.year, self.pub_date.month, self.pub_date.day, self.slug)</code></pre></p>
<p>With our model updated, let&#8217;s change our URLconf accordingly. Return to the main blog/ directory and open up urls.py, then amend the lines for the blog posts as follows:</p>
<p><pre><code>&nbsp;&nbsp;&nbsp;&nbsp;# Blog posts
&nbsp;&nbsp;&nbsp;&nbsp;url(r&#039;^\d{4}/\d{1,2}/(?P&lt;postSlug&gt;[-a-zA-Z0-9]+)/?$&#039;, &#039;blogengine.views.getPost&#039;),</code></pre></p>
<p>What we&#8217;ve changed here is that we&#8217;ve told urls.py to expect blog posts that look like 4 digits, then a forward slash, then one or two digits, then another forward slash, then a slug that can include hyphens, upper or lower case letters, and numbers.</p>
<p>With that done, we just need to update our template. Edit templates/posts.html to look like this:</p>
<p><pre><code>&lt;html&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;head&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;title&gt;My Django Blog&lt;/title&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/head&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;body&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% for post in posts %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;&lt;a href=&quot;{{ post.get_absolute_url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/h1&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h3&gt;{{ post.pub_date }}&lt;/h3&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ post.text }}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endfor %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;br /&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page.has_previous %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page.previous_page_number }}/&quot;&gt;Previous Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page.has_next %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page.next_page_number }}/&quot;&gt;Next Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/body&gt;
&lt;/html&gt;</code></pre><br />
Literally all we do is replace post.slug with post.get_absolute_url and remove the leading forward slash. If you then run python manage.py syncdb, restart the development server and go clicking around your posts, you should be able to see that our new URL system is now up and running.</p>
<p>With that done, the next step is to add support for multiple authors. Now, you might think that we&#8217;re going to have to create a new model for users, but that&#8217;s not so &#8211; Django ships with a number of useful models already, and we&#8217;re going to use one of them here.</p>
<p>Now, first of all, we need to amend our Post model to include the author&#8217;s details. Edit your blogengine/models.py to look like this:</p>
<p><pre><code>from django.db import models
from django.contrib.auth.models import User

# Create your models here.
class Post(models.Model):
&nbsp;&nbsp;&nbsp;&nbsp;title = models.CharField(max_length=200)
&nbsp;&nbsp;&nbsp;&nbsp;pub_date = models.DateTimeField()
&nbsp;&nbsp;&nbsp;&nbsp;text = models.TextField()
&nbsp;&nbsp;&nbsp;&nbsp;slug = models.SlugField(max_length=40, unique=True)
&nbsp;&nbsp;&nbsp;&nbsp;author = models.ForeignKey(User)

&nbsp;&nbsp;&nbsp;&nbsp;def __unicode__(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return self.title

&nbsp;&nbsp;&nbsp;&nbsp;def get_absolute_url(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return &quot;/%s/%s/%s/&quot; % (self.pub_date.year, self.pub_date.month, self.slug)</code></pre></p>
<p>There are two significant changes here. First, we import User from django.contrib.auth.models. User is a model provided by the auth model, and we&#8217;re going to use it here to represent the author of a given post. Then in the class definition of Post, we add a new field called author.</p>
<p>Note here that author is a foreign key field, and is passed a User object. Again for those unfamiliar with relational databases, a foreign key is a field in a database table that is also a primary key in another database table. Here we&#8217;re declaring that the author is one of the entries in the User table.</p>
<p>As well as this, we need to make some changes to the admin interface. By default, when we make a field in a model a foreign key, the admin interface will show a dropdown list of all of the instances of that object (so here, it would be a list of all the users on the system). But we don&#8217;t want that. We want the author to automatically be set as the current user, and for there to be no way to override this.</p>
<p>Open up admin.py and change it to look like this:</p>
<p><pre><code>import models
from django.contrib import admin
from django.contrib.auth.models import User

class PostAdmin(admin.ModelAdmin):
&nbsp;&nbsp;&nbsp;&nbsp;prepopulated_fields = {&quot;slug&quot;: (&quot;title&quot;,)}
&nbsp;&nbsp;&nbsp;&nbsp;exclude = (&#039;author&#039;,)

&nbsp;&nbsp;&nbsp;&nbsp;def save_model(self, request, obj, form, change):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj.author = request.user
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj.save()

admin.site.register(models.Post, PostAdmin)</code></pre></p>
<p>The changes made here are simple. We import the User model, and in the PostAdmin class definition we exclude the author field &#8211; this means that we don&#8217;t show this field at all.</p>
<p>Note the addition of the save_model method. In Django it&#8217;s easy to create a new object using your models:</p>
<p><pre><code>&gt;&gt;&gt; from blogengine.models import Post
&gt;&gt;&gt; p = Post()
&gt;&gt;&gt; p
&lt;Post: &gt;
&gt;&gt;&gt; p.title=&quot;My new blog post&quot;
&gt;&gt;&gt; p
&lt;Post: My new blog post&gt;</code></pre></p>
<p>However, the new object won&#8217;t actually be stored in the database properly until you call the save() method. Here, you would need to enter p.save() (Note this won&#8217;t actually work unless you enter all the fields manually). What we&#8217;re doing in admin.py is overriding the default save() method to set the author to the name of the user who wrote the post.</p>
<p>Now run python manage.py syncdb again. Note that as you&#8217;ve changed the Post model, your existing posts will be lacking the required author field, and so you may need to add these again manually &#8211; this will be a numeric ID mapping to a user id. If you only have one user set up, you should just be able to set this to 1 by using an UPDATE SQL query.</p>
<p>If you now make sure the development server is running and log into the administrative interface, the first page you see should have a section marked &#8220;Auth&#8221;, with two items underneath named Groups and Users.</p>
<p>Now, cast your mind back to when you activated the admin interface and synced the database. If you recall, at this time you were asked to create a superuser account in order to log into the admin interface. This was actually provided by the django.contrib.auth application, one of the applications that are shipped with Django and are active by default. This contains the User and Group models.</p>
<p>If you&#8217;re familiar with Linux or Unix, the Auth application will feel very familiar. The account you created at the start was a superuser account, much like the root account on a Unix system, with unlimited privileges. Other users can be created, and given permissions on an individual basis. You can also create groups and add users to those groups, and then set the privileges for those groups en masse. For instance, in a large collaborative blog with many authors, you may have one group for people who contribute articles who can create new posts, editors who can edit existing posts and so on. Similarly, if you had a working comments system, you could easily set up a moderators group who can delete comments, and add people to that group.</p>
<p>Let&#8217;s create another user account so we have more than one. From the main admin page, click on the link for Add next to Users. You&#8217;ll be taken to a screen that prompts you for a username and password for the new user. Fill these in (there are two password fields for confirmation purposes) as you wish &#8211; here I&#8217;m setting the new user as bob. On the next screen you can add some additional details for the new user account, such as first name, last name and email address &#8211; do this so you have some information to work with.</p>
<p>Lower down you&#8217;ll see a dialogue for entering the permissions. You can make the new user a superuser so that they have permission to do anything, you can say whether or not they are staff (they need to be staff to use the admin interface, so check that), and whether they are active (making it easy to deactivate a user account without the need to delete it). Below you&#8217;ll see another dialogue showing the available permissions and allowing you to allocate them to that user. Further down, you&#8217;ll see a dialogue for changing the start date and last login date for the user, and finally a dialogue for adding new groups and adding the user to existing groups.</p>
<p>Save the user details once you&#8217;re done, then go into your superuser account and add a first and last name so we have some data to work with for that as well. Note that just as with a root account on a Unix box, it&#8217;s not a great idea to use a superuser account for everyday work (you should create an account that has the minimum privileges you need and use that), but we&#8217;ll stick with it for now just for learning purposes &#8211; don&#8217;t forget if you should roll out a public facing Django-powered site in future, though!</p>
<p>With that done, we now have some data to work with to identify the author of a given post. Let&#8217;s fire up the interactive shell again with python manage.py shell:</p>
<p><pre><code>&gt;&gt;&gt; from blogengine.models import Post
&gt;&gt;&gt; Post.objects.all()
[&lt;Post: My first post&gt;]
&gt;&gt;&gt; p = Post.objects.get()
&gt;&gt;&gt; p
&lt;Post: My first post&gt;
&gt;&gt;&gt; p.author
&lt;User: root&gt;
&gt;&gt;&gt; p.author.first_name
u&#039;Matthew&#039;
&gt;&gt;&gt; p.author.last_name
u&#039;Daly&#039;</code></pre></p>
<p>Here, we can see that it&#8217;s easy to get the author&#8217;s details from the post. We define p as a reference to the single Post object,then we get the author, which in this case is called root. As I&#8217;ve defined a first and last name, we can get those too with p.author.first_name and p.author.last_name, which are strings containing the first and last name respectively. Note the &#8216;u&#8217; before the string &#8211; this just indicates that the string is Unicode.</p>
<p>So from here, it&#8217;s pretty easy to display the author&#8217;s name in each post. Go into templates/posts.html and add the following line where you want your author details to appear:</p>
<p><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;h3&gt;By {{ post.author.first_name }} {{ post.author.last_name }}&lt;/h3&gt;</code></p>
<p>Now, as long as you&#8217;ve added a first name and last name to that author&#8217;s details, if you visit http://127.0.0.1:8000, you should see the appropriate details.</p>
<p>Our next step is to add the facility to create flat pages, somewhat like the Pages functionality in WordPress. Again, Django comes with an application that will handle this, called flatpages, but it&#8217;s not enabled by default. Go into settings.py and at the bottom of INSTALLED_APPS, add the following:</p>
<p><code>&nbsp;&nbsp;&nbsp;&nbsp;&#039;django.contrib.flatpages&#039;,</code></p>
<p>Then run python manage.py syncdb again to add the appropriate tables to your database. Now, we need to add a flat page. Go back to the main page of the admin interface, and you should see that you now have the facility to add flat pages. Click on the Add link for flat pages, and give your page a URL, a title, and some text (here I&#8217;m giving it a URL of /about/ and a title of About), add it to a site at the bottom (thsi will say example.com, but don&#8217;t worry about that, it&#8217;s to do with the Sites application, which we&#8217;re not looking at right now) then save it. You should now have a FlatPage object available.</p>
<p>Let&#8217;s take a look at the tables created for flatpages using the sqlall command:</p>
<p><pre><code>$ python manage.py sqlall flatpages
BEGIN;
CREATE TABLE &quot;django_flatpage_sites&quot; (
&nbsp;&nbsp;&nbsp;&nbsp;&quot;id&quot; integer NOT NULL PRIMARY KEY,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;flatpage_id&quot; integer NOT NULL,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;site_id&quot; integer NOT NULL REFERENCES &quot;django_site&quot; (&quot;id&quot;),
&nbsp;&nbsp;&nbsp;&nbsp;UNIQUE (&quot;flatpage_id&quot;, &quot;site_id&quot;)
)
;
CREATE TABLE &quot;django_flatpage&quot; (
&nbsp;&nbsp;&nbsp;&nbsp;&quot;id&quot; integer NOT NULL PRIMARY KEY,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;url&quot; varchar(100) NOT NULL,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;title&quot; varchar(200) NOT NULL,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;content&quot; text NOT NULL,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;enable_comments&quot; bool NOT NULL,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;template_name&quot; varchar(70) NOT NULL,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;registration_required&quot; bool NOT NULL
)
;
CREATE INDEX &quot;django_flatpage_a4b49ab&quot; ON &quot;django_flatpage&quot; (&quot;url&quot;);
COMMIT;</code></pre></p>
<p>So we can see that django_flatpage, which contains the details about the actual flat pages, has the fields id, url, title, content, enable_comments, template_name, and registration_required. Some of these options are under the advanced options in the flat page interface, so you may have missed them.</p>
<p>Now fire up python manage.py shell again:</p>
<p><pre><code>&gt;&gt;&gt; from django.contrib.flatpages.models import FlatPage
&gt;&gt;&gt; f = FlatPage.objects.get()
&gt;&gt;&gt; f
&lt;FlatPage: /about/ -- About&gt;</code></pre></p>
<p>Here we have one FlatPage object only (note that get() should only be used if you will only get one result back), which is represented by a string that includes the URL and title.</p>
<p><pre><code>&gt;&gt;&gt; f.content
u&#039;This is my about page.&#039;</code></pre></p>
<p>We can easily access any of the fields in the flat page. Now, we need to define some URLs for our flat pages. Exit the Python shell and open urls.py, then insert the following rule underneath the one for blog posts:</p>
<p><pre><code>&nbsp;&nbsp;&nbsp;&nbsp;# Flat pages
&nbsp;&nbsp;&nbsp;&nbsp;url(r&#039;&#039;, include(&#039;django.contrib.flatpages.urls&#039;)),</code></pre></p>
<p>Note that this <em>must</em> be the last rule in your urls.py, because it will match anything. Now, you can try and load /about/, or whatever page you&#8217;ve created, but you&#8217;ll get an error stating that the template does not exist, so we need to create that. Go into your template directory, and create a directory inside that called flatpages. Then create a new file in there called default.html, and add the following code to it:</p>
<p><pre><code>&lt;html&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;head&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;title&gt;My Django Blog&lt;/title&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/head&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;body&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;{{ flatpage.title }}&lt;/h1&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ flatpage.content }}
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/body&gt;
&lt;/html&gt;</code></pre></p>
<p>Now, make sure you have the development server running and try to load http://127.0.0.1:8000/about/, or whatever your flat page URL is, and you should see your flat page&#8217;s title and content.</p>
<p>One final task for this lesson &#8211; we&#8217;re going to refactor our templates a little so that as little code as possible is duplicated and if we want to change anything we need to only do so in one place. Go to your template directory and edit posts.html to look like this:</p>
<p><pre><code>{% include &#039;header.html&#039; %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% for post in posts %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;&lt;a href=&quot;{{ post.get_absolute_url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/h1&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h3&gt;{{ post.pub_date }}&lt;/h3&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ post.text }}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h3&gt;By {{ post.author.first_name }} {{ post.author.last_name }}&lt;/h3&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endfor %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;br /&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page.has_previous %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page.previous_page_number }}/&quot;&gt;Previous Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page.has_next %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page.next_page_number }}/&quot;&gt;Next Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
{% include &#039;footer.html&#039; %}</code></pre></p>
<p>Here we&#8217;re taking the header and footer of the page out and replacing them with code that includes another file there instead. Next, we need to create those files in the same directory. Here&#8217;s header.html:</p>
<p><pre><code>&lt;html&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;head&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;title&gt;My Django Blog&lt;/title&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/head&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;body&gt;</code></pre></p>
<p>And here&#8217;s footer.html:</p>
<p><pre><code>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/body&gt;
&lt;/html&gt;</code></pre></p>
<p>None of this is terribly complex &#8211; we&#8217;re just moving the code into another file so that other templates can use the same files. Now save a copy of posts.html as single.html &#8211; we&#8217;re going to create a template for a single blog post. Edit the original posts.html to look like this:</p>
<p><pre><code>{% include &#039;header.html&#039; %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% for post in posts %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;&lt;a href=&quot;{{ post.get_absolute_url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/h1&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ post.text }}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endfor %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;br /&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page.has_previous %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page.previous_page_number }}/&quot;&gt;Previous Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page.has_next %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page.next_page_number }}/&quot;&gt;Next Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
{% include &#039;footer.html&#039; %}</code></pre></p>
<p>We&#8217;re just removing the date and author details from the template that shows multiple posts. Our existing single.html file can remain as it is for now, since that still has all the additional information we want to include in an individual post.</p>
<p>While we&#8217;re here, let&#8217;s update our flat pages to use the same header and footer. Go into the flatpages directory and change default.html to look like this:</p>
<p><pre><code>{% include &#039;header.html&#039; %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;{{ flatpage.title }}&lt;/h1&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ flatpage.content }}
{% include &#039;footer.html&#039; %}</code></pre></p>
<p>Not that the path to the template files is not relative to flatpages/default.html, but relative to the root of the template directory.</p>
<p>The last thing to do is to amend the view for our blog to use the correct templates. Go into bloginfo/views.py and change the getPost (NOT getPosts) function to pass the single.html template to render_to_response, instead of the posts.html template:</p>
<p><pre><code>&nbsp;&nbsp;&nbsp;&nbsp;# Display specified post
&nbsp;&nbsp;&nbsp;&nbsp;return render_to_response(&#039;single.html&#039;, { &#039;posts&#039;:post})</code></pre></p>
<p>You should now notice that the single posts and multiple posts are using different templates.</p>
<p>Hope you&#8217;ve enjoyed this lesson, and I&#8217;ll do another one as soon as I can. The code is <a href="https://github.com/matthewbdaly/Django-Tutorial-Blog">available on GitHub</a> if you prefer to get it that way.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.matthewdaly.co.uk/2012/03/24/yet-another-tutorial-for-building-a-blog-using-python-and-django-part-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Yet another tutorial for building a blog using Python and Django – Part 2</title>
		<link>http://www.matthewdaly.co.uk/2012/03/19/yet-another-tutorial-for-building-a-blog-using-python-and-django-part-2/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=yet-another-tutorial-for-building-a-blog-using-python-and-django-part-2</link>
		<comments>http://www.matthewdaly.co.uk/2012/03/19/yet-another-tutorial-for-building-a-blog-using-python-and-django-part-2/#comments</comments>
		<pubDate>Mon, 19 Mar 2012 15:18:57 +0000</pubDate>
		<dc:creator>Matthew</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[web development]]></category>

		<guid isPermaLink="false">http://www.matthewdaly.co.uk/?p=729</guid>
		<description><![CDATA[In the first part of this tutorial, we got the core elements of our blogging application working &#8211; we set up our model for posts, and a view, template and URL configuration to view the index. Next we&#8217;ll start extending this very basic functionality &#8211; we&#8217;ll add a view for individual posts as well, and [...]]]></description>
			<content:encoded><![CDATA[<p>In the first part of this tutorial, we got the core elements of our blogging application working &#8211; we set up our model for posts, and a view, template and URL configuration to view the index. Next we&#8217;ll start extending this very basic functionality &#8211; we&#8217;ll add a view for individual posts as well, and we&#8217;ll allow for each post to have a separate URL.</p>
<p>First, we need to set up some pagination for the home page. At this point, it&#8217;s worth taking the time to look at how we want our URL to look. Here, we&#8217;ll work on the basis that by default, the home page will show the first five blog posts, and if someone wants to see later posts, they need to append a number to the end. Here&#8217;s the URL for the second page assuming it&#8217;s at example.com:<br />
<code>http://www.example.com/2/</code></p>
<p>So, we need two separate rules for the URLs. We need one for a URL with no number at the end, and one for a URL with a number at the end, and an optional forward slash. Open up urls.py and edit it so the Home page section looks like this:</p>
<p><pre><code> # Home page
url(r&#039;^$&#039;, &#039;blogengine.views.getPosts&#039;),
url(r&#039;^(?P&lt;selected_page&gt;\d+)/?$&#039;, &#039;blogengine.views.getPosts&#039;),</code></pre></p>
<p>Note that I&#8217;ve edited the first rule to include ^$ as the regular expression. ^ denotes the start of a regex, and $ denotes the end, so this represents a URL with nothing added after the domain name, such as http://www.example.com. We&#8217;ve also changed getRecentPosts to getPosts, as that&#8217;s now a more descriptive name.</p>
<p>The second line will match if there is a digit (denoted by the \d+ section) and will pass that digit through to the getPosts function as selected_page. With that done, we now need to make the necessary changes in the view, so move into the blogengine directory and amend views.py to look like this:</p>
<p><pre><code># Create your views here.
from django.shortcuts import render_to_response
from django.core.paginator import Paginator
from blogengine.models import Post

def getPosts(request, selected_page=1):
&nbsp;&nbsp;&nbsp;&nbsp;# Get all blog posts
&nbsp;&nbsp;&nbsp;&nbsp;posts = Post.objects.all().order_by(&#039;-pub_date&#039;)

&nbsp;&nbsp;&nbsp;&nbsp;# Add pagination
&nbsp;&nbsp;&nbsp;&nbsp;pages = Paginator(posts, 5)
&nbsp;&nbsp;&nbsp;&nbsp;returned_page = pages.page(selected_page)

&nbsp;&nbsp;&nbsp;&nbsp;# Display all the posts
&nbsp;&nbsp;&nbsp;&nbsp;return render_to_response(&#039;posts.html&#039;, {&#039;posts&#039;:returned_page.object_list})</code></pre></p>
<p>Again, note the change in function name from getRecentPosts to getPosts. Now, let&#8217;s work through the rest of the code. You&#8217;ll notice the following line near the top:<br />
<code>from django.core.paginator import Paginator</code><br />
This imports the Paginator class, which is very useful for creating pagination. Then, you&#8217;ll notice the following line:<br />
<code>def getPosts(request, selected_page=1):</code><br />
If you know much about Python, you&#8217;ll know that you can specify a default value for a parameter passed to a function or method. Here, what we&#8217;re doing is setting the default value of selected_page to 1, so if someone visits http://www.example.com, for which the URLconf doesn&#8217;t specify a number, this defaults to 1. If they visit http://www.example.com/2 instead, the default value for selected_page will be overriden to 2.</p>
<p>Then you&#8217;ll note that we&#8217;ve refactored the lines that fetched the posts and sorted them into one line, and called that posts. After that we define pages as a Paginator object, and passed it the values of posts and 5. The first parameter is what we want to divide between pages, and the second is how many instances of this we should allow on an individual page. Here we&#8217;re passing through all of the posts, and allowing 5 posts per page. We then define returned_page as the page from pages that matches the number submitted in the selected_page variable. Finally we pass a list of all the objects that make up returned_page through to the template as posts.</p>
<p>So, we now have basic pagination in place. Next, we&#8217;ll add the capability to display individual posts.</p>
<p>Now, we could just be lazy and have each post referred to by the numerical ID that&#8217;s automatically added by Django to the database, but why would we want to do that? We want a nice, human and search engine friendly URL that gives some idea what the blog post is about. Django is structured in such a way that nice, friendly URLs without cruft are very easy to create, and it actually has a special type of field in the models called a slug field that&#8217;s ideal for creating URLs.</p>
<p>So first of all, go into blogengine/models.py and edit it to look like this:</p>
<p><pre><code>from django.db import models

# Create your models here.
class Post(models.Model):
&nbsp;&nbsp;&nbsp;&nbsp;title = models.CharField(max_length=200)
&nbsp;&nbsp;&nbsp;&nbsp;pub_date = models.DateTimeField()
&nbsp;&nbsp;&nbsp;&nbsp;text = models.TextField()
&nbsp;&nbsp;&nbsp;&nbsp;slug = models.SlugField(max_length=40, unique=True)

&nbsp;&nbsp;&nbsp;&nbsp;def __unicode__(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return self.title</code></pre><br />
The only change is the addition of the slug field. Like any other field, you&#8217;ll be able to edit the slug field using the admin interface. But, why should you have to? Existing blogging solutions like WordPress will suggest a URL for a blog post, so that&#8217;s what we want to do as well. Open blogengine/admin.py and edit it to look like this:<br />
<pre><code>import models
from django.contrib import admin

class PostAdmin(admin.ModelAdmin):
&nbsp;&nbsp;&nbsp;&nbsp;prepopulated_fields = {&quot;slug&quot;: (&quot;title&quot;,)}

admin.site.register(models.Post, PostAdmin)</code></pre><br />
If you know a little about object-oriented programming in Python, you should be able to grasp what&#8217;s going on here. We&#8217;re creating PostAdmin, which inherits from ModelAdmin, and using the title to prepopulate the slug field. We then register this as before, but using PostAdmin rather than the default ModelAdmin.</p>
<p>A fairly typical slug will be based on your title, but will strip out whitespace and other characters between the words and replace them with hyphens, and convert the result to lowercase, so a title like &#8220;My new bike&#8221; will become my-new-bike.</p>
<p>Also, note that in models.py, we pass the parameter unique=True for the slug. This indicates that the slug must be unique, so we can&#8217;t have the same URL applied to two different posts.</p>
<p>With our model and admin amended, it&#8217;s now time to create a view to deal with displaying an individual post. Add the following function to blogengine/views.py:<br />
<pre><code>def getPost(request, postSlug):
&nbsp;&nbsp;&nbsp;&nbsp;# Get specified post
&nbsp;&nbsp;&nbsp;&nbsp;post = Post.objects.filter(slug=postSlug)

&nbsp;&nbsp;&nbsp;&nbsp;# Display specified post
&nbsp;&nbsp;&nbsp;&nbsp;return render_to_response(&#039;posts.html&#039;, { &#039;posts&#039;:post})</code></pre><br />
This function receives the request object and a slug for the post. It then gets the specific post with that slug, and returns it. For now we&#8217;ll just use the existing posts.html template, but we&#8217;ll want to add a new template for single posts at some point.</p>
<p>With that done, the next step is to add a URLconf to handle blog posts. Open urls.py and add the following code after the lines for the home page:<br />
<pre><code> # Blog posts
url(r&#039;^(?P&lt;postSlug&gt;[-a-zA-Z0-9]+)/?$&#039;, &#039;blogengine.views.getPost&#039;),</code></pre></p>
<p>So, now we have a dedicated URL for each post. But how do we get there? We need to create a link from the home page to each individual blog post. Open up your posts.html template and edit it to look like this:</p>
<p><pre><code>&lt;html&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;head&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;title&gt;My Django Blog&lt;/title&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/head&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;body&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% for post in posts %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;&lt;a href=&quot;/{{ post.slug }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/h1&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h3&gt;{{ post.pub_date }}&lt;/h3&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ post.text }}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endfor %}
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/body&gt;
&lt;/html&gt;</code></pre><br />
Now, if you run python manage.py syncdb, the changes to your database schema will be made automatically. However, if you already have some test posts in the database, these won&#8217;t have a slug and that could cause problems. So you can either add slugs to the existing posts manually using an UPDATE SQL command, or if you&#8217;re using something like PHPMyAdmin you can use that to add slugs for these posts. Or if they&#8217;re just test posts and you don&#8217;t care about them in the slightest, just delete your database and start again from scratch.</p>
<p>With that done, if you then run python manage.py runserver, and then visit http://127.0.0.1:8000, you should see your home page. If you have at least one blog post set up, you should see those posts on the home page, and the title should be a hyperlink to that post. If you have more than 5 posts, you should be able to go to http://127.0.0.1:8000/2 and see the next 5 posts.</p>
<p>But wait! What if you don&#8217;t have more posts? You want some code in place to handle what happens if you try to go to http://127.0.0.1:8000/2 and it isn&#8217;t there. You also want to dynamically generate links for older and newer posts so that users can click back as far as they need to.</p>
<p>First of all, let&#8217;s put something in place to catch nonexistent pages. Open blogengine/views.py and edit the getPosts function to look like this:</p>
<p><pre><code># Create your views here.
from django.shortcuts import render_to_response
from django.core.paginator import Paginator, EmptyPage
from blogengine.models import Post

def getPosts(request, selected_page=1):
&nbsp;&nbsp;&nbsp;&nbsp;# Get all blog posts
&nbsp;&nbsp;&nbsp;&nbsp;posts = Post.objects.all().order_by(&#039;-pub_date&#039;)

&nbsp;&nbsp;&nbsp;&nbsp;# Add pagination
&nbsp;&nbsp;&nbsp;&nbsp;pages = Paginator(posts, 5)

&nbsp;&nbsp;&nbsp;&nbsp;# Get the specified page
&nbsp;&nbsp;&nbsp;&nbsp;try:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;returned_page = pages.page(selected_page)
&nbsp;&nbsp;&nbsp;&nbsp;except EmptyPage:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;returned_page = pages.page(pages.num_pages)

&nbsp;&nbsp;&nbsp;&nbsp;# Display all the posts
&nbsp;&nbsp;&nbsp;&nbsp;return render_to_response(&#039;posts.html&#039;, { &#039;posts&#039;:returned_page.object_list})

def getPost(request, postSlug):
&nbsp;&nbsp;&nbsp;&nbsp;# Get specified post
&nbsp;&nbsp;&nbsp;&nbsp;post = Post.objects.filter(slug=postSlug)

&nbsp;&nbsp;&nbsp;&nbsp;# Display specified post
&nbsp;&nbsp;&nbsp;&nbsp;return render_to_response(&#039;posts.html&#039;, { &#039;posts&#039;:post})</code></pre></p>
<p>The only differences here are that EmptyPage is imported, and we add error checking to returned_page so that if it throws an EmptyPage exception (meaning that the given page doesn&#8217;t exist), then it defaults to returning the highest numbered page. The value of pages.num_pages is the number of pages in total, so you use this to get the last numbered page. If you prefer, you can change it to default to the first page by replacing pages.num_pages with 1.</p>
<p>With this done, the next step is to create links for the next and previous pages. Fortunately Django makes this really easy. First, you have to pass through the returned_page object in views.py, like this:</p>
<p><pre><code>&nbsp;&nbsp;&nbsp;&nbsp;# Display all the posts
&nbsp;&nbsp;&nbsp;&nbsp;return render_to_response(&#039;posts.html&#039;, { &#039;posts&#039;:returned_page.object_list, &#039;page&#039;:returned_page})</code></pre></p>
<p>Here in addition to the existing posts object, we now pass through returned_page as page. Now, amend your posts.html template as follows:</p>
<p><pre><code>&lt;html&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;head&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;title&gt;My Django Blog&lt;/title&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/head&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;body&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% for post in posts %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;&lt;a href=&quot;/{{ post.slug }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/h1&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h3&gt;{{ post.pub_date }}&lt;/h3&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ post.text }}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endfor %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;br /&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page.has_previous %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page.previous_page_number }}/&quot;&gt;Previous Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% if page.has_next %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;a href=&quot;/{{ page.next_page_number }}/&quot;&gt;Next Page&lt;/a&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endif %}
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/body&gt;
&lt;/html&gt;
</code></pre></p>
<p>Here, if the given page has a previous page, we display a link to it, and if it has a next page, we display a link to that too. page.has_previous and page.has_next return True or False, and page.previous_page_number and page.next_page_number display a number for the appropriate page, so it&#8217;s easy to use them to link to the appropriate page.</p>
<p>And that will do for now! We&#8217;ve gotten quite a lot done this time, and we actually have something that, although it&#8217;s still missing many of the more sophisticated features of blogging platforms such as WordPress, is fundamentally usable as a blog as long as you either don&#8217;t want comment functionality or are prepared to use a third-party system such as Disqus. Feel free to congratulate yourself with a beverage of your choice, and we&#8217;ll carry on later.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.matthewdaly.co.uk/2012/03/19/yet-another-tutorial-for-building-a-blog-using-python-and-django-part-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>New theme</title>
		<link>http://www.matthewdaly.co.uk/2012/02/28/new-theme/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=new-theme</link>
		<comments>http://www.matthewdaly.co.uk/2012/02/28/new-theme/#comments</comments>
		<pubDate>Tue, 28 Feb 2012 18:25:30 +0000</pubDate>
		<dc:creator>Matthew</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[web development]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://www.matthewdaly.co.uk/?p=727</guid>
		<description><![CDATA[For a long time now, I&#8217;ve kept meaning to write my own WordPress theme from scratch for this site, but just haven&#8217;t been able to find the time to do so. Now, I&#8217;ve finally found the time to do so, and here it is! Please let me know what you think, but be gentle (this [...]]]></description>
			<content:encoded><![CDATA[<p>For a long time now, I&#8217;ve kept meaning to write my own WordPress theme from scratch for this site, but just haven&#8217;t been able to find the time to do so. Now, I&#8217;ve finally found the time to do so, and here it is!</p>
<p>Please let me know what you think, but be gentle (this is my first solo WordPress theme, and also the only one I&#8217;ve built completely from scratch), and if you find any issues with it please let me know. Don&#8217;t expect it to look great in IE6 or IE7 however &#8211; I&#8217;ve given it a very cursory review in those browsers, and that&#8217;s all. It&#8217;s still perfectly readable, but it uses a fair amount of CSS3 so it&#8217;s inevitably not going to look as pretty in those browsers.</p>
<p>The code is <a href="https://github.com/matthewbdaly/Steel-Age">on GitHub</a> if you want to take a look.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.matthewdaly.co.uk/2012/02/28/new-theme/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Yet another tutorial for building a blog using Python and Django &#8211; Part 1</title>
		<link>http://www.matthewdaly.co.uk/2012/02/24/yet-another-tutorial-for-building-a-blog-using-python-and-django-part-1/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=yet-another-tutorial-for-building-a-blog-using-python-and-django-part-1</link>
		<comments>http://www.matthewdaly.co.uk/2012/02/24/yet-another-tutorial-for-building-a-blog-using-python-and-django-part-1/#comments</comments>
		<pubDate>Fri, 24 Feb 2012 16:17:10 +0000</pubDate>
		<dc:creator>Matthew</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[web development]]></category>

		<guid isPermaLink="false">http://www.matthewdaly.co.uk/?p=698</guid>
		<description><![CDATA[While I&#8217;m extremely fond of Perl for quick hacks and scripts, and have used PHP a fair amount for web development purposes (both on its own and with the CodeIgniter framework), there is one language I love above all others, and that&#8217;s Python. I&#8217;ve found that, when compared to PHP or Perl, at least for [...]]]></description>
			<content:encoded><![CDATA[<p>While I&#8217;m extremely fond of Perl for quick hacks and scripts, and have used PHP a fair amount for web development purposes (both on its own and with the CodeIgniter framework), there is one language I love above all others, and that&#8217;s Python. I&#8217;ve found that, when compared to PHP or Perl, at least for me, it&#8217;s a lot easier to &#8220;get into the zone&#8221; when programming in Python, the code I produce tends to be a lot more readable and easier to follow, and the interactive interpreter makes it really easy to figure out what&#8217;s going on in a way that just isn&#8217;t possible with PHP or Perl. Also, Python was always designed to be an object-oriented language, and IMHO has a better object model than either Perl or PHP.</p>
<p>While it would be fair to say that Python doesn&#8217;t have a single web development framework that monopolises developer&#8217;s attention the way Rails does for Ruby programmers, Django is undoubtedly the best-known Python framework. It&#8217;s solid, easy to use, and has the best documentation of any web development framework I&#8217;ve ever seen (don&#8217;t get me wrong, CodeIgniter in particular has very good documentation, but Django&#8217;s is exceptional).</p>
<p>In this tutorial, we&#8217;re going to build a very simple blogging engine using Django. In its initial stages, it will be an extremely simple web app &#8211; we won&#8217;t bother with comments, tags, categories or multiple users , or any of the other niceties of a fully-fledged blogging engine. Instead, we will build a very basic Tumblr-style blogging engine, capable of publishing blog posts and very little else. As time goes by, we can add further functionality to this and build it up into a more capable blogging solution.</p>
<p>So, let&#8217;s get started. Go to the <a href="https://www.djangoproject.com/" title="Django project website">Django project website</a> and download the latest release (as at right now this is 1.3.1). Follow the installation instructions given there, and you should be ready to go. Note that from here on, I&#8217;m assuming you&#8217;re using a Unix-like operating system such as a Linux distro or Mac OS X &#8211; if you&#8217;re using Windows, there&#8217;s a few extra steps you&#8217;ll have to take, such as installing Python, and some of the commands you use may be different.</p>
<p>Once Django is installed, find a suitable folder in which to store your new Django project (perhaps a Projects folder in your home directory might be a good place). Note that Django includes its own development server, so you don&#8217;t need to install a full LAMP stack like you would if you were developing in PHP. Then, from the folder you want to store your project in, run the following command:</p>
<p><code>django-admin.py startproject blog</code></p>
<p>This will create a brand-new directory containing all the files you need for your new Django project. If you now cd into this directory, you should see several files have been created, namely __init__.py manage.py, settings.py, and urls.py.</p>
<p>Let&#8217;s go through what these files do. First of all, there&#8217;s __init__.py &#8211; don&#8217;t worry about this, it&#8217;s a blank file and you don&#8217;t need to touch it.</p>
<p>Next, manage.py contains a number of extremely useful commands that you will find handy when using Django. You&#8217;re unlikely to need to edit it, but you&#8217;ll use it a lot.</p>
<p>Next, settings.py is the settings for the web app you&#8217;re building. It will specify details like what Django applications you&#8217;re using, what timezone you&#8217;re in, your database connection details and so on. You&#8217;ll need to edit this, so open it up in your favourite text editor.</p>
<p>Look for a line that reads &#8220;DATABASES&#8221;. Under here you&#8217;ll notice the following line:<br />
<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#039;ENGINE&#039;: &#039;django.db.backends.&#039;, # Add &#039;postgresql_psycopg2&#039;,&nbsp;&nbsp;&nbsp;&nbsp; &#039;postgresql&#039;, &#039;mysql&#039;, &#039;sqlite3&#039; or &#039;oracle&#039;.</code><br />
You can use pretty much any relational database you like with Django, and because it uses its own Object-Relational Mapping (ORM), it generates the SQL needed for you, taking into account any quirks in that particular database engine. It therefore doesn&#8217;t really matter what database you use, and it&#8217;s easy to swap them out. For development purposes, we&#8217;ll use SQLite as it ships with Python and requires less configuration, so change this line to read as follows:<br />
<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#039;ENGINE&#039;: &#039;django.db.backends.sqlite3&#039;, # Add &#039;postgresql_psycopg2&#039;,&nbsp;&nbsp;&nbsp;&nbsp; &#039;postgresql&#039;, &#039;mysql&#039;, &#039;sqlite3&#039; or &#039;oracle&#039;.</code><br />
Next you&#8217;ll see this line:<br />
<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#039;NAME&#039;: &#039;&#039;,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Or path to database file if using sqlite3.</code><br />
It really doesn&#8217;t matter what you call the file. I tend to call mine backend.db, as follows:<br />
<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#039;NAME&#039;: &#039;backend.db&#039;,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Or path to database file if using sqlite3.</code><br />
If you keep going down, you&#8217;ll notice TIME_ZONE and LANGUAGE_CODE. You may wish to change these from their default settings (I change mine to Europe/London for TIME_ZONE and en-gb for LANGUAGE_CODE).</p>
<p>Even further down, you&#8217;ll notice the INSTALLED_APPS section. Django distinguishes between a project and an application &#8211; while a project will normally be a single website, an application will be a set of functionality within that website. For instance, our blog will be a single application, but we could reuse that application on multiple projects. Django also includes a number of applications out of the box &#8211; for instance, the flatpages and admin applications can be used together if you wanted to use Django to build a simple CMS, without having to build a new application at all.</p>
<p>For now, we don&#8217;t need to add any new applications, so let&#8217;s save the changes we&#8217;ve made to settings.py and move on to urls.py. This handles directing any incoming HTTP requests to the appropriate place to deal with them. It uses simple regular expressions to evaluate the incoming requests, and maps them to specific view functions. Note that it already includes URLs for the admin functionality, but these are commented out by default.</p>
<p>Exit urls.py and head back to the main directory for your project. Now, we need to test that everything works OK. Run the following command:<br />
<code>python manage.py runserver</code><br />
Remember I said that the manage.py script had a lot of useful functions? This is one of them. Django has its own simple web server so you don&#8217;t have to faff around setting up Apache just for development purposes, and this launches it. If you go to http://127.0.0.1:8000, you should see a screen telling you that Django is running.</p>
<p>Now, you can stop the server for now using Ctrl-C, and we&#8217;ll start work on your new app. Run the following command to create your new app:<br />
<code>python manage.py startapp blogengine</code><br />
Again, note that you used manage.py to do this. There should now be a directory called blogengine in your project. Move into it, and you should find that it contains four files &#8211; __init__.py, models.py, tests.py and views.py. Again, __init__.py can be safely ignored, and tests.py can also be left alone, but models.py and views.py deserve closer examination.</p>
<p>If you haven&#8217;t used an MVC framework before, then you&#8217;ll need this explaining. MVC stands for Model-View-Controller, and it describes a method of logically separating out code for a web application to make it easier to work with. Models represent the data held by the application, views represent what end-users see of the application, and controllers represent the logic that ties the two together.</p>
<p>Django uses a slightly unusual interpretation of MVC. The models work exactly the same as they do in other frameworks, but the logic is handled by the view, and the presentation is handled by templates. Compared to more conventional MVC frameworks such as CodeIgniter, Django&#8217;s views are more like controllers, and its templates are more like views. Django is therefore often described as an MTV framework (Model-Template-View), instead of an MVC one.</p>
<p>So, to create our blog, we first need to create a model to describe the data. Edit models.py so it looks like the following:<br />
<pre><code>from django.db import models

# Create your models here.
class Post(models.Model):
&nbsp;&nbsp;&nbsp;&nbsp;title = models.CharField(max_length=200)
&nbsp;&nbsp;&nbsp;&nbsp;pub_date = models.DateTimeField()
&nbsp;&nbsp;&nbsp;&nbsp;text = models.TextField()

&nbsp;&nbsp;&nbsp;&nbsp;def __unicode__(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return self.title
</code></pre></p>
<p>In Django, you create your models as Python classes, which makes it very easy to grasp. Here, a blog post is an object, and it has a title, a publication date, and some text. Note that Post here inherits from models.Model, and has specific types of field that map to field types in the database table. For instance, models.CharField obviously maps to a VARCHAR field in the database, and TextField maps to a TEXT field. You can actually see the SQL that will generate the database table for this model by returning to the project&#8217;s main directory and running python manage.py sqlall blogengine:<br />
<pre><code>BEGIN;
CREATE TABLE &quot;blogengine_post&quot; (
&nbsp;&nbsp;&nbsp;&nbsp;&quot;id&quot; integer NOT NULL PRIMARY KEY,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;title&quot; varchar(200) NOT NULL,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;pub_date&quot; datetime NOT NULL,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;text&quot; text NOT NULL
)
;
COMMIT;</code></pre></p>
<p>Note the &#8220;id&#8221; field. You didn&#8217;t add this &#8211; by default, Django will create an id field in any new table, and will make this the primary key in that database table. You can, however, override this behaviour if you wish. Here it&#8217;s exactly what we want so we&#8217;ll stick with it for now.</p>
<p>Also note the __unicode__ method. This represents a string that describes that object. Here the title of a blog post seems the most logical way of describing it, so we return the object&#8217;s title.</p>
<p>Now that we&#8217;ve got our model set up, how do we get the information into it? For a blog post, all of the information will be submitted by the user, so we need to set up some kind of administrative interface. Fortunately, one of Django&#8217;s killer features is the admin interface that ships with it. This makes it really quick and easy to get certain kinds of sites up and running.</p>
<p>First of all, we need to activate the admin application. Head up to settings.py and uncomment the line that reads:<br />
<code>&#039;django.contrib.admin&#039;,</code><br />
You also need to include the app you&#8217;re working on at the bottom, like this:<br />
<code>&#039;blogengine&#039;,</code><br />
Save it, then head for urls.py and uncomment the following lines:<br />
<pre><code># from django.contrib import admin
# admin.autodiscover()</code></pre><br />
And:<br />
<code>&nbsp;&nbsp; # url(r&#039;^admin/&#039;, include(admin.site.urls)),</code><br />
Now, in order for the admin interface to be able to set up new blog posts, you need to also register it. In the blogengine directory containing your app, create a new file called admin.py, and fill it out with the following code:<br />
<pre><code>import models
from django.contrib import admin

admin.site.register(models.Post)</code></pre><br />
Once that&#8217;s done, return to the project directory and run this command to create the database tables you need:<br />
<code>python manage.py syncdb</code><br />
You&#8217;ll get asked for some information to set up your user account &#8211; remember it as you&#8217;ll need it to log into the admin interface. Once that&#8217;s done, run python manage.py runserver again, and return to http://127.0.0.1:8000 again. You should be confronted with a 404 page &#8211; that&#8217;s fine, that&#8217;s exactly what we should be seeing. You&#8217;ll note that the message states that Django tried the ^admin/ path without success &#8211; what this means is that this is the only URL pattern in urls.py at the moment, and the path you entered didn&#8217;t match this.</p>
<p>If you change the URL in the browser to http://127.0.0.1:8000/admin, you should get a login screen. Enter the username and password you set when you ran syncdb and click Log in. You should now see Django&#8217;s admin interface, with Posts available, and an Add and Change dialogue visible next to it. If you want to add a few blog posts, just to have some data to work with, go ahead. Note that for the Date and Time dialogues, Django automatically adds the Today and Now shortcuts.</p>
<p>So, our model is now sorted and we have some data in the web app. The next step is to write our views. You&#8217;ll notice that the blogengine app contains a file called views.py &#8211; open this up and enter the following code:<br />
<pre><code># Create your views here.
from django.shortcuts import render_to_response
from blogengine.models import Post

def getRecentPosts(request):
&nbsp;&nbsp;&nbsp;&nbsp;# Get all blog posts
&nbsp;&nbsp;&nbsp;&nbsp;posts = Post.objects.all()

&nbsp;&nbsp;&nbsp;&nbsp;# Sort posts into chronological order
&nbsp;&nbsp;&nbsp;&nbsp;sorted_posts = posts.order_by(&#039;-pub_date&#039;)
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;# Display all the posts
&nbsp;&nbsp;&nbsp;&nbsp;return render_to_response(&#039;posts.html&#039;, { &#039;posts&#039;:sorted_posts})</code></pre><br />
Let&#8217;s go through this code. The first line imports the render_to_response method, which is used to render a template. The second line imports the Post model.</p>
<p>Next, we define the getRecentPosts view. For simplicity&#8217;s sake, we aren&#8217;t going to bother about pagination for the moment, so we&#8217;ll just get all the posts. The view is written as a Python function, and we pass it the request object as the sole parameter.</p>
<p>Next, we get all of the Post objects, using Post.objects.all(), and assign it to a list called posts. As we want these to be in reverse chronological order, we then reorder them by pub_date (note the &#8211; sign at the beginning to denote reverse order) and assign the result to sorted_posts. Finally, we load the posts.html template and pass through sorted_posts as the value in a dictionary called posts.</p>
<p>With our view done, we now need to produce a template for it. Head back up to your main project directory and create a new folder called templates. Then, go into settings.py and find the line marked TEMPLATE_DIRS. Inside the brackets, underneath the comments, add the full, absolute path to the new templates folder, as in this example:<br />
<code>&nbsp;&nbsp;&quot;/Users/matthewdaly/Development/Python/Django/blog/templates&quot;</code><br />
You&#8217;ll have to change this to the full, absolute path on your machine. This will tell Django to look for the templates in that folder. Now, go into templates, and create a new file called posts.html. Enter the following text into it:<br />
<pre><code>&lt;html&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;head&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;title&gt;My Django Blog&lt;/title&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/head&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;body&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% for post in posts %}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;{{ post.title }}&lt;/h1&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h3&gt;{{ post.pub_date }}&lt;/h3&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ post.text }}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{% endfor %}
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/body&gt;
&lt;/html&gt;</code></pre><br />
Most of this is just plain old HTML, but you&#8217;ll notice that {% %} denotes tags that can include some logic (such as a for loop in this case), and {{ }} denotes a variable. Remember that in the view we passed through a dictionary containing all of the Post objects, and here we&#8217;re iterating through all of those post objects, outputting their title, publication date and text content.</p>
<p>With this done, we need to configure the routes to call the getRecentPosts view when someone visits the home page. Open urls.py again and add the following code underneath where you enabled the admin, but still inside the parentheses:<br />
<pre><code>&nbsp;&nbsp;&nbsp;&nbsp;# Home page
&nbsp;&nbsp;&nbsp;&nbsp;url(r&#039;&#039;, &#039;blogengine.views.getRecentPosts&#039;),</code></pre><br />
Now, this is a very simple regular expression. Here, this is our default page, so we leave the single quotes after the r empty. We then specify that this URL should be handled by the getRecentPosts function, inside views.py, in the blogengine application.</p>
<p>Save that, and start up the development server again with python manage.py runserver. Then, if you haven&#8217;t already added a few test posts, do so via the admin interface. Then open http://127.0.0.1:8000, and you should see your blog posts.</p>
<p>So, we now have the beginnings of a blogging application! We&#8217;ll leave it here for now, and will go on to add functionality like viewing individual posts and pagination later. We&#8217;ll also look into adding further functionality to our blog, such as supporting multiple authors, tagging posts, and adding flat pages.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.matthewdaly.co.uk/2012/02/24/yet-another-tutorial-for-building-a-blog-using-python-and-django-part-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>So you REALLY don&#8217;t know regular expressions?</title>
		<link>http://www.matthewdaly.co.uk/2012/01/29/so-you-really-dont-know-regular-expressions/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=so-you-really-dont-know-regular-expressions</link>
		<comments>http://www.matthewdaly.co.uk/2012/01/29/so-you-really-dont-know-regular-expressions/#comments</comments>
		<pubDate>Sun, 29 Jan 2012 19:52:53 +0000</pubDate>
		<dc:creator>Matthew</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[programming]]></category>

		<guid isPermaLink="false">http://www.matthewdaly.co.uk/?p=665</guid>
		<description><![CDATA[Ever since I started my new job, I&#8217;ve noticed a curious phenomenon. I work with two wonderfully gifted programmers who both know PHP much better than I do, and I learn something new from them all the time. However, neither one of them really knows or uses regular expressions. Now, as I learned Perl before [...]]]></description>
			<content:encoded><![CDATA[<p>Ever since I started my new job, I&#8217;ve noticed a curious phenomenon. I work with two wonderfully gifted programmers who both know PHP much better than I do, and I learn something new from them all the time. However, neither one of them really knows or uses regular expressions.</p>
<p>Now, as I learned Perl before I learned PHP, naturally I learned regular expressions quite early on in that process. In Perl, regular expressions are a huge part of the language &#8211; you simply cannot get away without learning them to some extent as they are used extensively in so many parts of the language.</p>
<p>Apparently I&#8217;m not the only one to notice this. Here&#8217;s a quote I found on Stack Exchange:</p>
<blockquote><p>In earlier phases of my career (ie. pre-PHP), I was a Perl guru, and one major aspect of Perl gurudom is mastery of regular expressions.</p>
<p>On my current team, I&#8217;m literally the only one of us who reaches for regex before other (usually nastier) tools. Seems like to the rest of the team they&#8217;re pure magic. They&#8217;ll wheel over to my desk and ask for a regex that takes me literally ten seconds to put together, and then be blown away when it works. I don&#8217;t know&#8211;I&#8217;ve worked with them so long, it&#8217;s just natural at this point.</p>
<p>In the absence of regex-fluency, you&#8217;re left with combinations of flow-control statements wrapping strstr and strpos statements, which gets ugly and hard to run in your head. I&#8217;d much rather craft one elegant regex than thirty lines of plodding string searching.</p></blockquote>
<p>While I would hesitate to call myself a Perl guru (at best I would call myself intermediate with Perl), I would say I know enough about regular expressions that I can generally get useful work done with them.</p>
<p>Take the following example in Perl (edited somewhat as it didn&#8217;t play nice with TinyMCE):</p>
<p><pre><code>$fruit = &quot;apple,banana,cherry&quot;;
print $fruit;
@fruit = split(/,/,$fruit);
foreach(@fruit){print $_.&quot;\n&quot;;}
apple,banana,cherry
apple
banana
cherry</code></pre></p>
<p>Now, this code should be fairly easy to understand, even if you don&#8217;t really know Perl. $fruit is a string containing &#8220;apple,banana,cherry&#8221;. The split() function takes two arguments, a regular expression defining the character(s) that are used to separate the parts of the string you want to put into an array, and the string you want to split. This returns the array @fruit, which consists of three strings, &#8220;apple&#8217;, &#8220;banana&#8221;, and &#8220;cherry&#8221;.</p>
<p>In PHP, you can do pretty much the same thing, using the explode() function:</p>
<p><pre><code>$fruit = &quot;apple,banana,cherry&quot;;
echo $fruit.&quot;\n&quot;;
$fruitArray = explode(&quot;,&quot;,$fruit);
foreach($fruitArray as $fruitArrayItem)
{
echo $fruitArrayItem.&quot;\n&quot;;
}
apple,banana,cherry
apple
banana
cherry</code></pre><br />
As you can see, they work in pretty much the same way here. Both return basically the same output, and the syntax for using the appropriate functions for splitting the strings is virtually identical.</p>
<p>However, it&#8217;s once things get a bit more difficult that it becomes obvious how much more powerful regular expressions are. Say you&#8217;re dealing with a string that&#8217;s similar to that above, but may use different characters to separate the elements. For instance, say you&#8217;ve obtained the data that you want to pass through into an array from a text file and it&#8217;s somewhat inconsistent &#8211; perhaps the information you want is separated by differing amounts and types of whitespace, or different characters. The explode() function simply won&#8217;t handle that (at least, not without a lot of pain). But with Perl&#8217;s split() function, that&#8217;s no problem. Here&#8217;s how you might deal with input that had different types and quantities of whitespace as a separator:<br />
<code>@fruit = split(/\s+/,$fruit);</code><br />
Yes, it&#8217;s that simple! The \s metacharacter matches any type of whitespace, and the + modifier means that it will match one or more times. Now you can very easily convert the contents of that string into an array.</p>
<p>Or say you want to convert an entire string of text, with all kinds of punctuation and whitespace, into an array, but only keep the actual words. This wouldn&#8217;t be practical with explode(), but with split() it&#8217;s easy:<br />
<code>@fruit = split(/\W+/,$fruit);</code><br />
The \W metacharacter matches any non-word character (ie anything other than a-z, A-Z or 0-9), and again the + modifier means that it will match one or more times.</p>
<p>And of course, regular expressions are useful for many more tasks than this that, while possible with most language&#8217;s existing string functions, can get very nasty quite quickly. Say you want to match a UK postcode to check that it&#8217;s valid (note that for the sake of simplicity, I&#8217;m going to ignore BFPO and GIR postcodes). These use a format of one or two letters, followed by one digit, then may have an additional digit or letter, then a space, then a digit, then two letters. This would be a nightmare to check using most language&#8217;s native string functions, but with a regex in Perl, it&#8217;s relatively simple:<br />
<pre><code>my $postcode = &quot;NR1 1NP&quot;;
if($postcode =~ m/^[a-zA-Z]{1,2}\d{1}(|[a-zA-Z0-9]{1})(|\s+)\d{1}\w{2}$/)
{
print &quot;It matched!\n&quot;;
}</code></pre><br />
And if you wanted to return the first part of the postcode if it matched as well, that&#8217;s simple too:<br />
<pre><code>my $postcode = &quot;NR1 1NP&quot;;
if($postcode =~ s/^([a-zA-Z]{1,2}\d{1}(|[a-zA-Z0-9]{1}))(|\s+)\d{1}\w{2}$/$1/)
{
print &quot;It matched! $postcode\n&quot;;
}</code></pre></p>
<p>Now, you may say &#8220;But that&#8217;s in Perl! I&#8217;m using PHP!&#8217;. Well, regular expressions are an extremely powerful part of PHP that are very useful, they&#8217;re just not as central to the language as they are in Perl. PHP actually has two distinct types of regular expressions &#8211; POSIX-extended regular expressions, and Perl-compatible regular expressions (or PCRE). However, POSIX-extended regular expressions were deprecated from PHP 5.3 onwards, so it&#8217;s not really worth taking the time to learn them when PCRE will do exactly the same thing and is going to be around for the future. Furthermore, most other programming languages also support Perl-compatible regular expressions, so they&#8217;re fairly portable between languages, and once you&#8217;ve learned them in one language, you can easily use them in another. In other words, if you learn how to work with regular expressions in Perl, you can very easily transfer that knowledge to most other programming languages that support regular expressions.</p>
<p>In the first example given above, we can replace explode() with preg_split, and the syntax is virtually identical to split() in Perl, with the only difference being the name of the function and that the pattern to match is wrapped in double quotes:</p>
<p><pre><code>$fruit = &quot;apple,banana,cherry&quot;;
echo $fruit.&quot;\n&quot;;
$fruitArray = preg_split(&quot;/,/&quot;,$fruit);
foreach($fruitArray as $fruitArrayItem)
{
echo $fruitArrayItem.&quot;\n&quot;;
}
apple,banana,cherry
apple
banana
cherry</code></pre></p>
<p>Along similar lines, if we want to check if a string matches a pattern, we can use preg_match(), and if we want to search and replace, we can use preg_replace(). PHP&#8217;s regular expression support is not appreciably poorer than Perl&#8217;s, even if it&#8217;s less central to the language as a whole.</p>
<blockquote><p>But regular expressions are slower than PHP&#8217;s string functions!</p></blockquote>
<p>Yes, that&#8217;s true. So it&#8217;s a mistake to use regular expressions for something that can be handled quickly and easily using string functions. For instance, if in the following string you wanted to replace the word &#8220;cow&#8221; with &#8220;sheep&#8221;:</p>
<blockquote><p>The cow jumped over the moon</p></blockquote>
<p>You could use something like this:<br />
<pre><code>$text = &quot;The cow jumped over the moon&quot;;
$text = preg_replace(&quot;/cow/&quot;,&quot;sheep&quot;,$text);</code></pre><br />
However, because here you are only looking to match literal characters, you don&#8217;t need to use a regular expression. Just use the following:<br />
<code>$text = str_replace(&quot;cow&quot;,&quot;sheep&quot;,$text);</code></p>
<p>But, if you have to do some more complex pattern matching, you have to start using strpos to get the location of specific characters and returning substrings between those characters, and it gets very messy, very quickly indeed. In those cases, while I haven&#8217;t done any kind of benchmarking on it, it stands to reason that quite quickly you&#8217;ll reach a point where a regex would be faster.</p>
<p>However, for a number of common tasks, such as validating email addresses and URLs, there&#8217;s another way and you don&#8217;t need to resort to regular expressions, or faffing about with loads of string functions. The filter_var() function can be used for validating or sanitising email addresses and URLs, among other things, so this is worth using instead of writing a regex. If you&#8217;re using a framework such as CodeIgniter, you may have access to its native functions for validating this kind of thing, so you should use those instead.</p>
<blockquote><p>But regular expressions are ugly and make for less readable code!</p></blockquote>
<p>Not really. They seem intimidating to the newcomer, and very few people can just glance at a regex and instantly know what it does. But with regexes, you can often do complex things in far fewer lines of code than would be needed to accomplish the same thing using just PHP&#8217;s string functions. If you can do something in a line or two using string functions, it&#8217;s probably best to do that. But after that, things go downhill very quickly.</p>
<p>Once you learn them, regular expressions really are not that hard, and you&#8217;ll probably find enough things to use them for that you&#8217;ll get plenty of practice at them. They&#8217;re certainly more readable to anyone with even a modicum of experience using them than line after line of flow-control statements.</p>
<blockquote><p>But you shouldn&#8217;t be using regular expressions for parsing HTML or XML!</p></blockquote>
<p>Quite true. Regular expressions are the wrong tool for that. You should probably use an existing library of some kind for that.</p>
<blockquote><p>Some people, when confronted with a problem, think &#8220;I know, I&#8217;ll use regular expressions.&#8221; Now they have two problems.</p></blockquote>
<p>Ah, yes, surely one of the most misused quotes on the web! Again, regular expressions are not the right tool for every job, and there&#8217;s a lot of tasks they get used for, and quite frankly, shouldn&#8217;t be. Most of us who know regular expressions have been known to use them for things we probably shouldn&#8217;t (I actually only just stumbled across filter_var, so I&#8217;ve done my share of validating email addresses using regexes, and I&#8217;m as guilty as anyone else of overusing them). But there&#8217;s still plenty of stuff you should use it for when what you need to do can&#8217;t be accomplished quickly and easily using string functions.</p>
<p>Regular expressions are not inherently evil. They&#8217;re a tool like any other. What is bad is using them for things where a simple alternative exists. However, they are still extremely useful, and there&#8217;s plenty of valid use cases for them.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.matthewdaly.co.uk/2012/01/29/so-you-really-dont-know-regular-expressions/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Github</title>
		<link>http://www.matthewdaly.co.uk/2012/01/13/github/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=github</link>
		<comments>http://www.matthewdaly.co.uk/2012/01/13/github/#comments</comments>
		<pubDate>Fri, 13 Jan 2012 19:25:20 +0000</pubDate>
		<dc:creator>Matthew</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[tools]]></category>
		<category><![CDATA[version control]]></category>

		<guid isPermaLink="false">http://www.matthewdaly.co.uk/?p=661</guid>
		<description><![CDATA[To date, Subversion is the single versioning system I have the most experience with. I use it at work, and I was already somewhat familiar with it beforehand. However, with all the buzz over Git over the last few years, it&#8217;s always been tempting to explore that as an alternative. I&#8217;ve had a Github account [...]]]></description>
			<content:encoded><![CDATA[<p>To date, Subversion is the single versioning system I have the most experience with. I use it at work, and I was already somewhat familiar with it beforehand. However, with all the buzz over Git over the last few years, it&#8217;s always been tempting to explore that as an alternative.</p>
<p>I&#8217;ve had a Github account for over a year, but had as yet not added anything to it. However, today that changed. I&#8217;ve had a rather haphazard approch towards my .vimrc and other Vim configuration files for a while, with the result that they tend to be less than consistent across different machines. I&#8217;ve seen that a fair number of people put their Vim configuration files under version control, and that seemed like an effective solution, so I&#8217;ve gotten my .vimrc and .vim into a respectable state and added them to <a href="https://github.com/matthewbdaly/My-vim-configuration" target="_blank">a new repository</a>. Now I should have no excuse for letting them get out of sync.</p>
<p>I have to say, Github is a truly wonderful service. The tutorials for getting started with Git are really good, and make it easy to get started. It&#8217;s probably one of the main reasons why Git is becoming more and more popular- there isn&#8217;t really anything comparable for Subversion.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.matthewdaly.co.uk/2012/01/13/github/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux in the workplace</title>
		<link>http://www.matthewdaly.co.uk/2011/10/24/linux-in-the-workplace/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=linux-in-the-workplace</link>
		<comments>http://www.matthewdaly.co.uk/2011/10/24/linux-in-the-workplace/#comments</comments>
		<pubDate>Mon, 24 Oct 2011 21:18:12 +0000</pubDate>
		<dc:creator>Matthew</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[web development]]></category>

		<guid isPermaLink="false">http://www.matthewdaly.co.uk/?p=655</guid>
		<description><![CDATA[At the start of September I left my customer services role and started a new position as a web developer. I won&#8217;t give the name of either my old or new employer, but I will say that the new role is with a much smaller company, and the part I work for now is an [...]]]></description>
			<content:encoded><![CDATA[<p>At the start of September I left my customer services role and started a new position as a web developer. I won&#8217;t give the name of either my old or new employer, but I will say that the new role is with a much smaller company, and the part I work for now is an e-commerce store that enjoys a significant degree of independence from the parent company. There are only two developers including myself, and we are solely responsible for the company&#8217;s IT infrastructure, and we don&#8217;t have the hassle of dealing with legacy applications or infrastructure. We therefore have considerable freedom in terms of what we choose to use to get our work done.</p>
<p>When I first started, I used Windows XP Professional since that was what my work laptop came with, but it soon became obvious that there wasn&#8217;t actually anything I specifically needed to be using Windows for. I mostly work on the company&#8217;s intranet, which doesn&#8217;t really need to be tested in Internet Explorer as we use Firefox internally. For email and calendar, we use Google Apps, which works fine with virtually any email client that supports IMAP, so I was using Thunderbird with the Lightning plugin. When coding I used Netbeans with the jVi plugin for most of my work, with occasional usage of Vim for writing shorter scripts. I used AppServ to provide local versions of Apache, MySQL and PHP, and I used PHPMyAdmin to interact with the database. For version control, I used Subversion. From time to time I need to remote into another machine using VNC, SSH or RDP, for which I used mRemote, but I was confident I could find an equivalent application. Also, we use Ubuntu on most of our servers, so it made a lot of sense from a compatibility point of view to also use it on my own desktop. From time to time, I also found myself writing bash or Perl scripts for systems administration purposes, and since it wasn&#8217;t really very practical to do that in Windows when it was going to be running in Ubuntu, I&#8217;d used an Ubuntu Server install in Virtualbox to write it, but it was obvious that running Ubuntu as my desktop OS would make more sense.</p>
<p>As Ubuntu 11.10 was due a little over a month after I first started, I decided to hold off making the switch until then so I could start with the most recent version and not have the hassle of upgrading an existing install. I had already downloaded the 64-bit version of Ubuntu 11.10 for my home machines and burned them to a CD, so I brought the CD into work and set up a dual boot so I could revert back to XP if anything went wrong, and also so I could easily copy across any files I needed from the Windows partition.</p>
<p>It took a fair while to get everything I wanted installed, but a lot less time than it would have taken if I&#8217;d set up Windows XP from scratch. The hardware all worked fine out of the box, and most of the software I needed was in the repositories. The only thing that I really needed that wasn&#8217;t there was Netbeans (which has apparently now been removed from the repositories), but the version in the Ubuntu repositories has never been very up-to-date anyway. Instead I installed the version of Netbeans available on the website, and that has worked fine for me. While there wasn&#8217;t a version of mRemote available, I did discover Remmina, which has proven to be an excellent client for SSH, RDP and VNC, to the point that I&#8217;ve now stopped using the terminal to connect via SSH in favour of using Remmina instead. Thunderbird does just as good a job with my email and calendar as it does on Windows, and I also have Mutt available. Naturally, it couldn&#8217;t be simpler to install a full LAMP stack and PHPMyAdmin either. In fact, the only application that I use much that I couldn&#8217;t get a decent version of was MySQL Workbench, and that was only because Oracle haven&#8217;t yet released a version for Ubuntu 11.10 (tried the version for 11.04, but it doesn&#8217;t seem to work), but I can live without that.</p>
<p>What&#8217;s interesting is that despite all the scaremongering I&#8217;ve heard over the years about how Linux isn&#8217;t ready for the workplace, I&#8217;ve as yet had no problems whatsoever. For everything I used in Windows, it was either available on Ubuntu, or there was a viable equivalent, or I could get by fine without it. Granted, the nature of my work means I have little need for the small amount of functionality that Microsoft Office has and LibreOffice doesn&#8217;t, and I don&#8217;t need to use the kind of ghastly legacy apps written in Visual Basic that most large enterprises commonly use, but I haven&#8217;t noticed any significant barriers to my productivity.</p>
<p>In fact, if anything I&#8217;m considerably more productive. I know people like to rag on Unity, and I wasn&#8217;t happy with it in the netbook edition of Ubuntu 10.10 myself, but in 11.10 it&#8217;s really starting to show its promise, and I haven&#8217;t had any problems with it. The fact that I know Ubuntu a lot more thoroughly than I do Windows, purely from my own experience at home, means that I can get things done a lot quicker, but also the whole package management system means I&#8217;m largely free from the annoyances of opening an application in the morning to be confronted with an update dialogue, quite apart from the fact that very few updates require a restart. I&#8217;d go so far as to say that I&#8217;ve been more productive using Ubuntu at work than I would have been with either Windows 7 or OS X (and over the last few years I&#8217;ve used Windows Vista, Windows 7 and OS X fairly extensively).</p>
<p>I really don&#8217;t want this to turn into Yet Another Year of the Linux Desktop blog post, because that&#8217;s rather a tired old cliche, but I have absolutely no problems whatsoever getting my work done on Ubuntu. I&#8217;ll concede that as a developer I have significant freedom that isn&#8217;t often afforded to other people, and running some flavour of Unix makes a lot of sense if you&#8217;re a developer working with one of the open-source server-side languages such as PHP or Python (if I were a .NET developer, it would make rather less sense). I&#8217;m also lucky to be in a position where I don&#8217;t have to worry about legacy apps or IE compatibility too much. Nonetheless, it&#8217;s still remarkable how smoothly my migration across to Ubuntu on my work desktop has gone, and the extent to which I find it&#8217;s improved my workflow.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.matthewdaly.co.uk/2011/10/24/linux-in-the-workplace/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Hacked!</title>
		<link>http://www.matthewdaly.co.uk/2011/05/29/hacked/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=hacked</link>
		<comments>http://www.matthewdaly.co.uk/2011/05/29/hacked/#comments</comments>
		<pubDate>Sun, 29 May 2011 13:53:13 +0000</pubDate>
		<dc:creator>Matthew</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[server]]></category>

		<guid isPermaLink="false">http://www.matthewdaly.co.uk/?p=635</guid>
		<description><![CDATA[Had a rather unfortunate incident last month &#8211; someone hacked into my Pogoplug mail server, and managed to get their mitts on my .fetchmailrc, which had all the login details for several email accounts. They promptly began sending spam out using my Gmail account. Naturally this meant I spent ages running round like a headless [...]]]></description>
			<content:encoded><![CDATA[<p>Had a rather unfortunate incident last month &#8211; someone hacked into my Pogoplug mail server, and managed to get their mitts on my .fetchmailrc, which had all the login details for several email accounts. They promptly began sending spam out using my Gmail account.</p>
<p>Naturally this meant I spent ages running round like a headless chicken trying to lock them out &#8211; when I first noticed that they&#8217;d been sending emails directly from my mail server, I logged into it via SSH and shut it down, then changed the passwords on all my email accounts.</p>
<p>Thinking logically, there were four services that I had forwarded ports to the server for &#8211; SSH, Apache, Postfix and Dovecot. Now, I was running SSH on a non-standard port, had disabled root access, and didn&#8217;t allow password authentication (SSH keys only). Also, I had enabled DenyHosts, so I&#8217;m fairly confident SSH was not the point of entry.</p>
<p>So that leaves either Apache, Postfix or Dovecot. I had noticed in the error logs a lot of characters prefixed with backslashes, and wondered if someone was trying some kind of shellcode injection, and to be safe I had added new iptables rules to blacklist the IP addresses responsible. I had done what I could to secure Apache, but I can&#8217;t rule it out as the application that was compromised. I went through the server logs, but without finding anything &#8211; I&#8217;m guessing whoever was responsible deleted the appropriate entries in the log files. I couldn&#8217;t be sure that the server could still be trusted, so I did a fresh install, and have disabled port forwarding on my router.</p>
<p>This has certainly made me much more cautious and suspicious about security, which I guess can&#8217;t be a bad thing. Even beforehand, I found it pretty scary to see the sheer number of script kiddies who will try to hack into any server on the Internet.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.matthewdaly.co.uk/2011/05/29/hacked/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

