News, ideas and randomness

Don’t delete an image file when deleting a Django model instance

Posted: March 2nd, 2010 | Author: Scott Barnham | Filed under: Django | Tags: | No Comments »

If you have a Django model with a FileField or ImageField, when you delete the model instance, the associated file or image is also deleted. In most cases this is desirable and keeps things tidy, but I had a situation recently where the image file should not be deleted when the model was deleted. Here’s a simple way to override the default behaviour.

Custom file storage

Django uses storage classes to determine how files are read and written. Normally, the data is just written as files to disk, but there are other possibilities such as storing on remote servers.

It’s easy to write a custom file storage class to override the behaviour of the default FileStorageSystem. In this case, we only need to change the delete method so it does not delete the file.

In custom.py

from django.core.files import storage

class NoDeleteFileStorage(storage.FileSystemStorage):
    def delete(self, name):
        pass

We can then use the custom file storage by making an instance and passing it to the ImageField.

In models.py

from custom import NoDeleteFileStorage

ndfs = NoDeleteFileStorage()

class ImageInstance(models.Model):
    image = models.ImageField(storage=ndfs, ...)

It’s as simple as that! Custom file storage has some interesting possibilities. With it you can handle how files are named or integrate with some caching or CDN.


Custom web, Django and mobile development

Posted: February 8th, 2010 | Author: Scott Barnham | Filed under: Company | Tags: , | No Comments »

We started Red Robot Studios in 2008 to develop web-based apps using great technologies like Django. Since then we’ve learned an enormous amount about what it takes to build and deploy large-scale web applications, and as we’ve developed and released our own sites and apps, we’ve increasingly been approached by people who want us to work with them to help them build great sites and services.

We’re still going to be building and releasing our own projects but have also decided to offer our development service to clients as well. We have some excellent clients already and will be accepting work from new clients.

Our aim is not just to provide a normal development service but to help our clients achieve their goals. We want to help and advise based on our experience and go the extra mile to provide really great development services to all our clients.

Our main focus is Django developmentiPhone and mobile development, but we are well experienced in many other areas including things as diverse as WordPress plugins, Facebook apps and Firefox extensions. We’d love to hear about your great idea, so get in touch!


Requiring https for certain paths in Django

Posted: February 6th, 2010 | Author: Scott Barnham | Filed under: Django | No Comments »

A while ago I wrote about Securing Django with SSL. Here’s a small addition.

Some paths need https

If you’re using SSL it makes sense for certain parts of the site to require a secure connection. For example, the admin section.

Previously I shared the secure_required decorator which forces requests to use https for specific views. This works ok, but if you know an entire section of the site under a given path (e.g. /admin/) should be secure, it’s hassle to have to add the decorator to each view.

You can require secure connections over https using webserver config or using Django itself.

Requiring https using Nginx

In your Nginx config file under the section for the unsecure http/port 80 server you can specify a location path and redirect all requests to it to https instead.

server {
    listen 10.10.10.10:80;
    server_name example.com;
...
    location /admin {
        # force admin to use https
        rewrite (.*) https://example.com/$1 permanent;
    }
...
}

Apache and other web servers can have a similar configuration.

If you can configure it in the web server, that’s more efficient because the request can be redirected by the server, without having to contact your Django project. However, it should be fairly rare for requests to be redirected like this so it’s not a big performance issue and sometimes it’s easier to handle things in Django.

Requiring https using Django middleware

In Django it’s easy to write custom middleware which gets called before each request reaches a view.

Here’s a small piece of middleware which checks if the request is over http to a path we want to be secure and if so redirects to the same path but over https.

from django.http import HttpResponsePermanentRedirect
from django.conf import settings

class SecureRequiredMiddleware(object):
    def __init__(self):
        self.paths = getattr(settings, 'SECURE_REQUIRED_PATHS')
        self.enabled = self.paths and getattr(settings, 'HTTPS_SUPPORT')

    def process_request(self, request):
        if self.enabled and not request.is_secure():
            for path in self.paths:
                if request.get_full_path().startswith(path):
                    request_url = request.build_absolute_uri(request.get_full_path())
                    secure_url = request_url.replace('http://', 'https://')
                    return HttpResponsePermanentRedirect(secure_url)
        return None

In settings.py

MIDDLEWARE_CLASSES = (
...
    'myproject.middleware.SecureRequiredMiddleware',
)

HTTPS_SUPPORT = True
SECURE_REQUIRED_PATHS = (
    '/admin/',
    '/accounts/',
    '/management/',
)

SECURE_REQUIRED_PATHS is a list or tuple of paths that should be secure. Any request to a path which starts with one of these will be required to use https.

HTTPS_SUPPORT is a custom setting to make it easier to use this on your dev server without SSL support. Set it to True in the settings for the live server and False in the settings for the dev server.

So there we go, an easy way to require secure https requests for certain parts of your Django site.


Find Nearby Developers on Djangogigs

Posted: June 23rd, 2009 | Author: Scott Barnham | Filed under: Djangogigs | Comments Off

Djangogigs is a very successful job board we run for web developers who use the Django framework. A few jobs and freelance gigs are added each week and there is a large directory of developers available for hire.

Finding Django Developers

As the number of developers increases, it becomes harder to find the person you are looking for. A while ago we added filtering by country, but for large countries such as the US, there are still way too many developer profiles to look through. We now have nearly 700 Django developers on the site and needed to find a better way.

The solution we chose was geocoding the location of each developer so that we can find developers who are close to a given location. Ignoring the technical stuff, it’s very easy to use. Just type in the name of your town or city and get back a list of developers who are nearby.

We’re confident this will make it much easier to find Django developers and may add other search options in future. As always, we’re happy to hear your feedback.

Follow us on Twitter

We’ve also added a Djangogigs Twitter account which you can follow to see the latest gigs as they are added.


Securing Django with SSL

Posted: February 18th, 2009 | Author: Scott Barnham | Filed under: Django | 2 Comments »

When we built the centralized authentication system for Red Robot Studios we wanted all authentication and account resources to be available solely over https.

This article covers some tips and tricks we discovered while building the app, and how you can use Django to get fine-grained control as to which resources are available securely.

Why bother with security?

We all know that data sent over http is cleartext and can potentially be read on any network between the client and server. But the risk feels pretty minimal and many sites don’t bother using SSL to encrypt sensitive traffic. For online banking and ecommerce, you’d be crazy not to use it, but for other sites, why bother?

The chances of your http requests being snooped upon by an ISP, intermediate networks or your hosting company seem minimal. But one potentially big risk is users accessing your website on an open wireless network.

For example, perhaps your user has an unsecured wireless home or office network or maybe they use wireless networks in coffee shops and airports: It’s really easy in this situation for sensitive requests to be snooped upon.

The data on your website may not be sensitive, but if you use Django’s admin or authentication frameworks, two important bits of information are passed as cleartext.

When a user logs in, their username and password is posted in cleartext. Assuming login is successful, each subsequent request includes a cookie containing the sessionid. The sessionid is just a random string, but if you know the sessionid of a user, it is trivial to hijack the session and have the same access to the website as that user does until they log out.

Encrypting login sessions

If you want to be sure user credentials and sessions cannot be compromised by eavesdroppers, you need to use SSL encryption. Install an SSL certificate on the server so that traffic is encrypted end-to-end between client and server.

You probably don’t want the whole site to be secure because it will be a lot slower and significantly increase the load on your servers. Instead, you can be selective about which parts of the site should use https instead of http. If you want user sessions to be secure, you should make sure that logging in and all parts of the site that require a logged-in user use https.

SSL

Standard SSL certificates are pretty cheap these days – under $20 per year. We go some from RapidSSLOnline. Each secure site needs its own IP address, so if you’re hosting multiple sites using virtual hosting, you’ll need to look in to getting some dedicated IPs.

There are lots of guides to installing SSL certificates and configuring web servers such as Apache, Lighttpd and Nginx, so I won’t cover that here.

Making Django sessions secure

Django uses cookies for its sessions. When a cookie is set, you can specify that it be a secure cookie, meaning it is only ever passed over https and not in http requests. We can tell Django to use secure cookies for sessions by adding a setting to settings.py

SESSION_COOKIE_SECURE = True

If you set Django to use secure cookies then try to log in over http you will get the error

Looks like your browser isn’t configured to accept cookies. Please enable cookies, reload this page, and try again.”

This happens because Django sets the cookie, but it’s a secure cookie, so when the page loads over http, Django can’t see the cookie and so assumes cookies are disabled in your browser.

Requiring https for admin

To avoid this cookie warning and make sure you only ever pass your admin credentials over https, you can configure your web server so that any http requests are redirected to https.

For example, in Nginx it would look like:

server {
    server_name example.com;
    location /admin {
        # force admin to use https
        rewrite (.*) https://example.com/$1 permanent;
    }
...
}

In Apache, something like:

<Location /admin>
    RewriteRule (.*) https://example.com/$1 [L,R=301]
    ...
</Location>

Of course, these bits of config should go in the http config, not the https config or you will cause infinite redirects!

Requiring https for certain views

If all the logged-in parts of your site are in a certain path (e.g. /accounts/ and /members/) you can configure your web server in the same way to require https for these locations.

If certain views require https (e.g. /members/bert/ is public but /members/bert/edit/ requires login), you may want to check request.is_secure() in those views. A neat way to do it is with a decorator which can also redirect any http requests to https.

from django.conf import settings
from django.http import HttpResponseRedirect

def secure_required(view_func):
    """Decorator makes sure URL is accessed over https."""
    def _wrapped_view_func(request, *args, **kwargs):
        if not request.is_secure():
            if getattr(settings, 'HTTPS_SUPPORT', True):
                request_url = request.build_absolute_uri(request.get_full_path())
                secure_url = request_url.replace('http://', 'https://')
                return HttpResponseRedirect(secure_url)
        return view_func(request, *args, **kwargs)
    return _wrapped_view_func

Then on your view:

@secure_required
@login_required
def edit_member(request, slug):
    ...

Moving between http and https pages

It’s normal to use full path URLs like /accounts/login/ and /blog/. Bear in mind that if you are accessing the site over https and follow one of these links, you will also access them over https. If you want to be explicit, you need to specify the protocol and domain in the links, e.g. https://example.com/accounts/login/ and http://example.co/blog/ .

For $20 and a bit of config, you can secure logged-in sessions on your site and protect yourself and your users from being compromised by eavesdroppers. There are still plenty of sites where this is overkill, but you can see now how easy it is to secure your Django site with SSL.


Easy Job Boards Rebrand

Posted: January 2nd, 2009 | Author: Scott Barnham | Filed under: Easy Job Boards | Comments Off

We launched our hosted job board product a couple of months ago under the name “Fuselagejobs”. Much as we liked the name and plane logo, it’s not obvious from the name that it’s an easy way to have your own job board.

Today we changed to easyjobboards.com with a fresh new logo and more obvious domain name. We hope this will be much clearer to people who are looking for a hosted job board solution. We chose “easy” to emphasise one of our key goals: make it easy for non-technical folks to start and run a job board.

We have many improvements in the pipeline and we’d love to hear your suggestions.


Handling Subdomains in Django

Posted: December 12th, 2008 | Author: Scott Barnham | Filed under: Django | No Comments »

This is the first part of our series on some of the more interesting tech we’ve developed for Red Robot Studios. We’re working from webserver up, so we thought that subdomains would be a good place to start.

Subdomains are useful when you want to host multiple sites with the same code and different data. For example, providing websites for clubs where each club has its own subdomain. In Django, you could have a Club model and some associated models holding data. When a user visits alpha.example.com, you want to show the data from the Club model instance for alpha.

DNS Subdomains and Wildcards

You could add individual subdomains using DNS CNAMEs or A records, but if you want to generate objects in your Django app on the fly, have a look at wildcards. To match any subdomain you add “*” as a subdomain. It looks something like:

*.example.com 14400 IN A 208.77.188.166

This will match www.example.com as well as alpha.example.com, beta.example.com, etc.

Webserver Wildcards

The webserver config needs a similar setting so it knows to respond to any subdomain.

In Nginx, it looks something like:

server {
    listen       208.77.188.166:80;
    server_name  example.com *.example.com;
...

In Apache:

<VirtualHost 208.77.188.166>
    ServerName example.com
    ServerAlias *.example.com
...

If you want to use SSL certificates on your subdomains, you need to get a “wildcard subdomain certificate”. They cost more than a regular certificate, but are necessary to provide a valid certificate for any subdomain on your site. We go ours from RapidSSLOnline.

This should be enough config for your Django app to receive requests for any subdomain. Now your Django app needs to respond appropriately for each subdomain.

Getting the Subdomain in Django

There are a few different ways to do it, but we went with a piece of middleware that gets the subdomain from the request and retrieves a matching model.

We have added two additional settings: DOMAIN_MIDDLEWARE_MODEL and DOMAIN_MIDDLEWARE_INSTANCE_NAME to our settings.py so we can specify the model which the middleware queries, and the name which the instance is given when added to our request instance.

DOMAIN_MIDDLEWARE_MODEL = 'core.Club'
DOMAIN_MIDDLEWARE_INSTANCE_NAME = 'club'

The model is assumed to have a field named “slug”, which the middleware uses to match the subdomain against an instance of the model.

Right, so let’s create our middleware:

class DomainMiddleware(object):
    """Gets the correct instance of an application-specific model by matching the
    sub-domain of the request."""

    def __init__(self):
        self.site_domain = Site.objects.get_current().domain
        if self.site_domain.startswith('www.'):
            self.site_domain = self.site_domain[4:]
        self.SUBDOMAIN_RE = re.compile(r'^(?:www\.)?(?P<slug>[\w-]+)\.%s' % re.escape(self.site_domain))
        try:
            app_name, model_name = settings.DOMAIN_MIDDLEWARE_MODEL.split('.', 2)
            self.model = get_model(app_name, model_name)
            self.instance_name = settings.DOMAIN_MIDDLEWARE_INSTANCE_NAME
            assert self.instance_name
        except (AttributeError, AssertionError):
            raise ImproperlyConfigured('DomainMiddleware requires DOMAIN_MIDDLEWARE_MODEL and DOMAIN_MIDDLEWARE_INSTANCE_NAME settings')

In our init method we do some basic setup like creating a regex which will match the subdomain slug, and loading in our model using django.db.models.get_model with the app.model args from DOMAIN_MIDDLEWARE_MODEL.

    def process_view(self, request, view_func, view_args, view_kwargs):
        """If domain is not main site, check for subdomain.

        Get the model from the subdomain slug.
        """
        port = request.META.get('SERVER_PORT')
        domain = request.META.get('HTTP_HOST', '').replace(':%s' % port, '')
        if domain.startswith('www.'):
            domain = domain[4:]
        if domain != self.site_domain:
            match = self.SUBDOMAIN_RE.match(domain)
            if match:
                slug = match.group('slug')
                instance = get_object_or_404(self.model, slug=slug)
            setattr(request, self.instance_name, instance)
        return None

In process_view we grab the subdomain from the HTTP_HOST header of the request, and using get_object_or_404 we load the correct instance of the model and set it as an attribute on our request object with the name given in DOMAIN_MIDDLEWARE_INSTANCE_NAME.

When someone goes to alpha.example.com the middleware picks out alpha and gets the Club instance with slug=alpha and adds it to request to be used in views.

The middleware uses Site.objects.get_current() to get the base URL, so make sure you have Site set up properly or none of your subdomains will match.

The advantage of having the middleware load in the correct instance for you is that your views can simply use the club attribute to access all related data for this club.

def index(request):
    members = request.club.members.approved().order_by('-creation_date')
    return list_detail.object_list(
        request                 = request,
        queryset                = members,
        template_name           = 'club/index.html',
        template_object_name    = 'member'
    )

This is extremely useful when you want to ensure that your data is correctly filtered: you don’t need to have each view filter based on the subdomain, which is pretty error-prone, and if you get it wrong a user’s data would end up on someone else’s page. This way is a lot simpler.


Job Boards now with Submit, Analytics and API

Posted: December 2nd, 2008 | Author: Scott Barnham | Filed under: Easy Job Boards | Comments Off

Instead of launching a separate version of Fuselagejobs we decided to tighten our focus and add new features to the existing job boards.

  • You can now allow visitors to submit job listings to your job board. If you approve the job listing, it will be displayed on your job board.
  • We’ve improved the management interface where you can add and manage jobs and contact details.
  • You can now use Google Analytics to track visitors to your job board.
  • We added a lightweight JSON API to make it easier to display your jobs on other websites. Widgets coming soon.
  • We’re confident these new features make the job boards more useful and better value. We’d love to hear your feedback on these changes or anything you’d like to see.
  • Get your own job board now on Fuselagejobs – it’s free to try.


Why we launched a Job Board app

Posted: December 2nd, 2008 | Author: Scott Barnham | Filed under: Easy Job Boards | No Comments »

Our first product is Fuselagejobs which lets you easily start your own job board and list job vacancies online. It might seem like a strange choice for a hosted web application. Job listings have been on the web for a long time and there are established big players as well as open source alternatives. So where do we fit in?

Big Players

The big job databases have lots of traffic, lots of employers and lots of candidates. They list jobs from different industries and rely on search to find what you are looking for. That’s ok, but maybe not the best way for candidates to find interesting jobs and for employers to find the best candidates. It all seems very big and impersonal.

Niche Job Boards

Small job boards that target an industry niche or geographical area are becoming popular. We saw great success with Djangogigs, a job board for web developers who use the Django framework. That’s a good example of a tightly-focussed industry niche. It was launched over a year ago and has had over 500 jobs submitted so far. It quickly became the de facto place for employers to post job ads and candidates to look for work in that niche.

Many of the large blogs and other websites with a lot of users have started their own job boards as a way of helping their community and generating some revenue. The jobs are focussed enough that they are interesting to the website regulars. Employers pay to advertise knowing the people who see their job adverts are the type of candidates they’d like to attract.

DIY for non-geeks

So why not use one of the open source packages to set up your own job board? You could if you have the technical skills or have staff to do that for you. If you don’t want to wait for the IT guys or in-house developers to get round to it, Fuselagejobs is a good alternative. It’s easy to use, requires no technical skills and is fully hosted. Just sign up and start adding jobs within minutes.

Innovation

A basic job board is pretty easy to develop, but we think there’s room for innovation to make it easier for the right candidates to find the right jobs. As always, we’re keen to hear your feedback on what you’d like job boards to do for you.

So that’s why we launched a job board app!

Check out Fuselagejobs and create your own job board for free.


Job boards made easy

Posted: October 20th, 2008 | Author: Scott Barnham | Filed under: Easy Job Boards | Comments Off

We’ve just launched Fuselage which lets you create your own job board.

Each job board has its own website with admin interface and is hosted by Red Robot Studios. It’s very easy to get started and there’s nothing to install – all you need is a modern web browser.

Niche job boards are taking off. A niche job board brings together people with specific skills or located in a particular area. We know from past experience that a focussed community can build around a job board leading to higher quality candidates and more relevant job listings – a win-win situation for employers and candidates.

Fuselage fills the need for people who want to create their own niche job board or for companies or recruiters who need a simple way to list jobs online.

Fuselage is the first app to be released by Red Robot Studios and we have high hopes for it. In the coming weeks we’ll be adding new features and releasing different packages.

If you’re interested in job boards, you can try Fuselage for free.