tag (django)

archive

have nginx proxy properly pass ssl/https to apache mod_wsgi url_scheme for django/pyramid/flask etc ... Aug
09
1
3

I've had problems getting mod_wsgi to properly set the url_scheme to https when running under SSL. This causes references to fully-qualified urls in statements like redirects and static file locations resolve incorrectly to http when I am viewing the ssl page through https.

I have an nginx web server hosting static files and proxy apache mod_wsgi for the python applications which includes any combination of django, pyramid, and flask frameworks, although this will also work for any mod_wsgi app running under the nginx/apache stack.

I've seen examples of how to set up the apache config, and other examples of how to set up the nginx config, but I have yet to see someone explain how to set up the two together nor has anyone explained what is actually going on.

I am not a server admin so it's a struggle for me to wrangle things like this. I am by no means an expert on this subject but I am documenting my process on what I have found to work in hopes that it'll help others save some pain and frustration that I went through.

Short Version

Add set_proxy_header to the nginx settings after the proxy_pass statement in the server location directive.

proxy_set_header X-Forwarded-Protocol $scheme;

Add the SetEnvIf statement to set the HTTPS environment variable in the apache settings to pass to the wsgi process.

SetEnvIf X-Forwarded-Protocol https HTTPS=1

Long Version

My nginx site config typically looks similar to:

upstream example_backend {
  server 127.0.0.1:8080;
}

server {
    listen 80;
    server_name example.com;
    rewrite ^ https://$server_name$request_uri? permanent;
}

server {
    listen 443;
    server_name example.com;

    ssl  on;
    ssl_certificate  /path/to/example_combined.crt;
    ssl_certificate_key  /path/to/example.key;

    ssl_session_timeout  5m;

    ssl_protocols  SSLv2 SSLv3 TLSv1;
    ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
    ssl_prefer_server_ciphers   on;

    access_log  /var/log/nginx/example.access.log;
    error_log  /var/log/nginx/example.error.log;

    location / {
        # auth_basic "Restricted";
        # auth_basic_user_file /path/to/htpasswd;

        proxy_pass http://example_backend;
        include /etc/nginx/proxy.conf;

        # set a proxy header to pass tell apache if we're operating under ssl
        proxy_set_header X-Forwarded-Protocol $scheme;

        # you can use this header instead, or anyone you want, but coordinate it with the apache config
        # proxy_set_header X-Forwarded-HTTPS on;
    }

    location ~ ^/(media|static)/ {
        root   /path/to/html/root;
        #index  index.html index.htm;
    }
}

My /etc/nginx/proxy.conf file contains:

proxy_redirect              off;
proxy_set_header            Host $host;
proxy_set_header            X-Real-IP $remote_addr;
proxy_set_header            X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size        10m;
client_body_buffer_size     128k;
proxy_connect_timeout       90;
proxy_send_timeout          90;
proxy_read_timeout          90;
proxy_buffer_size           4k;
proxy_buffers               4 32k;
proxy_busy_buffers_size     64k;
proxy_temp_file_write_size  64k;

The key entry is the proxy header:

proxy_set_header X-Forwarded-Protocol $scheme;

This sets the X-Forwarded-Protocol header to http or https depending on the scheme. Since you're under SSL it should be set to https. An alternative is you can set a pre-defined header instead like:

proxy_set_header X-Forwarded-HTTPS on;

Choose one or the other, not both. You will check the header value in the apache config and set the HTTPS environment variable to pass to the mod_wsgi process which will set the wsgi.url_scheme variable for the wsgi app.

My apache config typically looks similar to:

WSGIPythonHome /path/to/virtualenv
WSGIPythonPath /path/to/virtualenv/lib/python2.7/site-packages

<VirtualHost *:8000>
    ServerAdmin user@example.com

    ServerName example.com
    # ServerAlias www.example.com

    #DocumentRoot /path/to/html

    WSGIDaemonProcess example user=example group=example processes=4 threads=4
    WSGIApplicationGroup %{GLOBAL}
    WSGIProcessGroup example
    WSGIPassAuthorization on
    WSGIScriptAlias / /path/to/production.wsgi

    # check the proxy header passed to us from nginx to set the HTTPS environment variable
    SetEnvIf X-Forwarded-Protocol https HTTPS=1

    # use this alternative if it was set in nginx
    # SetEnvIf X-Forwarded-HTTPS on HTTPS=1

    # use this if you do not want to check the headers with SetEnfIf (not recommended)
    # SetEnv HTTPS 1

    <Directory /path/to/wsgi/folder>
        Order deny,allow
        Allow from all
    </Directory>

    # Possible values include: debug, info, notice, warn, error, crit,
    # alert, emerg.
    LogLevel warn
    ErrorLog /var/log/apache2/example.error.log
    CustomLog /var/log/apache2/example.access.log combined
</VirtualHost>

You need to make sure the setenvif apache mod is enabled (on Ubuntu it is on by default). The key line here is making sure you're setting the HTTPS environment variable:

SetEnvIf X-Forwarded-Protocol https HTTPS=1

If you set X-Forwarded-HTTP on in the nginx as an alternative you should alter the conditional accordingly:

SetEnvIf X-Forwarded-HTTPS on HTTPS=1

Again,you should be using one or the other, not both.

If you want to force HTTPS onto mod-wsgi without checking for the proxy header for testing then you can manually set the variable:

SetEnv HTTPS 1

This is highly not recommended as it's a potential security hole.

comments

change an existing model, auth_user.username, max_length in django Jul
06
1
2

So lets say I had a model an an app that I didn't create that I wanted to change. Well, I would like to avoid going to that app and adjusting the code on the fly. Instead I would rather make the change in my custom application in order to keep the integrity of the code strong.

In my personal case I typically need to increase the max_length of the username in the django.contrib.auth package. You can perform the change by using the class_prepared signal:

# models.py
from django.db.models.signals import class_prepared

def longer_username(sender, *args, **kwargs):
    # you can't just do `if sender == django.contrib.auth.models.User`
    # because you have to import the model
    if sender.__name__ == 'User' and sender.__model__ == 'django.contrib.auth.models.User':
        sender._meta.get_field('username').max_length = 100

class_prepared.connect(longer_username)

You might need to change any help_text and forms.

This code will not update the underlying database. In order to make the update in the database I add a South schema migration.

import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models


class Migration(SchemaMigration):
    def fowards(self, orm):
        db.alter_column('auth_user', 'username', models.CharField(max_length=100))

    def backwards(self, orm):
        db.alter_column('auth_user', 'username', models.CharField(max_length=35))

# copy the rest of the file from the previous migration
# update the value for auth.user / username / maxlength

I don't think I need to say this, but this is hackish and should be used with great caution.

comments

html5 django form fields Jul
06
1
2

If you're ever developing for mobile it's really handy to use the HTML5 form inputs for things like email and url to bring up the contextual keyboard layouts.

An easy way to create the HTML5 input elements is to extend the Input widget input_type.

from django import forms
from django.widgets import Input


class Html5EmailInput(Input):
    input_type = 'email'


class Html5URLInput(Input):
    input_type = 'url'


class CustomForm(forms.Form):
    email = forms.CharField(widget=Html5EmailInput())
    url = forms.CharField(widget=Html5URLInput())

comments

display the value of a django form field in a template Jul
06
1
2

Sometimes you need to get at the raw value of a field in a django form. This is easily done in 1.3 using the following:

{{ form.field_name.value }}

If you're running a version lower than 1.3 you can get at the value with:

{% if form.instance.field_name %}
{{ form.instance.field_name }}
{% else %}
{{ form.data.field_name }}
{% endif %}

comments

accessing the object inside a django admin change_form.html template Jul
06
1
2

When overriding the change_form.html template sometimes I need access to the object that is being edited. The change_view method provides the original object in the 'original' context variable.

You can access the object in the template using the original variable:

{{ original }}

comments

showing a foreign key value in a django admin list display Nov
07
1
1

say you have two models like so:

# models.py

class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.ForeignKey("Author")

class Author(models.Model):
    name = models.CharField(max_length=100)

In this case it would be handy if we could list the author in the list_display of the book admin, but Django gives an error if we list foreign keys like so:

# admin.py

class BookAdmin(admin.ModelAdmin):
    list_display = ('title', 'author__name')

admin.register(Book, BookAdmin)

Luckily we can easily fix this by adjusting how we link into the author foreign key. We just need to add a method to the Book ModelAdmin and return the desired string.

# admin.py

class BookAdmin(admin.ModelAdmin):
    list_display = ('title', 'author_name')

    def author_name(self, instance):
        return instance.author.name

Be careful though as gratuitous additions of foreign key field can greatly increase the load time of the admin page for the list view of the model: use this judiciously.

comments

manually writing to model filefield in django views Oct
11
1
1

Django models.FileField is very helpful in simplifying file management, but sometimes you need to write/overwrite into a file field object in a view without having the admin helper functions available. It's not inherently clear how to do this manually. Never fear though it's still an easy process.

It's worth noting that Django uses a custom File class that acts as a wrapper around the python file object to add some enhanced functionality. You simply need to initialize a Django File with a python file to get a valid model.

Say you have something similar to the following model:

class FileExample(models.Model):
    test = models.FileField(upload_to='uploads')

You can then write something like this in your view:

from django.core.files import File
from test.models import FileExample

file_example = FileExample()

filename = '/path/to/file.txt'
f = open(filename, 'r')
djangofile = File(f)
file_example.test.save('filename.txt', djangofile)
f.close()

Furthermore you can combine some statements to simplify and shorten the process:

file_example = FileExample()
file_example.test.save('filename.txt', File(open('/path/to/file.txt', 'r')))

comments

uploading multiple files with the same input name in django Sep
20
1
1

So sometimes I have an input field in a form that I want to upload files to but I want to upload multiple files at a time. I'll usually just use javascript to add and remove the field dynamically, and to keep things simple I'll keep the input name the same. I'll eventually end up with markup that looks similar to the following:

<input type="file" name="file" />
<input type="file" name="file" />
<input type="file" name="file" />

Django attaches the uploads in the FILE dictionary variable on the request object which you can access like so:

for uploaded_file in request.FILES['file']:
    # process the file

which could look something like:

# views.py
from django.http import HttpResponseRedirect

def upload_file(request):
    for uploaded_file in request.FILES.getlist('file'):
        handle_uploaded_file(uploaded_file)

    return HttpResponseRedirect(...)


def handle_uploaded_file(f):
    destination = open('/upload/path/%s' % f.name, 'wb+')

    for chunk in f.chunks():
destination.write(chunk) destination.close()

comments

extract or return only the model instance of a foreign key or one to one field from a queryset in django Sep
14
1
1

Lets say I have two models connected by a OneToOne field like so:

class Post(models.Model):
    ...


class MagicalPost(models.Model):
    post = models.OneToOneField('Post')
    pony = models.TextField(max_length=100, help_text='This is where the MAGIC happens!')

I want to run a query over all MagicalPost objects but I only want to receive the Post instances.

I can use the select_related() option which will only create one database call:

magical_posts = MagicalPost.objects.select_related('post')
posts = []

for magical_post in magical_posts:
    posts.append(magical_post.post)

return posts

Or I could use two queries which syntactically is simpler:

posts_ids = MagicalPost.objects.values_list('post', flat=True)
posts = Post.objects.filter(id__in=posts_ids)
return posts

comments

change the default formating of the DateField in django forms Feb
23
1
1

Django forms are great, but sometimes you need to do a little work to get things just the way you want. The DateField has an easy way to restrict how data is entered through the input_formats option; but unless if I'm missing something there's isn't a similar way to help you set how the field displays the date in the template.

The default way the forms.DateField is displayed is YYYY-MM-DD, which is typically not what you want to present to the users.

The easiest way I have found to change how the date field displays is to write a small custom form field that inherits from forms.DateField that overrides the widget.

It would look something like this:

from django import forms
from django.utils.translation import ugettext_lazy as _


class FormattedDateField(forms.DateField):
    widget = forms.DateInput(format='%m/%d/%Y')

    def __init__(self, *args, **kwargs):
        super(FormattedDateField, self).__init__(*args, **kwargs)
        self.input_formats = ('%m/%d/%Y',)


class ProfileForm(forms.Form):
    email = forms.EmailField()
    first_name = forms.CharField(max_length=100)
    last_name = forms.CharField(max_length=100)
    birthday = FormattedDateField()

Or for a short-hand version that doesn't require overriding the form class could look something like the following:

class ProfileForm(forms.Form):
    email = forms.EmailField()
    first_name = forms.CharField(max_length=100)
    last_name = forms.CharField(max_length=100)
    birthday = forms.DateField(widget=forms.DateInput(format='%m/%d/%Y')), input_formats=('%m/%d/%Y',))

Now, when I initialize the birthday field in the form instead of displaying YYYY-MM-DD, it will look like MM/DD/YYYY which is more natural to an American audience.

comments

custom user profile and extending the user admin in django Dec
08
1
0

In almost every project I do these days I need to extend and add fields to the default Django User model to store things like profiles, preferences, history etc.... One way to do this is to create a new model that inherits off of the django User class and use that class for authentication. This is usually not the best idea. In my case it got complicated integrating this change back into third party applications even when setting the AUTH_PROFILE_MODULE and writing a custom authentication backend.

The method I have been using lately works off a custom Model that links back to the User through a OneToOneField: based off the docs this is the recommended way to extend the django user. I find this method much cleaner to work with, and ultimately more compatible and maintainable. For instance I can work in my additions to the User in parallel with other applications. This is not possible if we are all inheriting off the User.

One of the main arguments that I hear against using a OneToOneField over Inheritance is that it creates extra database calls. This is not true. Perhaps a little known fact is that django implements model inheritance through a one-to-one relation: accessing fields from the subclass will do a query and accessing fields from the parent will do another query, so you're doing the same amount of queries either way. It's been mentioned that sub-classing models is not the right solution in most cases.

The following technique is not specific to extending just the User. This is a way to add additional attributes to an existing model written anywhere else in the project without having to modify the original source code. You can then make the additions show up in the admin by registering a custom extended ModelAdmin. This is essentially the same way I add additional properties to Satchmo products for e-commerce shops, although there are additional steps I need to take that are specific to Satchmo.

For this example lets do something simple, lets say something like adding a user profile for a blog that gives me the ability to create an avatar and a biography description for each user.

I'll first start out by defining a custom profile model class that'll attach the desired fields to the django user model.

I want to create this model whenever a user is created so I'll also add a profile creation method to the post_save signal for the User model.

# models.py
from django.db import models
from django.db.models.signals import post_save
from django.contrib.auth.models import User


class UserProfile(models.Model):
    user = models.OneToOneField(User)
    avatar = models.ImageField(upload_to='uploads/avatars', default='', blank=True)
    biography = models.TextField(default='', blank=True)

    def __unicode__(self):
        return self.user.username


def create_user_profile(sender, instance, created, **kwargs):
    """Create the UserProfile when a new User is saved"""
    if created:
        profile = UserProfile()
        profile.user = instance
        profile.save()

post_save.connect(create_user_profile, sender=User)

This is a great start. The above code will provide me the ability to add additional fields to the django user with minimal intervention. This might be all I actually need, but I like to push things further; and in this case it's a good idea to make the user profile available in the admin.

Although I could have the admin autodiscover the model like usual, I find it much more efficient to inline the UserProfile into the existing User admin view. To do this I create an inline for the new profile and add it to a custom UserAdmin class that inherits off of the django UserAdmin class.

The process of replacing an existing registered model admin is easy, you just need to remember to unregister the model first. This gives me the ability to adjust the default django user admin view without having to modify any core code.

# admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin

from django.contrib.auth.models import User
from models import UserProfile


class ProfileInline(admin.StackedInline):
    model = UserProfile
    fk_name = 'user'
    max_num = 1


class CustomUserAdmin(UserAdmin):
    inlines = [ProfileInline,]


admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)

An optional step is to can add AUTH_PROFILE_MODULE in settings.py. I tend NOT to do this because other applications that I integrate, like Satchmo, already have this field set and I don't want step on anyone's toes.

Lets say I put models.py and admin.py in an application named 'custom'.

# settings.py
AUTH_PROFILE_MODULE = 'custom.UserProfile'

This setting allows me to pull the profile off of the django user with the get_profile() method.

profile = user.get_profile()

It's handy but not necessary. You can always get the profile the old-fashioned way:

profile = user.userprofile

I hope this explanation helps. I'm jotting it down mainly so I can refer back to it later as I tend to forget where things are located and what I need to include. There could be a better way to do things, but this has worked for me and hopefully it'll be helpful for someone else. As always feedback and comments are welcome.

Happy coding!

comments

restricting views to staff users in django Sep
02
1
0

Hopefully you've already found the login_required decorator for Django views, which make it incredibly simple to integrate authentication into the site.

One thing that's not documented well is there's another decorator @staff_member_required. This works the same way @login_required does except it restricts access to users who are both authenticated and has staff status set in their settings.

This comes in handy when extending the admin or writing custom moderation applications.

You can do something similar to:

from django.shortcuts import render_to_response
from django.template.context import RequestContext
from django.contrib.admin.views.decorators import staff_member_required


@staff_member_required
def view(request):

    ...

    return render_to_response('template.html', context_instance=RequestContext(request))

comments

dynamically create an xml file in django Jun
29
1
0

Sometimes when communicating to external systems like Flash I am required to dynamically create an XML file. You can utilise the template framework to do the heavy lifting.

It's worth noting that if you need to create syndication feeds you should use the syndication framework, and if you want to create a site map you should use the sitemap framework.

With that being said, lets say you have a model that looks something like this (I'll use a blog post concept for the demo):

# models.py

class Post(models.Model):
    user = models.ForeignKey(User, blank=True, null=True)
    publish = models.BooleanField(default=True)
    title = models.CharField(max_length=255)
    slug = models.SlugField(unique=True)
    edited = models.CharField(max_length=255, blank=True)
    tags = models.ManyToManyField('Tag')
    body = models.TextField()
    date_created = models.DateTimeField(auto_now_add=True)
    date_modified = models.DateTimeField(auto_now=True)

    def __unicode__(self):
        return self.title

    @models.permalink
    def get_absolute_url(self):
        post_date = self.date_created
        return_vars = {
            'year': post_date.year,
            'month': post_date.month,
            'day': post_date.day,
            'slug': self.slug,
        }

        return('blog_post', (), return_vars)

    ....

    @classmethod
    def get_posts(self, limit=None):
        if limit:
            return Post.objects.filter(publish=True).order_by('-date_created')[0:limit]
        else:
            return Post.objects.filter(publish=True).order_by('-date_created')

You can link a reference to an xml file in urls.py:

# urls.py

urlpatterns = patterns('project.quotes.views',
    ....
    url(r'^(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<slug>[\w-]+)/, 'post', name='blog_post'),
    url(r'^xml/latest.xml, 'xml_latest', name='blog_xml_latest'),
    ....
)

You can load and render the template which is in the xml format, and return it with mimetype="text/xml". Your view can be something like:

# views.py

template_vars = {...}

def xml_latest(request):
    """
    returns an XML of the most latest posts
    """
    template_vars['posts'] = Post.get_posts(30)
    template_vars['site'] = Site.objects.get_current()

    t = loader.get_template('blog/xml/posts.xml')
    c = Context(template_vars)

    return HttpResponse(t.render(c), mimetype="text/xml")

and your template can be something like:

# posts.xml

<?xml version="1.0" encoding="UTF-8"?>
<posts>
  {% for post in posts %}
  <post>
    <date>{{ post.date_created|date:"M j, Y" }}</date>
    {{ if post.edited }}
    <edited>{{ post.edited|date:"M j, Y" }}</edited>
    {{ endif }}
    <title>{{ post.title }}</title>
    <body>{{ post.body|striptags }}</body>
    <tags>
        {% for tag in post.tags %}
        <tag>
            <name>{{ tag.name }}</name>
            <url>http://{{ site.domain }}{{ tag.get_absolute_url }}</url>
        </tag>
        {% endfor %}
    </tags>
    <absolute_url>http://{{ site.domain }}{{ post.get_absolute_url }}</absolute_url>
  </post>
  {% endfor %}
</posts>

And that's that. There's probably a better more dynamic way to generate the XML file, but this works for me when I need something quick and dirty.

Please let me know if you have any suggestions on how to improve this process.

comments

testing 404 500 error pages in django development envirnoment with django-cms May
18
1
0

It's nice to be able to test your 404.html and 500.html templates in your development environment before you push to production.

You could use direct_to_template, but it would be better to use the default Django views that the errors actually use. These are "django.views.defaults.server_error" and "django.views.defaults.page_not_found". All you need to do is to add these to your urlpatterns in a conditonal statement for local development. *** note the += ***

# urls.py

urlpatterns = patterns('',
    ...
)

if settings.LOCAL_DEV:
    urlpatterns += patterns('',
        (r'^404/$', 'django.views.defaults.page_not_found'),
        (r'^500/$', 'django.views.defaults.server_error'),
        (r'^media/(?P.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT} ),
    )

If you're using Django-CMS this is not going to work because the cms page middleware will call the 404 error before it gets to the LOCAL_DEV urls. This is easily fixed by moving the cms url include to after the LOCAL_DEV urls.

urlpatterns += patterns('',
    url(r'^', include('cms.urls')), # put this near the end
)

comments

call slugify template tag in django model or view May
17
1
0

Edited: 6.1.2011 - fixed typo

Sometimes I need to slugify something within a django model or view file. Django already has this functionality written as a template filter, and you can reuse this outside of the templates. You can import slugify into your python code like so:

from django.template.defaultfilters import slugify

slugify(<value>)

Which then allows you to write something like:

class Custom(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField(blank=True) def save(self): self.slug = slugify(self.name) super(Custom, self).save()

However, if you need this type of functionality depending on your situation it might be easier to set this up using the django admin prepopulated_fields option.

# models.py
class Custom(models.Model):
    name = models.CharField(max_length=100)
    slug = models.SlugField()


# admin.py
class CustomAdmin(admin.ModelAdmin):
    prepopulated_fields = {'slug': ('name',)}

admin.site.register(Custom, CustomAdmin)

This will auto-populate the slug field while you type the name within the django admin.

comments

smtp django settings May
11
1
0

I have to go trolling the internet every time I need to locate the default email variables that I need to set up the SMTP connection the settings file for Django, so I'm throwing them here for reference. Perhaps someone else might also find this useful.

EMAIL_HOST = 'mail.digitaldreamer.net'
EMAIL_HOST_USER = '*****'
EMAIL_HOST_PASSWORD = '******'
EMAIL_PORT = '25'
DEFAULT_FROM_EMAIL = "test@example.com"
SERVER_EMAIL = "test@example.com"

comments

get all users in a group in django May
10
1
0

There's a couple of ways to get the list of all users in a group in Django.

First, you should include the User and Groups class from django.contrib.auth (I'm going to assume you have this included in INSTALLED_APPS or else you wouldn't be here).

from django.contrib.auth.models import User, Group

Then you can do:

group = Group.objects.get(name='blogger')
users = group.user_set.all()

or

User.objects.filter(groups__name='blogger')

or if you want to get fancy

perm = Permission.objects.get(codename='blogger')
users = User.objects.filter(Q(groups__permissions=perm) | Q(user_permissions=perm) ).distinct()

comments

iterate over a dictionary inside a django template Apr
27
1
0

The official Django documents how to iterate over a dictionary inside a for loop, but I sometimes forget. I'll attempt to do something like this:

{% for key, value in data %}
    {{ key }}: {{ value }}
{% endfor %}

but will fail miserably. The key to extracting a dictionary inside django templates is the items dictionary function, which "Return a copy of the dictionary’s list of (key, value) pairs".

This allows us to loop through the dictionary using items like so:

{% for key, value in data.items %}
    {{ key }}: {{ value }}
{% endfor %}

comments

integrating recaptcha into a contact form in a django project Apr
15
1
0

I finally had to add a spam blocker on my contact page. Those insidious bots are disturbingly effective at sniffing out forms.

I knew this was going to happen eventually, but I feel captchas are an inconvenience to users so I was trying to delay the inevitable, but it's like attempting to hold back the tide. Only a month after launch I'm receiving over 10 spams a day from my site.

I went with reCAPTCHA. It seemed like it was going to be easy to integrate, it has a Python Plugin, and users are already familiar with how captcha's work. It turns out I was right on all fronts. It's been the right decision.

Here's how I integrated reCAPTCHA into my site. This is also how I wrote my contact application.

First I added two variables to settings.py: RECAPTCHA_PUBLIC_KEY and RECAPTCHA_PRIVATE_KEY and set the values accordingly. Actually, I added these to local_settings.py and imported them into settings.py because I have a policy to never commit passwords and keys into the repository, but you get the idea.

If you want you can write a custom context processor to include these variables in every template, but since I only had one view I needed this for I didn't bother.

I then installed the recapcha-client python library into my virtual environment using pip (you can also use easy_install). Unfortunately I couldn't find any documentation on how to use the recaptcha client, but luckily there's not much to the package and it's easy enough look at the source to figure out what to do.

pip install recaptcha-client

or

easy_install recaptcha-client

Following Seek Nuance's tutorial I then modified my view to import captcha and to check reCAPTCHA before processing the form.

from recaptcha.client import captcha

from django.shortcuts import render_to_response
from django.template import RequestContext
from django.core.mail import send_mail, EmailMessage
from django.http import Http404, HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.contrib.sites.models import Site
from django.conf import settings

from contact.models import Contact
from contact.forms import ContactForm


# Create your views here.
def contact(request):
    template_vars = {
        'section': 'contact',
    }

    template_vars['RECAPTCHA_PUBLIC_KEY'] = settings.RECAPTCHA_PUBLIC_KEY

    if request.POST:
        contact_form = ContactForm(request.POST)
        template_vars['contact_form'] = contact_form

        # Check the form captcha.  If not good, pass the template an error code
        captcha_response = captcha.submit(request.POST.get("recaptcha_challenge_field", None),
                                          request.POST.get("recaptcha_response_field", None),
                                          settings.RECAPTCHA_PRIVATE_KEY,
                                          request.META.get("REMOTE_ADDR", None))

        if not captcha_response.is_valid:
            template_vars['captcha_error'] = "&error=%s" % captcha_response.error_code

        elif contact_form.is_valid():
            send(contact_form)
            return HttpResponseRedirect(reverse('contact_success'))

    else:
        contact_form = ContactForm()
        template_vars['contact_form'] = contact_form

    return render_to_response('contact/contact.html', template_vars, context_instance=RequestContext(request))

Copying straight from the reCAPTCHA client documentation, I then added their Challenge API code into my template. You can put this anywhere in the form you want to apply reCAPTCHA to like so:

<form id="contact-form" action="." method="post" />
<table id="contact">
    {{ contact_form.as_table }}

    <!-- recaptcha -->
    <tr>
        <td></td>
        <td>
          <script type="text/javascript">var RecaptchaOptions = {theme : 'white'};</script>
          <script type="text/javascript" src="http://api.recaptcha.net/challenge?k={{ RECAPTCHA_PUBLIC_KEY }}{{ captcha_error}}">
          </script>

          <noscript>
            <iframe src="http://api.recaptcha.net/noscript?k={{ RECAPTCHA_PUBLIC_KEY }}{{ captcha_error}}"
                    height="300" width="500" frameborder="0"></iframe><br>
            <textarea name="recaptcha_challenge_field" rows="3" cols="40">
            </textarea>
            <input type="hidden" name="recaptcha_response_field" 
                   value="manual_challenge">
          </noscript>
        </td>
    </tr>
    <!-- recaptcha -->

    <tr>
        <td></td>
        <td><input class="button" type="submit" name="submit" value="send"></td>
    </tr>
</table>
</form>

And that was it. It was pretty fast to set up. Now if only I could read that thing, but that's a different problem.

For completeness on how I wrote the contact mail application here's the rest of the views, models, forms, and urls.

# the rest of views.py

def send(contact_form):
    site = Site.objects.get(id=settings.SITE_ID)
    contact = Contact.objects.all()[0]

    # construct email
    from_email = '"%s" <%s>' % (contact_form.cleaned_data['name'], contact_form.cleaned_data['email'])
    send_to = '"%s" <%s>' % (contact.user.first_name + ' ' + contact.user.last_name, contact.user.email)
    subject = '[' + site.name + ' contact] ' + contact_form.cleaned_data['subject']

    if contact_form.cleaned_data['website']:
        message = 'website: %s\n\n' % (contact_form.cleaned_data['website'])
    else:
        message = ''

    message += contact_form.cleaned_data['message']

    #send email
    email = EmailMessage(subject, message, from_email, (send_to,), headers = {'Reply-To': from_email})
    email.send(fail_silently=False)


def success(request):
    template_vars = {
        'section': 'contact',
    }

    return render_to_response('contact/success.html', template_vars, context_instance=RequestContext(request))

models.py

from django.db import models
from django.contrib.auth.models import User


# Create your models here.
class Contact(models.Model):
    user = models.ForeignKey(User, help_text='This is the person who will be receiving emails from the website contact.')

    def __unicode__(self):
        return self.user.username

    # for now set up as a singleton
    def save(self):
        self.id = 1
        super(Contact, self).save()

    def delete(self):
        pass

forms.py

from django import forms
from django.contrib.auth.models import User


class ContactForm(forms.Form):
    name = forms.CharField(max_length=100)
    email = forms.EmailField()
    website = forms.URLField(required=False)
    subject = forms.CharField(max_length=100)
    message = forms.CharField(widget=forms.Textarea)

urls.py

from django.conf.urls.defaults import *

urlpatterns = patterns('digitaldreamer.contact.views',
    url(r'^success/, 'success', name='contact_success'),
    url(r'^, 'contact', name='contact'),
)

comments

get the current site object in a django view Mar
22
1
0

You can use the default Sites framework to get the current site in a django view.

from django.contrib.sites.models import Site

site = Site.objects.get_current()
domain = Site.objects.get_current().domain

or

from django.contrib.sites.models import Site
from django.conf import settings

site = Site.objects.get(id=settings.SITE_ID)

comments

have your django project use an external SMTP server Mar
22
1
0

Most of the time I want to have my Django project send mail through and an external SMTP server. You can easily set this up by adding the following variables to you settings file.

EMAIL_HOST = 'mail.example.com'
EMAIL_PORT = '25'
EMAIL_HOST_USER = 'test'
EMAIL_HOST_PASSWORD = '******'
DEFAULT_FROM_EMAIL = 'test@example.com'
SERVER_EMAIL = 'test@example.com'

comments

create relative paths for your django settings Mar
22
1
0

I wish Django used this by default, but you should be setting up your project using relative paths in your settings. By not hard coding your paths makes your project easier to manage.

Start by setting a variable to use as the base that pulls it's path dynamically through os.path; I use PROJECT PATH.

import os

PROJECT_PATH = os.path.realpath(os.path.dirname(__file__))

You can then append to the PROJECT_PATH variable when setting up your other paths that are based on your project's root path.

MEDIA_ROOT = PROJECT_PATH + '/media/'

TEMPLATE_DIRS = (
    PROJECT_PATH + '/templates/'
)

comments

broadcast django runserver Mar
22
1
0

Sometimes I need to broadcast a django project through the local network, or even externally, with runserver. This is pretty easy to do, although it should only be only be done for development. Never use runserver for production.

Running it through the local network will allow you to connect to your computer from any other computer on the local network. This is handy for client presentations or IE debugging.

You must first find your inet address. You can do this easily with the <ifconfig> command. After that just initialize runserver by passing it the ip and port (remember to source your virtualenv if needed).

ifconfig

./manage.py runserver 192.168.1.2:8000

If for some reason you need to broadcast runserver externally you can bind it to your external ip, or let automatically detect it like this:

./manage.py runserver 0.0.0.0:8000

comments

automatically dump and load data into django Mar
18
1
0

Django provides and easy way to backup and load data from your project regardless of which storage engine you're using through [dumpdata] and [loaddata]. If you don't provide the application name after the dumpdata command it'll output the data from all installed applications.

If you want the file to be formated for reading, use the [--indent] option.

manage.py dumpdata flatpages --indent=4 > flatpages.json

manage.py loaddata flatpages.json

You can name the dumpdata file [initial_data.json] if you want to automatically load the data file when you [syncdb] and [flush]. It's handy to use with the [--noinput] option to suppress user input if you have an initial_data.json set up.

Be very careful with initial_data.json though as you can unwillingly overwrite data, and you'll be a sad panda. I personally will only locally manage initial_data.json, and will keep it out of the repository at all times.

manage.py syncdb --noinput

manage.py flush --noinput

comments

django singleton Mar
17
1
0

Sometimes I have a django model that I want to behave like a singleton where there has to be one, and only one row present in the table at all times. I accomplish this by overriding the model's save() and delete() methods.

class Singleton(models.Model):
    # put model fields here

    def save(self):
        self.id=1
        super(Singleton, self).save()

    def delete(self):
        pass

comments

convert integer to string inside a django template Mar
15
1
0

Every now and then I come across a situation where I'm required to do an integer to string comparison inside a Django template. I need to unify the data types before I run the comparison or else the equality will fail. This sounds like a job for template filters.

I could write my own int_to_string filter like the following (the @stringfilter converts the argument to a string before it passes the variable into the function).

from django.template.defaultfilters import stringfilter

@register.filter
@stringfilter
def int_to_string(value):
    return value

However, I would then have to load the filters into the necessary templates. Yes, I know it's not hard to do, but I'm lazy. Wouldn't it be nice if there was a pre-built filter that did the conversion for me? Well I'm in luck because the slugify filter does just that.

All I need to do is slugify the integer and compare it to the string variable. It works like a charm because when using slugify on an integer, besides the int to string conversion, it won't transform the data.

{% ifequal my_integer|slugify my_string %}

comments