Book Image

Web Development with Django Cookbook

By : Aidas Bendoraitis
Book Image

Web Development with Django Cookbook

By: Aidas Bendoraitis

Overview of this book

<p>Django is easy to learn and solves all types of web development problems and questions, providing Python developers an easy solution to web-application development. With a wealth of third-party modules available, you'll be able to create a highly customizable web application with this powerful framework.</p> <p>Web Development with Django Cookbook will guide you through all web development processes with the Django framework. You will get started with the virtual environment and configuration of the project, and then you will learn how to define a database structure with reusable components. Find out how to tweak the administration to make the website editors happy. This book deals with some important third-party modules necessary for fully equipped web development.</p> <p>&nbsp;</p> <div class="book-toc-chapter">&nbsp;</div> <h2>Read an extract of the book</h2> <h3>Creating Filterable RSS Feeds</h3> <p>Django comes with a syndication feed framework that allows you to create RSS and Atom feeds easily. RSS and Atom feeds are XML documents with specific semantics. They can be subscribed in an RSS reader such as Feedly, or they can be aggregated into other websites, mobile applications, or desktop applications. In this recipe, we will create <em>BulletinFeed</em>, which provides a bulletin board with images. Moreover, the results will be filterable by URL query parameters.</p> <h4>Getting ready</h4> <p>Create a new <em>bulletin_board</em> app and put it under <em>INSTALLED_APPS</em> in the settings.</p> <h4>How to do it…</h4> <p>We will create a <em>Bulletin</em> model and an RSS feed for it that can be filtered by type or category, so that the visitor can subscribe only to bulletins that are, for example, offering used books:</p> <ol> <li>In the <em>models.py</em> file of that app, add the models <em>Category</em> and <em>Bulletin</em> with a foreign key relationship between them: <pre class="line-numbers"><code class="language-python">#bulletin_board/models.py # -*- coding: UTF-8 -*- from django.db import models from django.utils.translation import ugettext_lazy as _ from django.core.urlresolvers import reverse from utils.models import CreationModificationDateMixin from utils.models import UrlMixin TYPE_CHOICES = ( ("searching", _("Searching")), ("offering", _("Offering")), ) class Category(models.Model): title = models.CharField(_("Title"), max_length=200) def __unicode__(self): return self.title class Meta: verbose_name = _("Category") verbose_name_plural = _("Categories") class Bulletin(CreationModificationDateMixin, UrlMixin): bulletin_type = models.CharField(_("Type"), max_length=20, choices=TYPE_CHOICES) category = models.ForeignKey(Category, verbose_name=_("Category")) title = models.CharField(_("Title"), max_length=255) description = models.TextField(_("Description"), max_length=300) contact_person = models.CharField(_("Contact person"), max_length=255) phone = models.CharField(_("Phone"), max_length=200, blank=True) email = models.CharField(_("Email"), max_length=254, blank=True) image = models.ImageField(_("Image"), max_length=255, upload_to="bulletin_board/", blank=True) class Meta: verbose_name = _("Bulletin") verbose_name_plural = _("Bulletins") ordering = ("-created",) def __unicode__(self): return self.title def get_url_path(self): return reverse("bulletin_detail", kwargs={"pk": self.pk}) </code></pre> </li> <li>Then, create <em>BulletinFilterForm</em> that allows the visitor to filter bulletins by type and by category, as follows: <pre class="line-numbers"><code class="language-python">#bulletin_board/forms.py # -*- coding: UTF-8 -*- from django import forms from django.utils.translation import ugettext_lazy as _ from models import Category, TYPE_CHOICES class BulletinFilterForm(forms.Form): bulletin_type = forms.ChoiceField( label=_("Bulletin Type"), required=False, choices=(("", "---------"),) + TYPE_CHOICES, ) category = forms.ModelChoiceField( label=_("Category"), required=False, queryset=Category.objects.all(), ) </code></pre> </li> <li>Add a <em>feeds.py</em> file with the <em>BulletinFeed</em> class inside, as follows: <pre class="line-numbers"><code class="language-python">#bulletin_board/feeds.py # -*- coding: UTF-8 -*- from django.contrib.syndication.views import Feed from django.core.urlresolvers import reverse from models import Bulletin, TYPE_CHOICES from forms import BulletinFilterForm class BulletinFeed(Feed): description_template = "bulletin_board/feeds/bulletin_description.html" def get_object(self, request, *args, **kwargs): form = BulletinFilterForm(data=request.REQUEST) obj = {} if form.is_valid(): obj = { "bulletin_type": form.cleaned_data["bulletin_type"], "category": form.cleaned_data["category"], "query_string": request.META["QUERY_STRING"], } return obj def title(self, obj): t = u"My Website - Bulletin Board" # add type "Searching" or "Offering" if obj.get("bulletin_type", False): tp = obj["bulletin_type"] t += u" - %s" % dict(TYPE_CHOICES)[tp] # add category if obj.get("category", False): t += u" - %s" % obj["category"].title return t def link(self, obj): if obj.get("query_string", False): return reverse("bulletin_list") + "?" + obj["query_string"] return reverse("bulletin_list") def feed_url(self, obj): if obj.get("query_string", False): return reverse("bulletin_rss") + "?" + obj["query_string"] return reverse("bulletin_rss") def item_pubdate(self, item): return item.created def items(self, obj): qs = Bulletin.objects.order_by("-created") if obj.get("bulletin_type", False): qs = qs.filter( bulletin_type=obj["bulletin_type"], ).distinct() if obj.get("category", False): qs = qs.filter( category=obj["category"], ).distinct() return qs[:30] </code></pre> </li> <li>Create a template for the bulletin description in the feed as follows: <pre class="line-numbers"><code class="language-python">{#templates/bulletin_board/feeds/bulletin_description.html#} {% if obj.image %} &lt;p&gt;&lt;a href="{{ obj.get_url }}"&gt;&lt;img src="http://{{ request.META.HTTP_HOST }}{{ obj.image.url }}" alt="" /&gt;&lt;/a&gt;&lt;/p&gt; {% endif %} &lt;p&gt;{{ obj.description }}&lt;/p&gt; </code></pre> </li> <li>Create a URL configuration for the <em>bulletin board</em> app and include it in the root URL configuration, as follows: <pre class="line-numbers"><code class="language-python">#templates/bulletin_board/urls.py # -*- coding: UTF-8 -*- from django.conf.urls import * from feeds import BulletinFeed urlpatterns = patterns("bulletin_board.views", url(r"^$", "bulletin_list", name="bulletin_list"), url(r"^(?P&lt;bulletin_id&gt;[0-9]+)/$", "bulletin_detail", name="bulletin_detail"), url(r"^rss/$", BulletinFeed(), name="bulletin_rss"), ) </code></pre> </li> <li>You will also need the views and templates for the filterable list and details of the bulletins. In the <em>Bulletin</em> list page template, add this link: <pre class="line-numbers"><code class="language-python">&lt;a href="{% url "bulletin_rss" %}?{{ request.META.QUERY_STRING }}"&gt;RSS Feed&lt;/a&gt;</code></pre> </li> </ol> <h4>How it works…</h4> <p>So, if you have some data in the database and you open <em>http://127.0.0.1:8000/bulletin-board/rss/?bulletin_type=offering&amp;category=4</em> in your browser, you will get an RSS feed of bulletins with the type Offering and category ID 4.</p> <p>The <em>BulletinFeed</em> class has the <em>get_objects</em> method that takes the current <em>HttpRequest</em> and defines the <em>obj</em> dictionary used in other methods of the same class.</p> <p>The <em>obj</em> dictionary contains the bulletin type, category, and current query string.</p> <p>The <em>title</em> method returns the title of the feed. It can either be generic or related to the selected bulletin type or category. The <em>link</em> method returns the link to the original bulletin list with the filtering done. The <em>feed_url</em> method returns the URL of the current feed. The items method does the filtering itself and returns a filtered <em>QuerySet</em> of bulletins. And finally, the <em>item_pubdate</em> method returns the creation date of the bulletin.</p> <p>To see all the available methods and properties of the <em>Feed</em> class that we are extending, refer to the following documentation: <a href="https://docs.djangoproject.com/en/1.10/ref/contrib/syndication/#feed-class-reference">https://docs.djangoproject.com/en/1.10/ref/contrib/syndication/#feed-class-reference</a></p> <p>The other parts of the code are kind of self-explanatory!</p>
Table of Contents (17 chapters)
Web Development with Django Cookbook
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Including external dependencies in your project


Sometimes, it is better to include external dependencies in your project. This ensures that whenever one developer upgrades third-party modules, all the other developers will receive the upgraded version within the next update from the version control system (Git, Subversion, or others).

Also, it is good to have external dependencies included in your project when the libraries are taken from unofficial sources (somewhere other than Python Package Index (PyPI)) or different version control systems.

Getting ready

Start with a virtual environment with a Django project in it.

Tip

Downloading the example code

You can download the example code fles for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the fles e-mailed directly to you.

How to do it...

Execute the following steps one by one:

  1. Create a directory named externals under your Django project.

  2. Then, create the libs and apps directories under it.

    The libs directory is for Python modules that are required by your project, for example, requests, boto, twython, whoosh, and so on. The apps directory is for third-party Django apps, for example, django-haystack, django-cms, django-south, django-storages, and so on.

    Tip

    I highly recommend that you create README.txt files inside the libs and apps directories, where you mention what each module is for, what the used version or revision is, and where it is taken from.

  3. So, the directory structure should look like this:

  4. The next step is to put the external libraries and apps under the Python path so that they are recognized as if they were installed. This can be done by adding the following code in the settings:

    #settings.py
    # -*- coding: UTF-8 -*-
    import os
    import sys
    
    PROJECT_PATH = os.path.abspath(os.path.join(
        os.path.dirname(__file__), ".."))
    
    EXTERNAL_LIBS_PATH = os.path.join(PROJECT_PATH, "externals", "libs")
    EXTERNAL_APPS_PATH = os.path.join(PROJECT_PATH, "externals", "apps")
    sys.path = ["", EXTERNAL_LIBS_PATH, EXTERNAL_APPS_PATH] + sys.path

How it works...

A module is meant to be under the Python path if you can run Python and import the module. One of the ways to put a module under the Python path is to modify the sys.path variable before importing a module that is in an unusual location. The value of sys.path is a list of directories starting with an empty string for the current directory, followed by the directories in the virtual environment, and finally the globally shared directories of the Python installation. You can see the value of sys.path in the Python shell, as follows:

(myproject_env)$ python
>>> import sys
>>> sys.path

When trying to import a module, Python searches for the module in this list and returns the first result found.

So, at first, we define the PROJECT_PATH variable that is the absolute path to one level higher than the settings.py file. Then, we define the variables EXTERNAL_LIBS_PATH and EXTERNAL_APPS_PATH, which are relative to PROJECT_PATH. Lastly, we modify the sys.path property, adding new paths to the beginning of the list. Note that we also add an empty string as the first path to search, which means that the current directory of any module should always be checked first before checking other Python paths.

Tip

This way of including external libraries doesn't work cross-platform with Python packages that have C language bindings, for example, lxml. For such dependencies, I recommend using pip's requirements.txt file that was introduced in the previous recipe.

See also

  • The Creating a project file structure recipe

  • The Handling project dependencies with pip recipe

  • The Defining relative paths in the settings recipe

  • The Using the Django shell recipe in Chapter 10, Bells and Whistles