What's new in Django community blogs?

Continuously rebuild your project

[Archived Version] □ Published at Writing on django | David Winterbottom

New developers joining a project will often find that the project won't build cleanly on their machine, and hours of time will be sunk into setting up the project so work can start. This is sad and expensive for all concerned.

This is a particular menace in agencies (or anywhere with lots of small projects) where a large team of developers need to jump between projects. Tools like Vagrant and Docker can help but aren't the panacea they first seem to be [*].

Counter this by using continuous integration to build your project from scratch. Then any changes that break the build process (such as database schema changes not applying correctly) will be spotted early. New team members will ...


Contributing Back to Symposion

Jul 21 2014 [Archived Version] □ Published at Caktus Blog

Recently Caktus collaborated with the organizers of PyOhio, a free regional Python conference, to launch the PyOhio 2014 conference website. The conference starts this weekend, July 26 - 27. As in prior years, the conference web site utilizes Eldarion’s Symposion, an opensource conference management system. Symposion powers a number of annual conference sites including PyCon...


My First Docker

Jul 16 2014 [Archived Version] □ Published at TravisSwicegood.com

I’ve been told I should check out Docker for over a year. Chris Chang and Noah Seger at the Tribune were both big proponents. They got excited enough I always felt like I was missing something since I didn’t get it, but I haven’t had the time to really dig into it until the last few weeks.

After my initial glance at it, I couldn’t see how it was better/different than using Vagrant and a virtual machine. Over the last few weeks I’ve started dipping my toes in the Docker waters and now I’m starting to understand what the big deal is about.

Docker versus VM

I’ve been a longtime fan of Vagrant as a way to quickly orchestrate virtual machines. That fits my brain. It’s a server that’s run like any other box, just not on existing hardware. Docker goes a different route by being more about applications, regardless of the underlying OS. For example, let’s talk about my npm-cache.

Using this blog post as a base, I wanted to create an easily deployable nginx instance that would serve as a cache for npmjs.org. The normal route for this is to get nginx installed on a server and set it up with the right configuration. You could also add it to an existing nginx server if you have one running.

Docker views something like this npm-cache less as the pieces of that infrastructure (nginx and the server its on) and more as an application unto itself with an endpoint that you need to hit. Its a subtle shift, but important in a service-oriented world.

Getting Started

Docker has been described as Git for deployment, and there’s a reason. Each step of a deployment is a commit unto itself that can be shared and re-orchestrated into something bigger. For example, to start my npm-cache, I started by using the official nginx container.

The nginx container can be configured by extending it and providing your own configuration. I used in the configuration from yammer, created a few empty directories that are needed for the cache to work, then I was almost ready to go. The configuration needed to know how to handle rewriting the responses to point to the caching server.

Parameterizing a Container

This is where things got a little tricky for me as a Docker newbie. nginx rewrites the responses from npm and replaces registry.npmjs.org with your own host information. Starting the container I would know that information, but inside the running container, where the information was needed, I wouldn’t know unless I had a way to pass it in.

I managed this by creating a simple script called runner that checks for two environment variables to be passed in: the required PORT and the optional HOST value. HOST is optional because I know what it is for boot2docker (what I use locally). PORT is required because you have to tell Docker to bind to a specific port so you can control what nginx uses.

My runner script outputs information about whether those values are available, exiting if PORT isn’t, modifies the /etc/nginx.conf file, then starts nginx. The whole thing is less than 20 lines of code and could probably be made shorter.

Deploying with Docker

I got all of this running locally, but then the thought occurred to me that this shouldn’t be that hard to get running in the cloud. We use Digital Ocean a lot at Continuum, so I decided to see what support they have for Docker out-of-the-box. Turns out, you can launch a server with Docker already configured and ready to run.

With that, deploying is ridiculously easy. I started a small box with Docker installed, then used ssh to connect to the box, and ran the following commands:

docker pull tswicegood/npm-cache
export PORT=8080
docker run -d -e HOST=<my server's IP> -e PORT=$PORT -p $PORT:80 tswicegood/npm-cache

That’s it! Including network IO downloading the npm-cache, I spent less than five minutes from start to finish to get this deployed on a remote server. The best part, I can now use that server to deploy other infrastructure too!

Conclusion

Making deployment of a piece of infrastructure this easy is not a simple problem. I’m sure there are all sorts of edge cases that I haven’t hit yet, but kudos to the Docker team for making this so easy.

Check out Docker if you haven’t. The Getting Started tutorial is really great.


Building, Maintaining and Scaling Projects

Jul 15 2014 [Archived Version] □ Published at pydanny under tags  nasa whartonwc

These are the slides from the talk I gave today at the Wharton Web Conference.


July 2014 ShipIt Day Recap

Jul 14 2014 [Archived Version] □ Published at Caktus Blog

This past Friday we celebrated another ShipIt day at Caktus. There was a lot of open source contribution, exploring, and learning happening in the office. The projects ranged from native mobile Firefox OS apps, to development on our automated server provisioning templates via Salt, to front-end apps aimed at using web technology to create interfaces...


Deferred Tasks and Scheduled Jobs with Celery 3.1, Django 1.7 and Redis

Jul 14 2014 [Archived Version] □ Published at GoDjango Screencasts and Tutorials

Setting up celery with Django can be a pain, but it doesn't have to be. In this video learn what it takes to setup Celery for deferred tasks, and as your cron replacement. We will use Celery 3.1 and Django 1.7 both introduce changes you need to be aware of.
Watch Now...


Virtualenv for Django

Jul 14 2014 [Archived Version] □ Published at Programmer blog under tags  app django install package pip

One of main features and troubles for Django projects is the Django version you are using. Example for this would be the occasion of starting project for Django 1.4.x version. Trying to run it under Django 1.7.x would lead to various troubles, counting template changes, misconfiguration errors and so on.

Solution is to use virtual environment. The tool for this is called Virtualenv. It is a wrapper for a python interpreter. It enables you to have multiple versions of python packages that run independently.

The Virtualenv creates you a standalone environment. Basically it copies and keeps a system Python of yours in a specified directory. All the python packages will be installed there. (In case you have not forgot to activate it first, of course)

Lets get started then. We need Virtualenv itself for the first. There are many ways to install it depending on your system. Most obvious to use pip. Your installation command will look something like that:
pip install virtualenv
This installs the tool itself to your system. this only must be done once.

Assuming we have it now and going forward to usage basics. We need to create a virtual environment for our particular project. Usual rule for most of developers is to have a single virtual environment for a standalone project.
To create one for ours we need to be in the directory where we want it to reside. (Virtual environment is not movable by default). So choose wisely.
I suggest it be somewhere in the project directory. The directory that is up one step from the project one is ok too. Assuming we have a project that is called "example". I will enter the directory of that project:
$ cd example
garmoncheg:~/Projects/example$ ls -la
total 8
drwxr-xr-x 4 garmoncheg staff 136B Jul 14 14:26 ./
drwxr-xr-x 3 garmoncheg staff 102B Jul 14 14:26 ../
drwxr-xr-x 6 garmoncheg staff 204B Jul 14 14:26 example/
-rw-r--r-- 1 garmoncheg staff 250B Jul 14 14:26 manage.py
We have here a manage.py file to run our project with and a project files directory. Seems like a nice place to create a virtual environment at. Time to do it with executing a command:
$ virtualenv venv
New python executable in venv/bin/python
Installing setuptools............done.
Installing pip...............done.
We have commanded our virtualenv to create a directory for our new project environment and call it venv. You could use any name here. it will indicate your projects name or whatever your fantasy is up to. Mine project directory now looks like this:
$ ls -l
total 9
drwxr-xr-x 6 garmoncheg staff 204B Jul 14 14:26 example/
-rw-r--r-- 1 garmoncheg staff 250B Jul 14 14:26 manage.py
drwxr-xr-x 6 garmoncheg staff 204B Jul 14 14:32 venv/
I have a mange.py script here, example directory with project files and a venv directory containig my environment files.
Now we have created a directory for environment and put our environment in it. However system is not aware we are using this environment. We must activate it now. Doing that is easy enough.
Assuming you are in the same project directory and called the virtual environment we are creating "venv". Execute a command:
$ source venv/bin/activate
(venv)garmoncheg:~/Projects/example$
This will tell the system we are using the interpreter set we have just created. It is indicated with (venv) at the start of your terminal prompt. this means we are "inside" the environment now.
Note we are in the same directory we where before.

Lets familiarise ourselves with main virtualenv functionality now. Whats the main point of it?
Simple. All the "pip install something" made now in this terminal will install a Python package inside this venv folder, leaving the main system python files intact. You can have as many environment as you wish. You can install whatever Python package you want inside of that virtual environment.

Now lets go through basic commands. Lets try to run our project now, assuming you have followed my previous instructions. To do that just type:
(venv)$ ls
total 9
drwxr-xr-x 6 garmoncheg staff 204B Jul 14 14:26 example/
-rw-r--r-- 1 garmoncheg staff 250B Jul 14 14:26 manage.py
drwxr-xr-x 6 garmoncheg staff 204B Jul 14 14:32 venv/
(venv)garmoncheg:~/Projects/example$ python manage.py runserver
Traceback (most recent call last):
File "manage.py", line 8, in <module>
from django.core.management import execute_from_command_line
ImportError: No module named django.core.management
Whoops. We can not do that because there is no Django installed. Even though you have had it in system it does not seem to be present now.
All the credits to virtualenv activation command. (source path/to/activate/script). We have to have it installed in the environment now. However it is more convenient to look for ourselves there is no Dango in environment. Lets execute:
(venv)garmoncheg:~/Projects/example$ pip freeze
wsgiref==0.1.2
Tadaam. We only have wsgiref in our console environment now. Lets install django:
(venv)garmoncheg:~/Projects/example$ pip install django
Downloading/unpacking django
Downloading Django-1.6.5.tar.gz (6.6MB): 6.6MB downloaded
Running setup.py egg_info for package django

warning: no previously-included files matching '__pycache__' found under directory '*'
warning: no previously-included files matching '*.py[co]' found under directory '*'
Installing collected packages: django
Running setup.py install for django
changing mode of build/scripts-2.7/django-admin.py from 644 to 755

warning: no previously-included files matching '__pycache__' found under directory '*'
warning: no previously-included files matching '*.py[co]' found under directory '*'
changing mode of /Users/leopard/Developer/Python/Me/Tests/Projects/example/venv/bin/django-admin.py to 755
Successfully installed django
Cleaning up...
Tadaa. We have our django for the project now. Lets make sure with pip freeze:
(venv)garmoncheg:~/Projects/example$ pip freeze
Django==1.6.5
wsgiref==0.1.2
And our project now can run inside a wrapper. Lets test with:
(venv)leopard@garmoncheg:~/Projects/example$ python manage.py runserver

Validating models...

0 errors found
July 14, 2014 - 12:00:03
Django version 1.6.5, using settings 'example.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Yes it works.

Cons here that we must activate a virtual environment each time we want to seriously interact with our project. So even to run it we need to activate it first. but it is really the only option to work with multiple project in a system in an easy way.

To return the terminal to a proper state (back to system Python interpreter) you could just close it or deactivate the environment:
(venv)garmoncheg:~/Projects/example$ deactivate
garmoncheg:~/Projects/example$
Tadaa. (venv) preceding the terminal prompt is gone and we are back to the system interpreter.

Hope this helps someone. Suggestions? Helped? Please comment! I will really appreciate all the info.


Tip: Collecting Emails - Webhooks for UserVoice and WordPress.com

Jul 12 2014 [Archived Version] □ Published at atodorov.org

In my practice I like to use webhooks and integrate auxiliary services with my internal processes or businesses. One of these is the collection of emails. In this short article I'll show you an example of how to collect email addresses from the comments of a WordPress.com blog and the UserVoice feedback/ticketing system.

WordPress.com

For your WordPress.com blog from the Admin Dashboard navigate to Settings -> Webhooks and add a new webhook with action comment_post and fields comment_author, comment_author_email. A simple Django view that handles the input is shown below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@csrf_exempt
def hook_wp_comment_post(request):</p>

<pre><code>if not request.POST:
    return HttpResponse("Not a POST\n", content_type='text/plain', status=403)

hook = request.POST.get("hook", "")

if hook != "comment_post":
    return HttpResponse("Go away\n", content_type='text/plain', status=403)

name = request.POST.get("comment_author", "")
first_name = name.split(' ')[0]
last_name = ' '.join(name.split(' ')[1:])

details = {
    'first_name' : first_name,
    'last_name' : last_name,
    'email' : request.POST.get("comment_author_email", ""),
}

store_user_details(details)

return HttpResponse("OK\n", content_type='text/plain', status=200)
</code></pre>

<p>

UserVoice

For UserVoice navigate to Admin Dashboard -> Settings -> Integrations -> Service Hooks and add a custom web hook for the New Ticket notification. Then use a sample code like that:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@csrf_exempt
def hook_uservoice_new_ticket(request):</p>

<pre><code>if not request.POST:
    return HttpResponse("Not a POST\n", content_type='text/plain', status=403)

data = request.POST.get("data", "")
event = request.POST.get("event", "")

if event != "new_ticket":
    return HttpResponse("Go away\n", content_type='text/plain', status=403)

data = json.loads(data)

details = {
    'email' : data['ticket']['contact']['email'],
}

store_user_details(details)

return HttpResponse("OK\n", content_type='text/plain', status=200)
</code></pre>

<p>

store_user_details() is a function which handles the email/name received in the webhook, possibly adding them to a database or anything else.

I find webhooks extremely easy to setup and develop and used them whenever they are supported by the service provider. What other services do you use webhooks for? Please share your story in the comments.


Python Dev Tip: DRY your shell with PYTHONSTARTUP

Jul 12 2014 [Archived Version] □ Published at Revolution Systems Blog

Python Dev Tip: DRY your shell with PYTHONSTARTUP


NodeConf 2014: A story of aspiration and community

Jul 09 2014 [Archived Version] □ Published at Justin Abrahms

This year, I helped mentor NodeConf 2014, which was an amazing experience. I highly encourage you to get involved next year in any way you can. Mikeal Rogers puts on a fantastic conference that's sure to delight. More about the revelations I had after the jump.


Using Chart.js with Django

Jul 07 2014 [Archived Version] □ Published at GoDjango Screencasts and Tutorials

Chart.js Header

Chart.js is the new kid on on the block for JavaScript charts. Learn how to use them here to chart out the number of user registrations for the last 30 days.

The View

This view builds an array of people that registered on the site daily for the last 30 days.

from django.views.generic import TemplateView
from django.contrib.auth.models import User

import arrow


class AnalyticsIndexView(TemplateView):
    template_name = 'analytics/admin/index.html'

    def get_context_data(self, **kwargs):
        context = super(AnalyticsIndexView, self).get_context_data(**kwargs)
        context['30_day_registrations'] = self.thirty_day_registrations()
        return context

    def thirty_day_registrations(self):
        final_data = []

        date = arrow.now()
        for day in xrange(1, 30):
            date = date.replace(days=-1)
            count = User.objects.filter(
                date_joined__gte=date.floor('day').datetime,
                date_joined__lte=date.ceil('day').datetime).count()
            final_data.append(count)

        return final_data

The method thirty_day_registrations loops through from 1 to 30, and gets the count of registrations for that day. Then it returns that array back to the get_context_data method and assigns it to 30_day_registrations which is what we will use in our template.

The Template

The template is very basic in that it has just enough data to generate a line chart.

{% extends "base.html" %}

{% block extrahead %}
<script src="//cdnjs.cloudflare.com/ajax/libs/Chart.js/0.2.0/Chart.min.js" type="text/javascript"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
$( document ).ready(function() {
    var data = {
        labels: ['1', '5', '10', '15', '20', '25', '30'],
        datasets: [
            {
                label: "Site Registrations in the Last 30 Days",
                fillColor: "rgba(220,220,220,0.2)",
                strokeColor: "rgba(220,220,220,1)",
                pointColor: "rgba(220,220,220,1)",
                pointStrokeColor: "#fff",
                pointHighlightFill: "#fff",
                pointHighlightStroke: "rgba(220,220,220,1)",
                data: {{ 30_day_registrations }}
            }
        ]
    };
    var ctx = document.getElementById("myChart").getContext("2d");
    var myLineChart = new Chart(ctx).Line(data);
});
</script>
{% endblock %}

{% block content %}
<canvas id="myChart" width="500" height="300"></canvas>
{% endblock %}

If we go line-by-line of the important parts this should make more sense.

<canvas id="myChart" width="500" height="300"></canvas>

This is where in the page your chart is going to show up its only real requirement is it has an id and is a canvas object.

var ctx = document.getElementById("myChart").getContext("2d");

This gets us the canvas element for us to feed data to so we can see our chart.

var myLineChart = new Chart(ctx).Line(data);

We create a Chart object and pass it our canvas element. Then we create our Line chart using a data object we are going to create.

var data = {
    labels: ['1', '5', '10', '15', '20', '25', '30'],
    datasets: [
        {
            label: "Site Registrations in the Last 30 Days",
            fillColor: "rgba(220,220,220,0.2)",
            strokeColor: "rgba(220,220,220,1)",
            pointColor: "rgba(220,220,220,1)",
            pointStrokeColor: "#fff",
            pointHighlightFill: "#fff",
            pointHighlightStroke: "rgba(220,220,220,1)",
            data: {{ 30_day_registrations }}
        }
    ]
};

This is an example data object to create a chart. Most of it is self-explanatory, but the keys parts are:

  • labels: this is outside of the datasets object.
  • datasets: you can have multiple json objects in here for more lines in chart
  • data: in this case it is just an array, which in this case we are generating in our view and outputting here.

Conclusion

Chart.js is a great library for doing charting, especially if your primary audience is using more modern browsers. I have been looking at charting libraries, but so far this has been one of the best ones of both paid and free ones. The best part is right now chart.js is free. So if you need to do some charting I would say give it a shot.


django-parler 1.0 is out!

Jul 07 2014 [Archived Version] □ Published at Django Fluent CMS under tags  news

To make creating multilingual websites easier, django-parler 1.0 has just been released! This package allows creating translatable user data, by translating Django model fields.

The release includes full documentation, and a few other awesome features:

  • Python 3 support
  • Caching translations for performance
  • Prefetch support for performance
  • Support translatable slugs
  • Switch the language of the current page easily.
  • Easy migration for projects that used django-hvad before.
  • ORM methods for filtering translated models.

This is also the base for the next release of django-fluent-pages and django-fluent-contents, which also have multilingual support in git.


Tips for Upgrading Django

Jul 07 2014 [Archived Version] □ Published at Caktus Blog

From time to time we inherit code bases running outdated versions of Django and part of our work is to get them running a stable and secure version. In the past year we've done upgrades from versions as old as 1.0 and we've learned a few lessons along the way. Tests are a Must You cannot begin a...


Recreating the “Building a Blog in Django” Screencast

Jul 06 2014 [Archived Version] □ Published at ArunRocks under tags  django python3 technical

Few days back, Django 1.7 release candidate 1 was announced. I have never been so excited about a release in recent memory. It has a new app loading framework and schema migrations baked right in!

It is also the significant release that I had been waiting for to update my original Django screencast. With more than 77 thousand views, it is by far the most popular Django screencast on YouTube.

Back in 2012, when I was working in Django, there were a lot of comparisons between Rails and Django. One of the biggest selling points of Ruby on Rails was their official 15-minute blog screencast. It was fascinating to watch an expert Rails developer build an entire site from scratch especially how deftly they use TextMate, which could nearly read his mind.

Armed with a Linux terminal and Emacs, I set out to create a similar screencast for anyone interested in Django. Live coding was something that I had not tried before. I remember starting at 11 pm one night, hoping to complete my recording by midnight.

But by the time I finished a recording without any major goofups it was 5 am in the morning. So with barely any editing, I directly uploaded the raw footage to YouTube and hit the bed.

Over time, a lot of people have loved the screencast, often asking how they can setup their development environment to resemble mine. Emacs despite not being an IDE is often as productive or more, depending on how well you customise it.

Why the Remake?

I have done several more Django screencasts after that, preferring to demonstrate entire projects in Django rather than focus on a specific feature. But the original still remains a favourite as it is beginner-friendly and short.

Plenty of new cool features like Class-based Views had come to Django since then. I also noted that there were very few tutorials which used Python 3. For starting a new Django project, I would definitely recommend Python 3.

So, I decided to remake it but even shorter. Crazily enough, I wanted to cover a lot more than the first screencast. Here are the topics I wanted to touch upon (ones which were not covered in the first screencast are in bold):

  • New 1.7 defaults
  • Syntax changes in Python 3.4
  • QuerySets and Custom Managers
  • Admin and ModelAdmin
  • Rich-text posts with django-markdown
  • Generic Class-based Views
  • Tags
  • RSS Feeds
  • Migrations
  • Running testcases

Thankfully, Django 1.7 comes with great project defaults like a predefined SQLite 3 database and admin URLs setup, which gives you a running start. Migrations is a real time-saver as you don’t have to keep re-entering your test data.

The entire recording clocked slightly above 15 mins. Here is the final video and the text version below it:

Screencast

People have translated my tutorials into various languages in the past. So I have added subtitles to this screencast. Enjoy the automatic translations in your preferred language!

Text Version

Some prefer to read the transcript of the screencast. Assuming you already have Django 1.7 installed in Python 3.4, you can follow these steps.

  1. Start the project

    django-admin startproject qblog
    cd qblog
    ./manage.py migrate
    ./manage.py createsuperuser
    ./manage.py runserver
    
  2. Add an app called blog

    ./manage startapp blog
    
  3. Open blog/models.py and change its contents to:

    from django.db import models
    from django.core.urlresolvers import reverse
    
    class EntryQuerySet(models.QuerySet):
        def published(self):
            return self.filter(publish=True)
    
    class Entry(models.Model):
        title = models.CharField(max_length=200)
        body = models.TextField()
        slug = models.SlugField(max_length=200, unique=True)
        publish = models.BooleanField(default=False)
        created = models.DateTimeField(auto_now_add=True)
        modified = models.DateTimeField(auto_now=True)
    
        objects = EntryQuerySet.as_manager()
    
        def __str__(self):
            return self.title
    
        class Meta:
            verbose_name = "Blog Entry"
            verbose_name_plural = "Blog Entries"
            ordering = ["-created"]
    
  4. In qblog/settings.py add "blog" to INSTALLED_APPS and migrate:

    ./manage.py makemigrations blog
    ./manage.py migrate blog
    
  5. Change blog/admin.py to:

    from django.contrib import admin
    from . import models
    
    class EntryAdmin(admin.ModelAdmin):
        list_display = ("title", "created")
        prepopulated_fields = {"slug": ("title",)}
    
    admin.site.register(models.Entry, EntryAdmin)
    

    Go to admin. Add an unpublished post and published one. Then open ./manage shell and check the difference between Entry.objects.all() and Entry.objects.published().

Markdown

  1. Install django-markdown

    pip install django-markdown
    
  2. Add "django_markdown" to INSTALLED_APPS.

  3. Add a second line to qblog/urls.py:

    url(r'^markdown/', include('django_markdown.urls')),
    
  4. Change blog/admin.py to:

    from django_markdown.admin import MarkdownModelAdmin
    
    class EntryAdmin(MarkdownModelAdmin):
        ...
    

    Enter test entries now.

Index View

  1. Change blog/views.py to:

    from django.views import generic
    from . import models
    
    class BlogIndex(generic.ListView):
        queryset = models.Entry.objects.published()
        template_name = "home.html"
        paginate_by = 2
    
  2. Create a url mapping in qblog/urls.py:

    urlpatterns = patterns(
        '',
        url(r'^admin/', include(admin.site.urls)),
        url(r'^markdown/', include('django_markdown.urls')),
        url(r'^', include('blog.urls')),
    )
    
  3. Create blog/urls.py:

    from django.conf.urls import patterns, include, url
    from . import views
    
    urlpatterns = patterns(
        '',
        url(r'^$', views.BlogIndex.as_view(), name="index"),
    )
    

Index Template

  1. Make template and static directories. Then, copy the files:

    mkdir templates
    cp -R /somewhere/templates/* templates
    mkdir static
    cp -R /somewhere/static/* static
    
  2. Create templates/home.html with:

    {% extends "base.html" %}
    {% load django_markdown %}
    
    {% block blog_entries %}
    {% for object in object_list %}
      <div class="post">
        <h2>{{ object.title }}</h2>
        <p class="meta">{{ object.created }}</p>
        {{ object.body|markdown }}
      </div>
    {% endfor %}
    {% endblock %}
    
  3. Add to qblog/settings.py:

    TEMPLATE_DIRS = (os.path.join(BASE_DIR, "templates"), )
    STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"), )
    

    Refresh the home page now. Enjoy the first non-admin page.

Feed

  1. Create blog/feed.py with:

    from django.contrib.syndication.views import Feed
    from blog.models import Entry
    
    class LatestPosts(Feed):
        title = "Q Blog"
        link = "/feed/"
        description = "Latest Posts"
    
        def items(self):
            return Entry.objects.published()[:5]
    
  2. Add to blog/urls.py (last but one line):

    url(r'^feed/$', feed.LatestPosts(), name="feed"),
    
  3. Mention in the templates/base.html template’s HEAD:

    <link href="/feed/" rel="alternate" type="application/rss+xml" title="Q Blog Feed" />
    

Entry View

  1. Add to blog/views.py:

    class BlogDetail(generic.DetailView):
        model = models.Entry
        template_name = "post.html"
    
  2. Add to blog/urls.py:

    url(r'^(?P<slug>\S+)$', views.BlogDetail.as_view(), name="entry_detail"),
    
  3. Add to blog/models.py:

    objects = EntryQuerySet.as_manager()
    
    def get_absolute_url(self):
        return reverse("entry_detail", kwargs={"slug": self.slug})
    

Entry Template

  1. Open templates/home.html and save it as post.html while removing the for loop, like this:

    {% extends "base.html" %}
    {% load django_markdown %}
    
    <div class="post">
      <h2><a href="{% url "entry_detail" slug=object.slug %}">{{ object.title }}</a></h2>
      <p class="meta">
        {{ object.created }} |
        Tagged under {{  object.tags.all|join:", " }}
      </p>
      {{ object.body|markdown }}
    </div>
    

    Update templates/home.html with the same <h2> line.

Tags

  1. Add tags to blog/models.py:

    class Tag(models.Model):
        slug = models.SlugField(max_length=200, unique=True)
    
        def __str__(self):
            return self.slug
    
    class Entry(models.Model):
        ....
        tags = models.ManyToManyField(Tag)
    
  2. Migrate:

    ./manage.py makemigrations posts
    ./manage.py migrate posts
    
  3. Add to blog/admin.py:

    admin.site.register(models.Tag)
    
  4. Change template templates/_entry.html to:

    <p class="meta">{{ object.created }} |
      Tagged under {{ object.tags.all|join:", " }}
    </p>
    

Testing

  1. Add these two test cases in blog/tests.py:

    from django.test import TestCase
    from django.contrib.auth import get_user_model
    from .models import Entry
    
    class BlogPostTest(TestCase):
    
        def test_create_unpublished(self):
            entry = Entry(title="Title Me", body=" ", publish=False)
            entry.save()
            self.assertEqual(Entry.objects.all().count(), 1)
            self.assertEqual(Entry.objects.published().count(), 0)
            entry.publish = True
            entry.save()
            self.assertEqual(Entry.objects.published().count(), 1)
    
    class BlogViewTests(TestCase):
        def test_feed_url(self):
            response = self.client.get('/feed/')
            self.assertIn("xml", response['Content-Type'])
    
  2. Run the tests by ./manage.py test blog and they should both pass.

Final Notes

The entire source is available on Github. If you face any issues, make sure that you are running the same Python/Django versions and compare your code with mine.

As always, I would love to hear your comments and feedback.


Pillow 2-5-0 is out!

Jul 05 2014 [Archived Version] □ Published at Alex Clark - Python Web Developer & Musician under tags  django mozilla plone python

Pillow is the "friendly" PIL fork by Alex Clark and Contributors. PIL is the Python Imaging Library by Fredrik Lundh and Contributors

Since Pillow 2.0 the Pillow Team has adopted a quarterly release cycle; as such, Pillow 2.5.0 is out! Here's what's new in this release:

2.5.0 (2014-07-01)

  • Imagedraw rewrite [terseus, wiredfool]
  • Add support for multithreaded test execution [wiredfool]
  • Prevent shell injection #748 [mbrown1413, wiredfool]
  • Support for Resolution in BMP files #734 [gcq]
  • Fix error in setup.py for Python 3 [matthew-brett]
  • Pyroma fix and add Python 3.4 to setup metadata #742 [wirefool]
  • Top level flake8 fixes #741 [aclark]
  • Remove obsolete Animated Raster Graphics (ARG) support [hugovk]
  • Fix test_imagedraw failures #727 [cgohlke]
  • Fix AttributeError: class Image has no attribute 'DEBUG' #726 [cgohlke]
  • Fix msvc warning: 'inline' : macro redefinition #725 [cgohlke]
  • Cleanup #654 [dvska, hugovk, wiredfool]
  • 16-bit monochrome support for JPEG2000 [videan42]
  • Fixed ImagePalette.save [brightpisces]
  • Support JPEG qtables [csinchok]
  • Add binary morphology addon [dov, wiredfool]
  • Decompression bomb protection [hugovk]
  • Put images in a single directory [hugovk]
  • Support OpenJpeg 2.1 [al45tair]
  • Remove unistd.h #include for all platforms [wiredfool]
  • Use unittest for tests [hugovk]
  • ImageCms fixes [hugovk]
  • Added more ImageDraw tests [hugovk]
  • Added tests for Spider files [hugovk]
  • Use libtiff to write any compressed tiff files [wiredfool]
  • Support for pickling Image objects [hugovk]
  • Fixed resolution handling for EPS thumbnails [eliempje]
  • Fixed rendering of some binary EPS files (Issue #302) [eliempje]
  • Rename variables not to use built-in function names [hugovk]
  • Ignore junk JPEG markers [hugovk]
  • Change default interpolation for Image.thumbnail to Image.ANTIALIAS [hugovk]
  • Add tests and fixes for saving PDFs [hugovk]
  • Remove transparency resource after P->RGBA conversion [hugovk]
  • Clean up preprocessor cruft for Windows [CounterPillow]
  • Adjust Homebrew freetype detection logic [jacknagel]
  • Added Image.close, context manager support. [wiredfool]
  • Added support for 16 bit PGM files. [wiredfool]
  • Updated OleFileIO to version 0.30 from upstream [hugovk]
  • Added support for additional TIFF floating point format [Hijackal]
  • Have the tempfile use a suffix with a dot [wiredfool]
  • Fix variable name used for transparency manipulations [nijel]

Acknowledgements

With every release, there are notable contributions I must acknowledge:

  • Thanks to Stephen Johnson for contributing http://pillow.readthedocs.org, we continue to rely on & extend this resource.
  • Thanks to Christopher Gohlke for producing Windows Egg, Exe, and Wheel distributions.
  • Thanks to Matthew Brett for producing OS X Wheels (for the first time ever!)
  • Thanks to Eric Soroos for his contributions and serving as "Pillow Man #2" (2nd in command).
  • Welcome to Hugo VK who has joined the Pillow Team & contributed significantly to this release.
  • Thanks to all the remaining unnamed contributors! We appreciate every commit.

Enjoy Pillow 2.5.0 & please report issues here: https://github.com/python-imaging/Pillow/issues


django-planet aggregates posts from Django-related blogs. It is not affiliated with or endorsed by the Django Project.

Social Sharing

Feeds

Tag cloud

admin administration advanced ajax apache api app appengine app engine apple aprendiendo python architecture articles asides audrey authentication automation backup bash basics bitbucket blog blog action day book books buildout business cache celery celerycrawler challenges cherokee choices class-based-views cliff cloud code coding couchdb css d data database databases debian deploy deployment developers development digitalocean django djangocon django-rest-framework django templates documentation dojango dojo dreamhost dughh eclipse education email encoding error events extensions fabric facebook family fashiolista fedora field filter fix flash flask form forms friends gae gallery games geek general gentoo gis git github gnome google google app engine gunicorn hackathon hacking hamburg heroku holidays hosting howto how-tos html http i18n image install intermediate internet ios iphone java javascript jobs journalism jquery json justmigrated linear regression linkedin linode linux mac machine learning math memcached mercurial meta migration mirror misc model models mod_wsgi mongodb mozilla mvc mysql nelenschuurmans newforms news nginx nosql ogólne open source open-source orm osx os x ottawa paas performance philosophy php pi pil pinax pip piston planet plone plugin pony postgres postgresql ppoftw private programmieren programming programming &amp; internet project projects pycon pygrunn pyladies pypi pypy python python3 quick tips quora rabbitmq rails rant ratnadeep debnath redis release request resolutions rest review rtnpro ruby science script security server setup simple smiley snaking software software development south sql ssh ssl static storage supervisor svn sysadmin tag talk nerdy to me tastypie tdd techblog technical technology template templates test testing tests tip tools tornado transifex travel tumbles tutorial twitter twoscoops typo3 ubuntu uncategorized unicode unix usergroup uwsgi uxebu virtualenv virtualenvwrapper web web 2.0 web design &amp; development webdev web development webfaction whoosh windows wordpress work