Crosstips.org

My fun Crossword solver project. Crosstips.org & Krysstips.se

Kung Fu

Fujian White Crane Kung Fu

Fry-IT

Fry-IT is the company I work for

Photos

Photoalbum, both old and new.

Zope

What I have and am doing with Zope

Receptsamlingen

In Swedish only. About my "Collection of Recipes" website.

Contact me

My contact details and how to contact me.

 

KungFuPeople.com
Do you train Kung Fu?
Or know someone who does?
Then check out KungFuPeople.com


Mobile version of this page Mobile version of this page

RSS

Hot topics

by Peter Bengtsson: How is that any different? My point was that by leaving it as it is it beco...

Musings about django.contrib.auth.models.User

by Ash Christopher: Instead of sub-classing, you can look at using mix-ins....

Musings about django.contrib.auth.models.User

by Federico Cáceres: +1 to user profiles!...

Musings about django.contrib.auth.models.User

by Peter Bengtsson: Yes, User profiles all the way!...

Musings about django.contrib.auth.models.User

by Vincent Driessen: Have you ever extended the User object in another way, then? Without using ...

Musings about django.contrib.auth.models.User

by : Some consider the module system of nginx to be a strength. Dynamic loading...

fcgi vs. gunicorn vs. uWSGI

by Steve Schwarz: Nice post and explanation. I just came across django-queued-storage that ha...

Hosting Django static images with Amazon Cloudfron

by David De Sousa: thanks, I'll look up to it....

Hosting Django static images with Amazon Cloudfron

by Peter Bengtsson: There's an app called django-storage which I've used in another project to ...

Hosting Django static images with Amazon Cloudfron

by David De Sousa: is there a way to upload all the FileFields and the ImageFields to the Amaz...

Hosting Django static images with Amazon Cloudfron

Old entries


April, 2010
Word Whomp solvers love Crosstips
UPPER vs. ILIKE
Who was logged in during a Django exception
fcgi vs. gunicorn vs. uWSGI
Cycling across England on Orange Snapshot

March, 2010
The awesomest way possible to serve your static stuff in Django with Nginx
Beautiful photos from the Katrina hurricane
Speed test between django_mongokit and postgresql_psycopg2
How and why to use django-mongokit (aka. Django to MongoDB)
Ubuntu Cola or Ubuntu Linux
Importance of public URLs and how enterprisecarsales.com gets it wrong

February, 2010
January, 2010
2009
2008
2007
2006
2005
2004
2003

 

You're viewing blogs from Django only.

View all different categories

27th of August

Musings about django.contrib.auth.models.User

Dawned on me that the Django auth user model that ships with Django is like the string built-in of a high level programming language. With the string built-in it's oh so tempting to add custom functionality to it like a fancy captialization method or some other function that automatically strips whitespace or what not. Yes, I'm looking at you Prototype for example.

By NOT doing that, and leaving it as it is, you automatically manage to Keep It Simple Stupid and your application code makes sense to the next developer who joins your project.

I'm not a smart programmer but I'm a smart developer in that I'm good at keeping things pure and simple. It means I can't show off any fancy generators, monads or metaclasses but it does mean that fellow coders who follow my steps can more quickly hit the ground running.

My colleagues and I now have more than ten Django projects that rely on, without overriding, the django.contrib.auth.models.User class and there has been many times where I've been tempted to use it as a base class or something instead but in retrospect I'm wholeheartedly happy I didn't. The benefit isn't technical; it's a matter of teamwork and holistic productivity.

9th of July

Hosting Django static images with Amazon Cloudfront (CDN) using django-static

About a month ago I add a new feature to django-static that makes it possible to define a function that all files of django-static goes through.

First of all a quick recap. django-static is a Django plugin that you use from your templates to reference static media. django-static takes care of giving the file the optimum name for static serving and if applicable compresses the file by trimming all whitespace and what not. For more info, see The awesomest way possible to serve your static stuff in Django with Nginx

The new, popular, kid on the block for CDN (Content Delivery Network) is Amazon Cloudfront. It's a service sitting on top of the already proven Amazon S3 service which is a cloud file storage solution. What a CDN does is that it registers a domain for your resources such that with some DNS tricks, users of this resource URL download it from the geographically nearest server. So if you live in Sweden you might download myholiday.jpg from a server in Frankfurk and if you live in North Carolina, USA you might download the very same picture from Virgina, USA. That assures the that the distance to the resource is minimized. If you're not convinced or sure about how CDNs work check out THE best practice guide for faster webpages by Steve Sounders (it's number two)

A disadvantage with Amazon Cloudfront is that it's unable to negotiate with the client to compress downlodable resources with GZIP. GZIPping a resource is considered a bigger optimization win than using CDN. So, I continue to serve my static CSS and Javascript files from my Nginx but put all the images on Amazon Cloudfront. How to do this with django-static? Easy: add this to your settings:

 DJANGO_STATIC = True
 ...other DJANGO_STATIC_... settings...
 # equivalent of 'from cloudfront import file_proxy' in this PYTHONPATH
 DJANGO_STATIC_FILE_PROXY = 'cloudfront.file_proxy'

Then you need to write that function that get's a chance to do something with every static resource that django-static prepares. Here's a naive first version:

 # in cloudfront.py

 conversion_map = {} # global variable
 def file_proxy(uri, new=False, filepath=None, changed=False, **kwargs):
     if filepath and (new or changed):
         if filepath.lower().split('.')[-1] in ('jpg','gif','png'):
             conversion_map[uri] = _upload_to_cloudfront(filepath)
     return conversion_map.get(uri, uri)


>Read the whole text (1013 more words)

30th of May

Correction: running Django tests with MongoDB is NOT slow

At Euro DjangoCon I met lots of people and talked a lot about MongoDB as the backend. I even did a presentation on the subject which led to a lot of people asking me more questions about MongoDB.

I did mention to some people that one of the drawbacks of using MongoDB which doesn't have transactions is that you have to create and destroy the collections (like SQL tables) each time for every single test runs. I thought this was slow. It's not

Today I've been doing some more profiling and testing and debugging and I can conclude that it's not a problem. Creating the database has a slight delay but it's something you only have to do once and actually it's very fast. Here's how I tear down the collections in between each test:

 class BaseTest(TestCase):

    def tearDown(self):
        for name in self.database.collection_names():
            if name not in ('system.indexes',):
                self.database.drop_collection(name)

For example, running test of one of my apps looks like this:

 $ ./manage.py test myapp
 ...........lots.............
 ----------------------------------------------------------------------
 Ran 55 tests in 3.024s

So, don't fear writing lots of individual unit tests. MongoDB will not slow you down.

25th of May

"Using MongoDB in your Django app - implications and benefits"

Straight from DjangoCon 2010 here in Berlin. Slides from my talk on "Using MongoDB in your Django app - implications and benefits" are available as a HTML5 web page so you'll need one of those fancy browsers like Chrome to be able to view it. Sorry.

23rd of May

mongoengine vs. django-mongokit

django-mongokit is the project you want to use if you want to connect your Django project to your MongoDB database via the pymongo Python wrapper. An alternative (dare I say competing alternative) is MongoEngine which is bridge between Django and straight to pymongo. The immediate difference you notice is the syntax. django-mongokit looks like MongoKit syntax and MongoEngine looks like Django ORM. They both accomplish pretty much the same thing. So, which one is fastest?

First of all, remember this? where I showed how django-mongokit sped past the SQL ORM like a lightning bullet. Well appears MongoEngine is even faster.

mongoengine vs. django-mongokit

That's an average of 23% faster for all three operations!

20th of May

Review: Django 1.1 Testing and Debugging

The lovely people of Packt Publishing asked me to review Karen Tracey's latest book Django 1.1 Testing and Debugging.

I didn't actually read the book but rather skimmed it, apart from some selected parts and from what I read it's obvious that Karen has an ability to write to people who are not experts on the subject. Years of being a top contributor on the Django users mailing list must have something to do with it.

But here's the cracker. I didn't learn anything from this book (actually, I wasn't aware of the pp command in the pdb debugger). Is that a complaint about the book? No! It just means that the book was aimed at beginners and apparently I'm not a beginner any more. Great!

One thing I would have liked to see is more about testing strategy since this is something beginners often have problems with. I don't know if there even is such a word as "testing strategy" but I'm referring to the thinking behind what to test and more importantly sometimes what not to test. Beginners have a tendency to write tests for the most specific things and thus spending all their time assuring the most unrealistic scenarios are covered. Also, a lot of beginner tests I see check basic things like types which the semi-compiler will just automatically cover for you. Perhaps for a beginner, just getting some tests up and running this is a big step forward.

I'm a little bit disappointed that my lovely gorun wasn't mentioned in the book :) Perhaps the next version Karen?

15th of April

Who was logged in during a Django exception

In lack of a fancier solution here's how I solved a problem of knowing who was logged in when an error occurred. I'm building a Intranet like system for a close group of people and if an error occurs I get an email that reminds me to add more tests. So I fix the bugs and upgrade the server. But I often want to know what poor sucker was logged in at the time the exception happened so that I can email them and say something like "Hi! I noticed your stumbled across a bug. My bad. Just wanted to let you know I've fixed that now"

So to do this I installed a silly little piece of middleware:

 from django.conf import settings
 class ExceptionExtraMiddleware(object):
    def process_exception(self, request, exception):
        if settings.DEBUG:
            return
        try:
            logged_in_info = ''
            if request.user and request.user.is_authenticated():
                logged_in_info = "%s" % request.user
                if request.user.email:
                    logged_in_info += ' %s' % request.user.email
                if request.user.first_name or request.user.last_name:
                    logged_in_info += ' (%s %s)' % \
                      (request.user.first_name, request.user.last_name)
            if logged_in_info:
                request.META['ERROR-X-LOGGED-IN'] = logged_in_info
        except:
            # don't make matters worse in these sensitive times
            logging.debug("Unable to debug who was logged in", exc_info=True)

This means that when I get an email with the traceback and snapshot of the request object I get this included:

 ...
 'ERROR-X-LOGGED-IN': u'anita (Anita Test)',
 ...

UPDATE

The code above had a bug in it. Doing an if on request.user will return true even if there is no logged in user. The safest thing is to change it to:

 if request.user and request.user.is_authenticated():

8th of April

fcgi vs. gunicorn vs. uWSGI

uwsgi is the latest and greatest WSGI server and promising to be the fastest possible way to run Nginx + Django. Proof here But! Is it that simple? Especially if you're involving Django herself.

So I set out to benchmark good old threaded fcgi and gunicorn and then with a source compiled nginx with the uwsgi module baked in I also benchmarked uwsgi. The first mistake I did was testing a Django view that was using sessions and other crap. I profiled the view to make sure it wouldn't be the bottleneck as it appeared to take only 0.02 seconds each. However, with fcgi, gunicorn and uwsgi I kept being stuck on about 50 requests per second. Why? 1/0.02 = 50.0!!! Clearly the slowness of the Django view was thee bottleneck (for the curious, what took all of 0.02 was the need to create new session keys and putting them into the database).

So I wrote a really dumb Django view with no sessions middleware enabled. Now we're getting some interesting numbers:

 fcgi (threaded)              640 r/s
 fcgi (prefork 4 processors)  240 r/s (*)
 gunicorn (2 workers)         1100 r/s
 gunicorn (5 workers)         1300 r/s
 gunicorn (10 workers)        1200 r/s (?!?)
 uwsgi (2 workers)            1800 r/s
 uwsgi (5 workers)            2100 r/s
 uwsgi (10 workers)           2300 r/s

 (* this made my computer exceptionally sluggish as CPU when through the roof)


>Read the whole text (471 more words)

24th of March

The awesomest way possible to serve your static stuff in Django with Nginx

I'm the proud creator of django-static which is a Django app that takes care of how you serve your static media the best way possible. Although some of these things are subjective generally this is the ideal checklist of servicing your static media:

  1. Cache headers must be set to infinity
  2. URLs must be unique so that browsers never have to depend on refreshing
  3. The developer (who decided which media to include) should not have to worry himself with deployment
  4. The developer/artist (who makes the media) should not have to worry himself with deployment
  5. All Javascript and CSS must be whitespace optimized in a safe way and served with Gzip
  6. All images referenced inside CSS should be taken care of too
  7. It must be possible to combine multiple resources of Javascript or CSS into one
  8. It must be possible to easily test production deployment in development environment without too much effort
  9. A sysadmin shouldn't have to understand a developers Django application
  10. A development environment must be unhindered by this optimization
  11. Processing overhead of must be kept to a minimum
  12. Must be possible to easily say which resources can be whitespace optimized and which can not


>Read the whole text (805 more words)

9th of March

Speed test between django_mongokit and postgresql_psycopg2

Following on from yesterday's blog about How and why to use django-mongokit I extended the exampleproject which is inside the django-mongokit project with another app called exampleapp_sql which does the same thing as the exampleapp but does it with SQL instead. Then I added a very simple benchmarker app in the same project and wrote three functions:

  1. One to create 10/100/500/1000 instances of my class
  2. One to edit one field of all 10/100/500/1000 instances
  3. One to delete each of the 10/100/500/1000 instances


>Read the whole text (325 more words)

8th of March

How and why to use django-mongokit (aka. Django to MongoDB)

How and why to use django-mongokit Here I'm going to explain how to combine Django and MongoDB using MongoKit and django-mongokit.

MongoDB is a document store built for high speed and high concurrency with a very good redundancy story. It's an alternative to relational databases (e.g. MySQL) that is what Django is tightly coupled with in it's ORM (Object Relation Mapping) and what it's called now is ODM (Object Document Mapping) in lack of a better acronym. That's where MongoKit comes in. It's written in Python and it connects to the MongoDB database using a library called pymongo and it turns data from the MongoDB and turns it into instances of classes you have defined. MongoKit has nothing to do with Django. That's where django-mongokit comes in. Written by yours truly.


>Read the whole text (1551 more words)

16th of January

Bookmarklet to replace the current domain with localhost:8000

If you, like me, have various projects that do things like OAuth on Twitter or Google or you have a development site that goes to PayPal. So you're doing some Django development on http://localhost:8000/foo and click, for example, to do an OAuth on Twitter with an app you have there. Then Twitter will redirect you back to the live site with which you've set it up. But you're doing local development so you want to go back to http://localhost:8080/... instead.

Add this bookmarklet: to localhost:8000 to your browser Bookmarks toolbar and it does exactly that.

Here's its code in more verbose form:

 (function() { 
    a = function(){
      location.href = window.location.href.replace(/http:\/\/[^\/]+\//,
             'http://localhost:8000/')
    };
    if (/Firefox/.test(navigator.userAgent)) { 
      setTimeout(a,0)
    } else {
       a()
    }
  })()

16th of December

Migrating with South on a field that uses auto_now_add=True

I have a Django model that looks something like this:

 class MyModel(models.Model):
    modify_date = models.DateTimeField(auto_now=True)
    ...

Retroactively now I wanted to add a field called add_date which uses the auto_now_add=True trick. The migration used in this project is South which is great but doesn't work very well with the auto_now_add=True because the field doesn't have a straight forward default. So, first I changed the field to this:

 class MyModel(models.Model):
    modify_date = models.DateTimeField(auto_now=True)
    add_date = models.DateTimeField(auto_now_add=True, null=True)
    ...

Notice the null=True which is important. Then I used startmigration to generate the code for the forward and backward to which I added a this stuff:

 class Migration:

    def forwards(self, orm):

        db.add_column('myapp_mymodel', 'add_date', orm['myapp.mymodel:add_date'])
        for each in MyModel.objects.all():
            # since MyModel is referenced elsewhere I can work out the oldest date
            oldest_date = get_oldest_related_date(each, 
                               default=each.modify_date)
            each.add_date = oldest_date
            each.save()

That way all old records will have the date (not entirely accurate but good enough) and all new records will automatically get a date. Is there a better way? I bet, but I don't know how to do it.

15th of November

Those Crazy Chinese

Those Crazy Chinese My friend Chris West has built a great new site called Those Crazy Chinese which describes itself like this:

"Chinese (Mandarin) is a beautiful and highly literal language - directly translated, many words have entertaining and occasionally logical meanings."

It's built in Django and it integrates to Twitter so if you're on Twitter just follow it there to get the latest additions of new interesting and amusing literal translations. This website is mainly geared towards people who are, like Chris, learning Mandarin.

16th of October

Messed up columns in Django Admin

In case this bites someone else like it bit more and chewed off many many minutes of debugging time.

If you ever get weird columns in your Django Administration interface, I now know why that happens. See this screenshot example:

Messed up columns in Django Admin

This happens when you've defined a TEMPLATE_STRING_IF_INVALID in your settings.py. I always put in my local_settings.py this line:

 TEMPLATE_STRING_IF_INVALID = '{{ %s }}'

So that I can quickly see which variable references in template code is potential typos. I'm not a big fan of the implicit magic of equating absence to False/None so I try to avoid the confusion altogether.

12th of October

Automatically strip whitespace in Django forms

The current project I'm working has at the time of writing 20 different forms (90% model forms) instantiated in different scenarios. Django doesn't automatically strip whitespace in text based fields. So instead of doing this:

 class ContactMarketingForm(forms.ModelForm):
    class Meta:
        model = ContactMarketing
        exclude = ('contact',)

    def clean_notes(self):
        return self.cleaned_data['notes'].strip()

    def clean_name(self):
        return self.cleaned_data['name'].strip()

Instead I wrote a common class for all of my form classes to use:

 class _BaseForm(object):
    def clean(self):
        for field in self.cleaned_data:
            if isinstance(self.cleaned_data[field], basestring):
                self.cleaned_data[field] = self.cleaned_data[field].strip()
        return self.cleaned_data

 class BaseModelForm(_BaseForm, forms.ModelForm):
    pass

 class ContactMarketingForm(BaseModelForm):
    class Meta:
        model = ContactMarketing
        exclude = ('contact',)

Now all text inputs and textareas are automatically whitespace stripped. Perhaps useful for other Djangonauts.

26th of September

London Frock Exchange launched

London Frock Exchange launched Today we launched The London Frock Exchange which is a joint project between Fry-IT, Charlotte Davies and Sarah Caverhill

Elevator sales pitch: Unlike other clothes swapping sites, with Charlotte, Sarah and Rani as an expert hub in the middle you don't swap straight across; no you swap one frock in and can choose a frock (of equal value) from the pool of frocks.

Fry-IT is co-founding this venture and hope it'll make us billionaires by the end of the year (They take a small admin fee of £25 for sending you a frock back but sending it in is free with freepost). It's been great fun to work on it over the last couple of months as it means we (Fry-IT is a all-male highly technical company) have had to learn about sizes, body shapes and trying to learn how a female web audience thinks. The ladies have done a great job of seeding it with lots and lots of frocks all of which you can wear in a matter of days if you just swap one of equal value in first. Enjoy!

24th of August

Custom CacheMiddleware that tells Javascript a page is cached in Django

Here I'm going to explain a solution I had to make for a site I recently launched. Basically, I wanted to cache the whole page in memcache and set the appropriate Expires and Cache-Control headers so that my view was only rendered once an hour and parts of the page needs to be unique (i.e. "Hi, logged in as xxxx")

The advantages is great: The page loads fast, content is stored in memcache every hour, page still appears to be dynamic.

The disadvantages are not so great: the AJAX loads fast but causes a flicker

Basically, I wrote a custom decorator called custom_cache_page(<delay in seconds>) that works like the normal cache_page(<delay in seconds>) decorator available in stock Django. However, my decorator inserts a piece of HTML into the rendered HTML (before it's stored in memcache) that I later use to update certain elements of the page with AJAX instead of server side.


>Read the whole text (519 more words)

18th of August

Calling all kung fu people - kungfupeople.com

Calling all kung fu people - kungfupeople.com Tonight we're launching our new Kung Fu website: kungfupeople.com

My friend Chris and I have been busy building a website where people who do kung fu can put themselves on a map to say where they train kung fu, what style they do and what kung fu club they belong to. The site is very much centred on having a world map and each little pin on the map is one kung fu martial artist.

This site is build in Django and is based on work that was done to build Django People originally developed by Simon Willison. We took his original code and revamped it almost completely.

Our goal is to slowly build up a world map of people from all sorts of clubs and styles and hopefully one day become the best place on the Internet for understanding what clubs are available where and what styles different people do. The site has been in an "alpha testing" phase now for a couple of weeks and even though we still have lots of ideas and cool features to add we believe it's ready to go live.

So if you train kung fu or know someone who trains kung fu go to our website and add yourself to the map

1st of June

My dislike for booleans and that impact on the Django Admin

I've got this model in Django:

 class MyModel(models.Model):
    completed_date = models.DateTimeField(null=True)

My dislike for booleans and that impact on the Django Admin By using a DateTimeField instead of a BooleanField I'm able to record if an instance is completed or not and when it was completed. A very common pattern in relational applications. Booleans are brief but often insufficient. (Check out Ned Batchelder's Booleans suck)

To make it a bit more convenient (and readable) to work with I added this method:

 class MyModel(models.Model):
    completed_date = models.DateTimeField(null=True)

    @property
    def completed(self):
        return self.completed_date is not None

That's great! Now I can do this (use your imagination now):

 >>> from myapp.models import MyModel
 >>> instance = MyModel.objects.all()[0]
 >>> instance.completed
 False
 >>> instance.completed_date = datetime.datetime.now()
 >>> instance.save()
 >>> instance.completed
 True

I guess I could add a setter too.

But Django's QuerySet machinery doesn't really tie in with the ORM Python classes until the last step so you can't use these property methods in your filtering/excluding. What I want to do is to be able to do this:

 >>> from myapp.models import MyModel
 >>> completed_instances = MyModel.objects.filter(completed=True)
 >>> incomplete_instances = MyModel.objects.filter(completed=False)

To be able to do that I had to add special manager which is sensitive to the parameters it gets and changes them on the fly. So, the manager plus model now looks like this:

 class SpecialManager(models.Manager):
    """turn certain booleanesque parameters into date parameters"""

    def filter(self, *args, **kwargs):
        self.__transform_kwargs(kwargs)
        return super(SpecialManager, self).filter(*args, **kwargs)

    def exclude(self, *args, **kwargs):
        self.__transform_kwargs(kwargs)
        return super(SpecialManager, self).exclude(*args, **kwargs)

    def __transform_kwargs(self, kwargs):
        bool_name, date_name = 'completed', 'completed_date'
        for key, value in kwargs.items():
            if bool_name == key or key.startswith('%s__' % bool_name):
                if kwargs.pop(key):
                    kwargs['%s__lte' % date_name] = datetime.now()
                else:
                    kwargs[date_name] = None

 class MyModel(models.Model):
    completed_date = models.DateTimeField(null=True)

    @property
    def completed(self):
        return self.completed_date is not None

Now, that's fine but there's one problem. For the application in hand, we're relying on the admin interface a lot. Because of the handy @property decorator I set on the method completed() I now can't include completed into the admin's list_display so I have to do this special trick:

 class MyModelAdmin(admin.ModelAdmin):
    list_display = ('is_completed',)

    def is_completed(self, object_):
        return object_.completed
    is_completed.short_description = u'Completed?'
    is_completed.boolean = True

Now, I get the same nice effect in the admin view where this appears as a boolean. The information is still there about when it was completed if I need to extract that for other bits and pieces such as an advanced view or auditing. Pleased!

Now one last challenge with the Django admin interface was how to filter on these non-database-fields? It's been deliberately done so that you can't filter on methods but it's slowly changing and with some hope it'll be in Django 1.2. But I'm not interested in making my application depend on a patch to django.contrib but I really want to filter in the admin. We've already added some custom links and widgets to the admin interface.

After a lot of poking around and hacking together with my colleague Bruno Renié we came up with the following solution:

 class MyModelAdmin(admin.ModelAdmin):
    list_display = ('is_completed',)

    def is_completed(self, object_):
        return object_.completed
    is_arrived.short_description = u'Completed?'
    is_arrived.boolean = True

    def changelist_view(self, request, extra_context=None, **kwargs):
        from django.contrib.admin.views.main import ChangeList
        cl = ChangeList(request, self.model, list(self.list_display),
                        self.list_display_links, self.list_filter,
                        self.date_hierarchy, self.search_fields, 
                        self.list_select_related,
                        self.list_per_page,
                        self.list_editable, self)
        cl.formset = None

        if extra_context is None:
            extra_context = {}

        if kwargs.get('only_completed'):
            cl.result_list = cl.result_list.exclude(completed_date=None)
            extra_context['extra_filter'] = "Only completed ones"

        extra_context['cl'] = cl
        return super(SendinRequestAdmin, self).\
          changelist_view(request, extra_context=extra_context)

    def get_urls(self):
        from django.conf.urls.defaults import patterns, url
        urls = super(SendinRequestAdmin, self).get_urls()
        my_urls = patterns('',
                url(r'^only-completed/$', 
                    self.admin_site.admin_view(self.changelist_view),
                     {'only_completed':True}, name="changelist_view"),
        )
        return my_urls + urls

Granted, we're not getting the nice filter widget on the right hand side in the admin interface this time but it's good enough for me to be able to make a special link to /admin/myapp/mymodel/only-completed/ and it works just like a normal filter.

Ticket 5833 is quite busy and has been going on for a while. It feels a daunting task to dig in and contribute when so many people are already ahead of me. By writing this blog entry hopefully it will help other people who're hacking on their Django admin interfaces who, like me, hate booleans.

 

Older entriesOrder entries