Welcome to django-libs’s documentation!

django-libs provides useful tools and helpers that can be used in almost all Django applications. By putting all these into one central package we hope to ease our maintenance work across multiple Django projects and to reduce tedious (and erroneous) copy and paste orgies.

Contents:

Context Processors

analytics

Most projects have the Google Analytics tracking code in their base template. If you like to put that code into a partial template or even re-use your whole base template between projects, then it would be a good idea to set the analytics code in your local_settings.py and add it to your template context using this context processor.

Add the processor to your list of context processors:

TEMPLATE_CONTEXT_PROCESSORS = (
    ...
    'django_libs.context_processors.analytics',
)

Use it in your template:

<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', '{{ ANALYTICS_TRACKING_ID }}', '{{ ANALYTICS_DOMAIN }}');
  ga('send', 'pageview');
</script>

Decorators

lockfile

Very useful for custom admin commands. If your command is scheduled every minute but might take longer than one minute, it would be good to prevent execution of the command if the preceeding execution is still running. Lockfiles come in handy here.

Note: This decorator requires the lockfile package to be installed. Either add it to your requirements if not already in or to get the latest version from pypi do:

pip install lockfile

You should create a setting LOCKFILE_PATH which points to /home/username/tmp/.

Usage:

from django_libs.decorators import lockfile
...

LOCKFILE = os.path.join(
    settings.LOCKFILE_PATH, 'command_name')

class Command(BaseCommand):

    @lockfile(LOCKFILE)
    def handle(self, *args, **kwargs):
        ...

Factories

IMPORTANT: The following factories are still available, but no longer maintained. We recommend to use https://github.com/klen/mixer for fixtures.

Factories will be removed in v2.

HvadFactoryMixin

Writing factories for models under django-hvad is a bit hard because for each object you also have to create a translation object. This mixin takes care of this. Simply inherit from this mixin and write your factory as if it was a normal model, but make sure to add a language_code field with the default language you would like to use:

import factory
from django_libs.tests.factories import HvadFactoryMixin

from .. import models

class NewsEntryFactory(HvadFactoryMixin, factory.DjangoModelFactory):
    language_code = 'en'  # This is important
    title = factory.Sequence(lambda x: 'A title {0}'.format(x))
    slug = factory.Sequence(lambda x: 'a-title-{0}'.format(x))
    is_published = True

    class Meta:
        model = models.NewsEntry

UserFactory

We use https://github.com/rbarrois/factory_boy to create fixtures for our tests. Since Django comes with a User model that is needed by almost all tests a UserFactory is useful for all Django projects.

Usage

Let’s assume you want to write a view test and your view requires an authenticated user. You can create the user using the UserFactory like so:

from django.test import TestCase
from django_libs.tests.factories import UserFactory

class MyViewTest(TestCase):
    def setUp(self):
        self.user = UserFactory()

    def test_view(self):
        self.client.login(username=self.user.email, password='test123')
        resp = self.client.get('/')
        self.assertEqual(resp.status_code, 200)

UploadedImageFactory

Are you also tired of having to deal with images in upload form tests? Well here’s help! With the UploadedImageFactory you can create a SimpleUploadedFile with just one line of code.

Example:

# Say your form has an image field
from django import forms

MyImageForm(forms.Form):
    avatar = forms.ImageField(...)
    ...  # other fields

# Then you want to test this, so in your test case you do
from django.test import TestCase

from django_libs.tests.factories import UploadedImageFactory

from ..forms import MyForm

class MyImageFormTestCase(TestCase):
    def test_form(self):
        files = {'avatar': UploadedImageFactory()}
        data = {
            ...  # other data
        }
        form = MyForm(data=data, files=files)
        self.assertTrue(form.is_valid())

Fields

ColorField

If you want to store hex color code to a field, you can make use of the ColorField. It also provides a color picker, which can be used in the admin.

Simple add it to your color classes:

from django.db import models

from hvad.models import TranslatableModel, TranslatedFields
from django_libs.models_mixins import TranslationModelMixin


class Category(models.Model):
    color = ColorField()

Forms

PlaceholderForm

Simple form mixin, which uses the field label as a placeholder attribute. E.g.:

first_name = forms.CharField(label=_(‘Name’))

will be rendered as:

<input id=”id_first_name” name=”first_name” placeholder=”Name” type=”text”>

StripTagsFormMixin

A mixin that allows to mark certain fields to not allow any HTML tags.

Then the form gets initiated and data is given, all HTML tags will be stripped away from that data.

Usage:

class MyForm(StripTagsFormMixin, forms.Form):
    text = forms.CharField(max_length=1000)

    STRIP_TAGS_FIELDS = ['text', ]

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        self.strip_tags()
  1. Inherit from StripTagsFormMixin
  2. Add STRIP_TAGS_FIELDS attribute to your form class
  3. Override __init__ and call self.strip_tags() after your super call

JavaScript

getcookie.js

Provides the function getCookie which allows to retrieves values from the cookie. This is especially useful when you want to do a POST request via AJAX.

Usage:

<script src="{{ STATIC_URL }}django_libs/js/getcookie.js"></script>
<script>
    var data = [{name: 'csrfmiddlewaretoken', value: getCookie('csrftoken')}];
    $.post(
        '/some/url/'
        ,data
        ,function(data) {
            if (data == 'success') {
                // do something
            }
        }
    );
</script>

modals.js

Provides functions to easily get or post requests that should be shown in a Twitter Bootstrap modal. In order to use this:

  1. Make sure that you are using bootstrap-modal
  2. Make sure that you are using the AjaxRedirectMiddleware
  3. Add <div id=”ajax-modal” class=”modal hide fade” tabindex=”-1”></div> at the end of your base.html

Now you could place a button somewhere in your code and use the onclick event to open the modal. You can pass in the URL that serves the modal’s template and extra context that should be sent in the request as GET data:

<a href="#" onclick="getModal('/ajax-url/', {next: '/profile/'}); return false;">Open Modal</a>

In your modal you might have a form with a submit button. You can now trigger the POST request like so:

// This is how your modal template should look like
<form id="formID" method="post" action="/ajax-url/">
    {% csrf_token %}
    <div class="modal-body">
        <fieldset>
            {% for field in form %}
                {% include "partials/form_field.html" %}
            {% endfor %}
        </fieldset>
        <input type="hidden" name="next" value="{% if next %}{{ next }}{% endif %}" />
    </div>
    <div class="modal-footer">
        <input type="button" onclick="postModal('/ajax-url/', $('#formID')); return false;" value="Submit">
    </div>
</form>

Template Tags

add_form_widget_attr

Adds widget attributes to a bound form field.

This is helpful if you would like to add a certain class to all your forms (i.e. form-control to all form fields when you are using Bootstrap):

{% load libs_tags %}
{% for field in form.fields %}
    {% add_form_widget_attr field 'class' 'form-control' as field_ %}
    {{ field_ }}
{% endfor %}

The tag will check if the attr already exists and only append your value. If you would like to replace existing attrs, set replace=1:

{% add_form_widget_attr field 'class' 'form-control' replace=1 as field_ %}

block_anyfilter

Turns any template filter into a blocktag.

Usage:

{% load libs_tags %}
{% block_anyfilter django.template.defaultfilters.truncatewords_html 15 %}
    // Something complex that generates html output
{% endblockanyfilter %}

This is useful when you are working with django-cms’ render_placeholder tag, for example. That tag is unfortunately not an assignment tag, therefore you can’t really do anything with the output. Imagine you want to show a list of latest news and for each news a little excerpt based on the content placeholder. That placeholder could contain anything, like images and h1 tags but you really just want to show the first ten words without images or any styles. Now you can do this:

{% block_anyfilter django.template.defaultfilters.truncatewords_html 15 %}
{% block_anyfilter django.template.defaultfilters.striptags %}
{% render_placeholder news_entry.content %}

{% endblockanyfilter %}

{% endblockanyfilter %}

calculate_dimensions

calculate_dimensions is a way to auto-correct thumbnail dimensions depending on the images format. The required args are am image instance, the length of the long image side and finally the length of the short image side.

Usage Example with easy_thumbnails:

{% load libs_tags thumbnail %}
{% calculate_dimensions image 320 240 as dimensions%}
<img src="{% thumbnail image dimensions %}" />

It then ouputs 320x240 if the image is landscape and 240x320 if the image is portait.

call

call is an assignemnt tag that allows you to call any method of any object with args and kwargs, because you do it in Python all the time and you hate not to be able to do it in Django templates.

Usage:

{% load libs_tags %}
{% call myobj 'mymethod' myvar foobar=myvar2 as result %}
{% call myobj 'mydict' 'mykey' as result %}
{% call myobj 'myattribute' as result %}
{{ result }}

concatenate

Concatenates the given strings.

Usage:

{% load libs_tags %}
{% concatenate "foo" "bar" as new_string %}
{% concatenate "foo" "bar" divider="_" as another_string %}

The above would result in the strings “foobar” and “foo_bar”.

exclude

exclude is a filter tag that allows you to exclude one queryset from another.

Usage:

{% load libs_tags %}
{% for clean_obj in qs|exclude:dirty_qs %}
    {{ clean_obj }}
{% endfor %}

get_content_type

get_content_type is a simple template filter to return the content type of an object or to return one of the content type’s fields.

This might be very useful if you want to e.g. call a URL, which needs a content object as a keyword argument.

In order to use it, just import the tag library and set the tag:

{% load libs_tags %}
<a href="{% url "review_content_object" content_type=user|get_content_type:'model' object_id=user.pk %}">Review this user!</a>

As you can see, you can provide a field argument to return the relevant content type’s field.

get_form_field_type

Returns the widget type of the given form field.

This can be helpful if you want to render form fields in your own way (i.e. following Bootstrap standards).

Usage:

{% load libs_tags %}
{% for field in form %}
    {% get_form_field_type field as field_type %}
    {% if "CheckboxInput" in field_type %}
        <div class="checkbox">
            <label>
                // render input here
            </label>
        </div>
    {% else %}
        {{ field }}
    {% endif %}
{% endfor %}

get_range

get_range behaves just like Python’s range function and allows you to iterate over ranges in your templates:

{% load libs_tags %}
{% for item in 5|get_range %}
    Item number {{ item }}
{% endfor %}

You can also calculate the difference between your value and a max value. This is useful if you want to fill up empty space with items so that the total amount of items is always max_num:

{% load libs_tags %}
{% for item in object_list.count|get_range %}
    // render the actual items
{% endfor %}
{% for item in object_list.count|get_range:10 %}
    // render the placeholder items to fill up the space
{% endfor %}

get_range_around

Returns a range of numbers around the given number.

This is useful for pagination, where you might want to show something like this:

<< < ... 4 5 (6) 7 8 .. > >>

In this example 6 would be the current page and we show 2 items left and right of that page.

Usage:

{% load libs_tags %}
{% get_range_around page_obj.paginator.num_pages page_obj.number 2 as pages %}

The parameters are:

  1. range_amount: Number of total items in your range (1 indexed)
  2. The item around which the result should be centered (1 indexed)
  3. Number of items to show left and right from the current item.

get_verbose

get_verbose is a simple template tag to provide the verbose name of an object’s specific field.

This can be useful when you are creating a DetailView for an object where, for some reason you don’t want to use a ModelForm. Instead of using the {% trans %} tag to create your labels and headlines that are related to the object’s fields, you can now obey the DRY principle and re-use the translations that you have already done on the model’s field’s verbose_name attributes.

In order to use it, just import the tag library and set the tag:

{% load libs_tags %}
<ul>
    <li>
        <span>{{ news|get_verbose:"date" }}</span>
    </li>
    <li>
        <span>{{ news|get_verbose:"title" }}</span>
    </li>
</ul>

get_query_params

Allows to change (or add) one of the URL get parameter while keeping all the others.

Usage:

{% load libs_tags %}
{% get_query_params request "page" page_obj.next_page_number as query %}
<a href="?{{ query }}">Next</a>

You can also pass in several pairs of keys and values:

{% get_query_params request "page" 1 "foobar" 2 as query %}

You often need this when you have a paginated set of objects with filters.

Your url would look something like /?region=1&gender=m. Your paginator needs to create links with &page=2 in them but you must keep the filter values when switching pages.

If you want to remove a special parameter, you can do that by setting it’s value to !remove:

{% get_query_params request "page" 1 "foobar" "!remove" as query %}

get_site

get_site returns the current site.

In order to use it, just import the tag library and set the tag:

{% load libs_tags %}
{% get_site as site %}

is_context_variable

Checks if a given variable name is already part of the template context.

This is useful if you have an expensive templatetag which might or might not have been called in a parent template and you also need it in some child templates.

You cannot just check for {% if variable_name %} because that would equal to False in all cases:

  • if the variable does not exist
  • if the variable exists but is None
  • if the variable exists but is False
  • if the variable exists but is 0

This tag allows you to do something like this:

{% is_context_variable 'variable_name' as variable_exists %}
{% if not variable_exists %}
    {% expensive_templatetag as variable_name %}
{% endif %}
{{ variable_name }}

load_context

load_context allows you to load any python module and add all it’s attributes to the current template’s context. This is very useful for the RapidPrototypingView, for example. You would be able to create the template without having any view providing a useful context (because the view might not exist, yet). But as a template designer you might already know that the view will definitely return a list of objects and that list will be called objects and each object will have a name attribute.

Here is how you would use it:

  • create a file yourproject/context/__init__.py
  • create a file yourproject/context/home.py. A good convention would be to name these context modules just like you would name your templates.

Now create the context that you would like to use in your home.html template:

# in object_list.py:
objects = [
    {'name': 'Object 1', },
    {'name': 'Object 2', },
]

Now create your template:

# in home.html
{% load libs_tags %}
{% load_context "myproject.context.home" %}

{% for object in objects %}
    <h1>{{ object.name }}</h1>
{% endfor %}

This should allow your designers to create templates long before the developers have finished the views.

render_analytics_code

The same as render_analytics_code but uses the new syntax and always uses anonymize IP.

Usage:

{% load libs_tags %}
...
<head>
...
{% render_analytics_code %}
</head>

save

save allows you to save any variable to the context. This can be useful when you have a template where different sections are rendered depending on complex conditions. If you want to render <hr /> tags between those sections, it can be quite difficult to figure out when to render the divider and when not.

Usage:

{% load libs_tags %}
...
{% if complex_condition1 %}
    // Render block 1
    {% save "NEEDS_HR" 1 %}
{% endif %}

{% if complex_condition2 %}
    {% if NEEDS_HR %}
        <hr />
        {% save "NEEDS_HR" 0 %}
    {% endif %}
    // Render block 2
    {% save "NEEDS_HR" 1 %}
{% endif %}

sum

Adds the given value to the total value currently held in key.

Use the multiplier if you want to turn a positive value into a negative and actually substract from the current total sum.

Usage:

{% sum "MY_TOTAL" 42 -1 %}
{{ MY_TOTAL }}

set_context

NOTE: It turns out that this implementation only saves to the current template’s context. If you use this in a sub-template, it will not be available in the parent template. Use our save tag for manipulating the global RequestContext.

set_context allows you to put any variable into the context. This can be useful when you are creating prototype templates where you don’t have the full template context, yet but you already know that certain variables will be available later:

{% load libs_tags %}
{% set_context '/dummy-url/' as contact_url %}
{% blocktrans with contact_url=contact_url %}
Please don't hesitate to <a href="{{ contact_url }}">contact us</a>.
{% endblocktrans %}

verbatim

verbatim is a tag to render x-tmpl templates in Django templates without losing the code structure.

Usage:

{% load libs_tags %}
{% verbatim %}
{% if test1 %}
    {% test1 %}
{% endif %}
{{ test2 }}
{% endverbatim %}

The output will be:

{% if test1 %}
    {% test1 %}
{% endif %}
{{ test2 }}

Loaders

This module provides a few simple utility functions for loading classes from strings like myproject.models.FooBar.

load_member_from_setting

Use this function to load a member from a setting:

# in your settings.py:
FOOBAR_CLASS = 'myproject.models.FooBar'

# anywhere in your code:
from django_libs.loaders import load_member_from_setting
cls = load_member_form_setting('FOOBAR_CLASS')

If you are using the reusable app settings pattern, you can hand in an optional parameter which should be the app_settings module where you define your app’s setting’s default values:

# in your app_settings.py:
from django.conf import settings

FOOBAR_CLASS = getattr(settings, 'APPNAME_FOOBAR_CLASS', 'appname.models.FooBar')

# anywhere in your code:
from appname import app_settings
from django_libs.loaders import load_member_from_setting

cls = load_member_from_setting('FOOBAR_CLASS', app_settings)

load_member

This function is used by load_member_from_setting internally. Use this if you already have the FQN string of the member you would like to load:

# anywhere in your code:
from django_libs.loaders import load_member

cls = load_member('myproject.models.FooBar')

split_fqn

This function is used by load_member internally. Use this if you want to get the left and right side of a fully qualified name:

# anywhere in your code:
from django_libs.loaders import split_fqn

modulename, classname = split_fqn('myproject.models.FooBar')

In this example, modulename would be myproject.models and classname would be FooBar.

If you need it more dynamically, you can also pass in a function that returns a fqn string:

# anywhere in your code
from django_libs.loaders import split_fqn

def function_that_returns_fqn_string():
    return 'myproject.models.FooBar'

modulename, classname = split_fqn(function_that_returns_fqn_string)

Management Commands

cleanup_mailer_messagelog

If you want to delete old message logs of the mailer app simple use:

./manage.py cleanup_mailer_messagelog

You can also add the command to your cronjobs:

0 4 * * 4 $HOME/bin/django-cleanup-mailer-messagelog.sh > $HOME/mylogs/cron/django-cleanup-mailer-messagelog.log 2>&1

Logs younger than 122 days (~4 months) will be ignored, logs older than 122 days will be deleted.

Middlewares

AjaxRedirectMiddleware

When calling a view from an AJAX call and when that view returns a redirect, jQuery changes the status code to 200. This means, in your success callback you will not be able to determine, if the view returned to 200 or a redirect.

Interestingly, there is a workaround: If we return some made up status code, jQuery will not change it.

This middleware makes sure that, if there was a redirect and if it was an AJAX call, the return code will be set to 278.

In order to use this middleware, add it to your MIDDLEWARE_CLASSES setting:

MIDDLEWARE_CLASSES = [
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    ...
    'django_libs.middleware.AjaxRedirectMiddleware',
]

In your jQuery script you can now react to redirects:

$.post(url, data, function(data, textStatus, jqXHR) {
    if (jqXHR.status == 278) {
        window.location.href = jqXHR.getResponseHeader("Location");
    } else {
        $("#" + container).replaceWith(data);
    }
});

When you are using this middleware, it means that Redirects will no longer be executed on the server and your AJAX function has to call the redirect URL manually. If you really want to get the HTML that the last view in the redirect chain would return, you can disable this middleware for some requests by adding ajax_redirect_passthrough parameter to your data payload. When this parameter is given, the middleware will be skipped:

<form method="post" action=".">
    <input type="hidden" name="ajax_redirect_passthrough" value="1" />
    ...
</form>

CustomBrokenLinkEmailsMiddleware

Use this instead of the default BrokenLinkEmailsMiddleware in order to see the current user in the email body. Use this with Django 1.6+.

ErrorMiddleware

Add this middleware if you would like to see the user’s email address in the traceback that is sent to you when a 500 error happens.

In order to use this middleware, add it to your MIDDLEWARE_CLASSES setting:

MIDDLEWARE_CLASSES = [
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    ...
    'django_libs.middleware.ErrorMiddleware',
]

SSLMiddleware

Add this middleware as the first middleware in your stack to forward all requests to http://yoursite.com to https://yoursite.com. Define exceptions via the setting NO_SSL_URLS - these requests will be served without HTTPS.

Models Mixins

TranslationModelMixin

hvad’s safe_translation_getter doesn’t care about untranslated objects, so we built this mixin to add some falllbacks

You can use this by inheriting the class:

from django.db import models

from hvad.models import TranslatableModel, TranslatedFields
from django_libs.models_mixins import TranslationModelMixin


class HvadModel(TranslationModelMixin, TranslatableModel):
    translations = TranslatedFields(
        title=models.CharField(
            verbose_name=_('Title'),
            max_length=256,
        ),
    )

This mixin will automatically return the title field if its __str__ function is called and it will always return a title string (no pk fallback or anything like that needed). If there’s no translation available in the current language it searches for others.

Storage support

Amazon S3

If you want to store your media files in an Amazon S3 bucket we provide some helpful files for you.

First of all, setup your Amazon stuff. This article will help you out:

Then install django-storages (http://django-storages.readthedocs.org/) and boto (https://github.com/boto/boto). Add the following code to your local_settings.py:

USE_S3 = False
AWS_ACCESS_KEY = 'XXXX'
AWS_SECRET_ACCESS_KEY = 'XXXX'
AWS_STORAGE_BUCKET_NAME = 'bucketname'
AWS_QUERYSTRING_AUTH = False
S3_URL = 'https://%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME

if USE_S3:
    DEFAULT_FILE_STORAGE = 'django_libs.s3.MediaRootS3BotoStorage'
    THUMBNAIL_DEFAULT_STORAGE = DEFAULT_FILE_STORAGE
    MEDIA_URL = S3_URL + '/media/'

    # Add this line, if you're using ``django-compressor``
    COMPRESS_STORAGE = 'django_libs.s3.CompressorS3BotoStorage'

MEDIA_ROOT = os.path.join(PROJECT_ROOT, '../..',  'media')
STATIC_ROOT = os.path.join(PROJECT_ROOT, '../..', 'static')

Test the upload. If you get a NoAuthHandlerFound exception, add the following lines to $HOME/.boto:

[Credentials]
aws_access_key_id = XXXX
aws_secret_access_key = XXXX

If you’re using django-compressor add the following settings:

COMPRESS_PARSER = 'compressor.parser.HtmlParser'
COMPRESS_CSS_FILTERS = [
    'django_libs.compress_filters.S3CssAbsoluteFilter',
]
COMPRESS_ENABLED = True

Make sure to run ./manage.py compress --force on every deployment. Also check:

http://martinbrochhaus.com/compressor.html

Test Email Backend

EmailBackend

EmailBackend is a simple Email backend, that sends all emails to a defined address, no matter what the recipient really is.

This is useful in times of development & testing to prevent mass mails to example.com or existing addresses and to review all email communication.

In order to use it, set this in your local_settings.py:

EMAIL_BACKEND = 'django_libs.test_email_backend.EmailBackend'
TEST_EMAIL_BACKEND_RECIPIENTS = (
    ('Name', 'email@gmail.com'),
)

If you’re using django-mailer don’t forget to add:

MAILER_EMAIL_BACKEND = 'django_libs.test_email_backend.EmailBackend'

WhitelistEmailBackend

WhitelistEmailBackend provides more control over what can be sent where.

To use it, first define the EMAIL_BACKEND_WHITELIST setting::

EMAIL_BACKEND_WHITELIST = [r'.*@example.com']

This setting holds regex patterns which define, which emails may be sent and which are being discarded. The above example will allow every email adress from the example.com domain to be delivered.

If you still want to receive all the discarded emails, you can additionally define TEST_EMAIL_BACKEND_RECIPIENTS like above and set EMAIL_BACKEND_REROUTE_BLACKLIST to True:

EMAIL_BACKEND_REROUTE_BLACKLIST = True
TEST_EMAIL_BACKEND_RECIPIENTS = (
    ('Name', 'email@gmail.com'),
)

With this setup, all recipients, that match one of the whitelisted email patterns will be sent to the correct recipient, but in case it didn’t match, the recipients will be replaced with the ones from the TEST_EMAIL_BACKEND_RECIPIENTS setting.

Test Mixins

ViewRequestFactoryTestMixin

In order to use the ViewRequestFactoryTestMixin you need to import it and add a few methods on your test case. A typical test case looks like this:

from django.test import TestCase

from django_libs.tests.mixins import ViewRequestFactoryTestMixin
from mixer.backend.django import mixer

from .. import views


class InvoiceDetailViewTestCase(ViewTestMixin, TestCase):
    """Tests for the ``InvoiceDetailView`` generic class based view."""
    view_class = views.InvoiceDetailView

    def setUp(self):
        self.invoice = mixer.blend('invoices.Invoice')
        self.user = self.invoice.user

    def get_view_kwargs(self):
        return {'pk': self.invoice.pk}

    def test_view(self):
        self.is_not_callable()  # anonymous
        self.is_callable(user=self.user)
        self.is_postable(user=self.user, data={'amount': 1},
                         to_url_name='invoice_list')
        self.is_postable(user=self.user, data={'amount': 1}, ajax=True)

Have a look at the docstrings in the code for further explanations: https://github.com/bitmazk/django-libs/blob/master/django_libs/tests/mixins.py

ViewTestMixin

In order to use the ViewTestMixin you need to import it and implement a few methods on your test case. A typical test case looks like this:

from django.test import TestCase
from django_libs.tests.mixins import ViewTestMixin
from your_invoice_app.tests.factories import InvoiceFactory

class InvoiceDetailViewTestCase(ViewTestMixin, TestCase):
    """Tests for the ``InvoiceDetailView`` generic class based view."""
    def setUp(self):
        self.invoice = InvoiceFactory()
        self.user = self.invoice.user

    def get_view_name(self):
        return 'invoice_detail'

    def get_view_kwargs(self):
        return {'pk': self.invoice.pk}

    def test_view(self):
        self.should_redirect_to_login_when_anonymous()
        self.should_be_callable_when_authenticated(self.user)
        # your own tests here

For a slightly longer explanation on why the test looks like this, please read on...

Tutorial

It is a good idea to write a test that calls your view before you actually write the view. And when you are at it, you might just as well test if a view that is protected by login_required, actually does require the user to be logged in. Walking down that road, you might also just as well try to call the view and manipulate the URL so that this user tries to access another user’s objects. And so on, and so forth...

Fact is: You will be calling self.client.get and self.client.post a lot in your integration tests (don’t confuse these tests with your unit tests).

Let’s assume that you have defined your urls.py like this:

...
url(r'^invoice/(?P<pk>\d+)/', InvoiceDetailView.as_view(), name='invoice_detail'),
...

In order to test such a view, you would create an integration_tests/views_tests.py file and create a test case for this view:

from django.test import TestCase

class InvoiceDetailViewTestCase(TestCase):
    def test_view(self):
        resp = self.client.get('/invoice/1/')

Writing the test this way is flawed because if you ever change that URL your test will fail. It would be much better to use the view name instead:

from django.core.urlresolvers import reverse
...
class InvoiceDetailViewTestCase(TestCase):
    def test_view(self):
        resp = self.client.get(reverse('invoice_detail'))

If your view is just slightly complex, you will have to call self.client.get several times and it is probably not a good idea to repeat the string invoice_detail over and over again, because that might change as well. So let’s centralize the view name:

class InvoiceDetailViewTestCase(TestCase):
    def get_view_name(self):
        return 'invoice_detail'

    def test_view(self):
        resp = self.client.get(reverse(self.get_view_name()))

The code above was simplified. The reverse calls would fail because the view actually needs some kwargs. A proper call would look like this:

invoice = InvoiceFactory()
resp = self.client.get(reverse(self.get_view_name(), kwargs={
    'pk': invoice.pk}))

This can get annoying when you need to call the view many times because most of the time you might call the view with the same kwargs. So let’s centralize the kwargs as well:

class InvoiceDetailViewTestCase(TestCase):
    def setUp(self):
        self.invoice = InvoiceFactory()

    def get_view_name(self):
        ...

    def get_view_kwargs(self):
        return {'pk': self.invoice.pk}

    def test_view(self):
        resp = self.client.get(reverse(self.get_view_name(),
            self.get_view_kwargs()))

This is much better. Someone who looks at your test, can easily identify the view name and the expected view kwargs that are needed to get a positive response from the view. When writing tests you don’t have to think about the view name or about constructing the view kwargs any more, which will speed up your workflow.

But this is still an awful lot of code to type. Which is why we created the ViewTestMixin:

class InvoiceDetailViewTestCase(ViewTestMixin, TestCase):
    def setUp(self):
        ...

    def get_view_name(self):
        ...

    def get_view_kwargs(self):
        ...

    def test_view(self):
        resp = self.client.get(self.get_url())

Now we have got it down to a one-liner to call self.client.get in a future proof and maintainable way. After writing a few hundred tests with this approach new patterns emerge. You will want to test almost all views if they are accessible by anonymous or the opposite: If they are not accessible by anonymous but by a logged in user.

For this reason the ViewTestMixin provides a few convenience methods:

class InvoiceDetailViewTestCase(ViewTestMixin, TestCase):
    ...
    def test_view(self):
        user = UserFactory()
        self.should_redirect_to_login_view_when_anonymous()
        self.should_be_callable_when_authenticated(user)

If your view expectes some data payload (either POST or GET data), then you can set self.data_payload in your test. If all your tests need the same data, you can override the get_data_payload() method:

class InvoiceDetailViewTestCase(ViewTestMixin, TestCase):
    ...
    def get_data_payload(self):
        # If you stick to this implementation, you can still change the
        # data payload for ``some`` of your tests.
        if hasattr(self, 'data_payload'):
            return self.data_payload
        return {'foo': 'bar', }

    def test_view(self):
        user = UserFactory()
        self.should_redirect_to_login_view_when_anonymous()

        # Now your view will be called with the given data payload
        self.should_be_callable_when_authenticated(user)

        self.data_payload = {'foobar': 'barfoo'}
        # Now you have changed the standard payload to be returned by
        # ``get_data_payload``
        self.should_be_callable_when_authenticated(user)

“is_callable” and “is_not_callable”

If a view becomes more complex, you might end up with rather many assertions for many different situations. If you take all these cases into account when testing, which you probably should, you will write a lot of:

def test_view(self):
    # case 1
    resp = self.client.get(self.get_url())
    self.assertEqual(resp.status_code, 200, msg=(
        'If this then that, because foo is bar.'))
    # case 2
    resp = ...
    self.assertEqual(...)
    # case 3
    ...

is_callable and is_not_callable let you quickly assign different values to customize your actual assertion case in one method call. is_callable by default makes an assertion on status code 200. is_not_callable defaults to an assertion on status code 404.

Warning

Note if you used previous versions, that is_callable will only default to 200 in the future. It’s best to use and_redirects_to for a redirect assertion or if you only want to make sure to get the right code set status_code to 302.

Also the code parameter changed into status_code.

They can still be used, but you will get annoying warnings. So, you might as well change it right away.

Argument Definition
method String that defines if either ‘post’ or ‘get’ is used.
data dictionary with GET data payload or POST data. If not provided it calls self.get_data_payload() instead.
kwargs dictionary to overwrite view kwargs. If not provided, it calls self.get_view_kwargs() instead.
user Assign a user instance to log this user in first. As in self.should_be_callable_when_authenticated() the password is expected to be ‘test123’.
anonymous If this is assigned True, the user is logged out before the assertion. So basically you test with an anonymous user. Default is False.
and_redirects_to If set, it performs an assertRedirects assertion. Note that, of course this will overwrite the status_code to 302.
status_code If set, it overrides the status code, the assertion is made with.
ajax If True it will automatically set HTTP_X_REQUESTED_WITH='XMLHttpRequest' to simulate an ajax call. Defaults to False.

You can also define no arguments to test according to your current situation. Then still, it is a handy shortcut.

Further methods are:

  • should_be_callable_when_anonymous
  • should_be_callable_when_has_correct_permissions

Have a look at the docstrings in the code for further explanations: https://github.com/bitmazk/django-libs/blob/master/django_libs/tests/mixins.py

Utils

Converter

html_to_plain_text

Converts html code into formatted plain text.

Use it to e.g. provide an additional plain text email.

Just feed it with some html:

from django_libs.utils import html_to_plain_text

html = (
    """
    <html>
            <head></head>
            <body>
                <ul>
                    <li>List element</li>
                    <li>List element</li>
                    <li>List element</li>
                </ul>
            </body>
        </html>
    """
)
plain_text = html_to_plain_text(html)

This will result in:

* List element
* List element
* List element

You can also feed the parser with a file:

from django_libs.utils import html_to_plain_text

with open('test_app/templates/html_email.html', 'rb') as file:
    plain_text = html_to_plain_text(file)

You can customize this parser by overriding its settings:

HTML2PLAINTEXT_IGNORED_ELEMENTS

Default: [‘html’, ‘head’, ‘style’, ‘meta’, ‘title’, ‘img’]

Put any tags in here, which should be ignored in the converting process.

HTML2PLAINTEXT_NEWLINE_BEFORE_ELEMENTS

Default: [‘h1’, ‘h2’, ‘h3’, ‘h4’, ‘h5’, ‘h6’, ‘div’, ‘p’, ‘li’]

Put any tags in here, which should get a linebreak in front of their content.

HTML2PLAINTEXT_NEWLINE_AFTER_ELEMENTS

Default: [‘h1’, ‘h2’, ‘h3’, ‘h4’, ‘h5’, ‘h6’, ‘div’, ‘p’, ‘td’]

Put any tags in here, which should get a linebreak at the end of their content.

HTML2PLAINTEXT_STROKE_BEFORE_ELEMENTS

Default: [‘tr’]

Put any tags in here, which should get a stroke in front of their content.

HTML2PLAINTEXT_STROKE_AFTER_ELEMENTS

Default: [‘tr’]

Put any tags in here, which should get a stroke at the end of their content.

HTML2PLAINTEXT_STROKE_TEXT

Default: ‘——————————n’

You can override the appearance of a stroke.

Decorators

Allows you to decorate a function based on a condition.

This can be useful if you want to require login for a view only if a certain setting is set:

from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator

from django_libs.utils import conditional_decorator


class MyView(ListView):
    @conditional_decorator(method_decorator(login_required),
                           settings.LOGIN_REQUIRED)
    def dispatch(self, request, *args, **kwargs):
        return super(MyView, self).dispatch(request, *args, **kwargs)

Email

send_email

send_email sends html emails based on templates for subject and body.

Please note that protocol and domain variables have already been placed in the context. You are able to easily build links:

<a href="{{ protocol }}{{ domain }}{% url "home" %}">Home</a>

Have a look at the docstrings in the code for further explanations: https://github.com/bitmazk/django-libs/blob/master/django_libs/utils_email.py

In order to use it, include the following code:

send_email(
    request={},
    context={'Foo': bar},
    subject_template='email/notification_subject.html',
    body_template='email/notification_body.html',
    from_email=('Name', 'email@gmail.com'),
    recipients=[self.user.email, ],
    reply_to='foo@example.com',  # optional
)

Log

This logging filter adds the current user’s email to the request’s META dict. This way the user will show up in the traceback that is sent via email by Django’s logging framework.

Add it to your logging settings like so:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        ...
        'add_current_user': {
            '()': 'django_libs.utils_log.AddCurrentUser'
        },
    },
    'handlers': {
        'mail_admins': {
            'level': 'WARNING',
            'filters': [
                ...
                'add_current_user',
            ],
            'class': 'django.utils.log.AdminEmailHandler'
        },
    },
    ...
}

FilterIgnorable404URLs

This logging filter allows you to ignore 404 logging for certain URLs and for certain user agents.

We’ve already prepared some URLs and user agents. You might want to add them to your settings:

from django_libs.settings.django_settings import *  # NOQA

Alternatively, you can extend those lists or write your own.

How to define your own list of ignorable URLs:

IGNORABLE_404_URLS = [
    re.compile(r'\.php/?$', re.I),
    re.compile(r'\'/?$', re.I),
    re.compile(r'^/assets/'),
    ...
]

How to define your list of ignorable user agents:

IGNORABLE_404_USER_AGENTS = [
     re.compile(r'FacebookBot', re.I),
     re.compile(r'Googlebot', re.I),
     re.compile(r'Mail.RU_Bot', re.I),
     re.compile(r'Twitterbot', re.I),
     re.compile(r'bingbot', re.I),
     ...
 ]

Then add the logging filter to your logging settings:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        ...
        'filter_ignorable_404_urls': {
            '()': 'django_libs.utils_log.FilterIgnorable404URLs'
        },
    },
    'handlers': {
        'mail_admins': {
            'level': 'WARNING',
            'filters': [
                ...
                'filter_ignorable_404_urls',
            ],
            'class': 'django.utils.log.AdminEmailHandler'
        },
    },
    ...
}

NOTE: Make sure to set the log level for the mail_admins handler and for the loggers that use this handler to WARNING, otherwise 404 emails will not be sent.

Text

Returns a random string. By default it returns 7 unambiguous capital letters and numbers, without any repetitions:

from django_libs.utils import create_random_string

result = create_random_string()

Will return something like CH178AS. You can set a length, characters to use and you can allow repetitions:

result = create_random_string(length=3, chars='abc123', repetitions=True)

Views

Http404TestView & Http500TestView

Warning: These views are deprecated. Use the RapidPrototypingView instead.

Simple template views that use the 404.html and 500.html template. Just create this template in your project’s templates` folder and add the views to your urls.py:

from django_libs.views import Http404TestView, Http500TestView
urlpatterns += patterns(
    '',
    url(r'^404/$', Http404TestView.as_view()),
    url(r'^500/$', Http500TestView.as_view()),
    ...
)

HybridView

You often need to display a different home page for authenticated users. For example Facebook shows a login page when you visit their site but when you are logged in it shows your stream under the same URL.

This HybridView does the same thing. Here is how you use it in your urls.py:

from django_libs.views import HybridView
from myapp.views import View1
from myapp2.views import func_based_view

authed_view = View1.as_view(template_name='foo.html')
anonymous_view = func_based_view
anonymous_view_kwargs = {'template_name': 'bar.html', }

urlpatterns += patterns(
    '',
    ...
    url(r'^$',
        HybridView.as_view(
            authed_view=authed_view, anonymous_view=anonymous_view,
            anonymous_view_kwargs=anonymous_view_kwargs
        ),
    name='home',
)

PaginatedCommentAJAXView

Provides a simple solution to display comments from the Django comment framework for any object. It’s paginated and because it uses ajax, there’s no need to reload the page every time you want to change a page.

Hook up the view in your urls::

from django_libs.views import PaginatedCommentAJAXView

urlpatterns += patterns(
    '',
    ...
    url(r'^comments/$', PaginatedCommentAJAXView.as_view(),
        name='libs_comment_ajax'),
)

Add the comment scripts. E.g. in your base.html do::

{% load static %}

<script type="text/javascript" src="{% static "django_libs/js/comments.js" %}"></script>

Add the markup to the template, that contains the object, you want to display comments for::

<div data-id="ajaxComments" data-ctype="mymodel" data-object-pk="{{ object.pk }}" data-comments-url="{% url "libs_comment_ajax" %}"></div>
  • data-id=ajaxComments indicates to the scripts, that inside this div is where to render the comment list template.
  • data-ctype is the content type name of the object. E.g. ‘user’ for auth.User.
  • data-object-pk is most obiously the object’s primary key.
  • data-comments-url is the url you’ve hooked up the view.

To customize the template take a look at django_libs/templates/django_libs/partials/ajax_comments.html.

Also you can choose the amount of comments per page via the setting COMMENTS_PAGINATE_BY::

COMMENTS_PAGINATE_BY = 10  # default

There you go. All done.

RapidPrototypingView

This view allows you to render any template even when there is no URL hooked up and no view implemented. This allows your designers to quickly start writing HTML templates even before your developers have created views for those templates.

In order to use this view, hook it up in your urls.py:

from django_libs.views import RapidPrototypingView
urlpatterns += patterns(
    '',
    url(r'^prototype/(?P<template_path>.*)$',
        RapidPrototypingView.as_view(),
        name='prototype')
    ...
)

Now you can call any template by adding it’s path to the URL of the view:

localhost:8000/prototype/404.html
localhost:8000/prototype/cms/partials/main_menu.html

Check out the load_context templatetag which allos you to create fake context variables for your template.

UpdateSessionAJAXView

This view allows you to update any session variables in an AJAX post.

In order to use this view, hook it up in your urls.py:

from django_libs.views import UpdateSessionAJAXView
urlpatterns += patterns(
    '',
    url(r'^update-session/$', UpdateSessionAJAXView.as_view(),
        name='update_session'),
    ...
)

Now you can call it by using session_name and session_value:

<script src="{% static "django_libs/js/getcookie.js" %}"></script>
<script>
    var data = [
        {name: 'csrfmiddlewaretoken', value: getCookie('csrftoken')}
        ,{name: 'session_name', value: 'foo'}
        ,{name: 'session_value', value: 'bar'}
    ];
    $.post(
        '/update-session/'
        ,data
    );
</script>

Views Mixins

AccessMixin

Use this mixin if you want to allow users of your app to decide if the views of your app should be accessible to anonymous users or only to authenticated users:

form django_libs.views_mixins import AccessMixin

class YourView(AccessMixin, TemplateView):
    access_mixin_setting_name = 'YOURAPP_ALLOW_ANONYMOUS'

    # your view code here

Given the above example, users of your app would have to set YOURAPP_ALLOW_ANONYMOUS to True or False.

AjaxResponseMixin

Use this with views that can be called normally or from an AJAX call. Usually, when you call a view normally, you will have {% extends "base.html" %} at the beginning of the view’s template. However, when you call the same view from an AJAX call you just want to update a partial region of your page, therefore the view needs to return that partial template only.

You can use this by inheriting the class:

from django_libs.views_mixins import AjaxResponseMixin

class MyView(AjaxResponseMixin, CreateView):
    ajax_template_prefix = 'partials/ajax_'

The attribute ajax_template_prefix defaults to ajax_. If you would like to store your app’s ajax templates in a different way, for example in a subfolder called partials, you can override that attribute in your class.

DetailViewWithPostAction

This view enhances the class-based generic detail view with even more generic post actions. In order to use it, import it like all the other generic class based views and view mixins.

  • Create a Mixin or View which inherits from this action mixin.
  • Be sure to add a general get_success_url() function or custom success functions for each post action.
  • Create your post actions
  • Make sure to add this action names to the name attribute of an input field.

Basic usage in a html template:

<form method="post" action=".">
    {% csrf_token %}
    <input name="post_verify" type="submit" value="Verify" />
</form>

Usage in a views.py:

from django_libs.views_mixins import DetailViewWithPostAction

class NewsDetailBase(DetailViewWithPostAction):
    def post_verify(self):
        self.object.is_verified = True
        self.object.save()

    def post_reject(self):
        self.object.is_verified = False
        self.object.save()


class NewsEntryDetailView(NewsDetailBase):
    model = NewsEntry

def get_success_url(self):
    return reverse('newsentry_detail', kwargs={'pk': self.object.pk})

    def post_verify(self):
        super(NewsEntryDetailView, self).post_verify()
        ctx_dict = {
            'verified': True,
            'entry': self.object,
        }
        self.send_mail_to_editor(ctx_dict)

    def get_success_url_post_reject(self):
        return reverse('newsentry_list')

JSONResponseMixin

You can find out more about the JSONResponseMixin in the official Django docs: https://docs.djangoproject.com/en/dev/topics/class-based-views/#more-than-just-html

In order to use it, just import it like all the other generic class based views and view mixins:

from django.views.generic import View
from django_libs.views_mixins import JSONResponseMixin

class MyAPIView(JSONResponseMixin, View):
    pass

Indices and tables