1. Here’s a PDF of the slides and notes from my talk at Clepy: Ingredients for Building a DSL in Python.  Most of the content is in the notes, so zoom out if you don’t see them.  A few slides have accompanying code files, which I’ll get online later tonight.

    Here’s a PDF of the slides and notes from my talk at Clepy: Ingredients for Building a DSL in Python. Most of the content is in the notes, so zoom out if you don’t see them. A few slides have accompanying code files, which I’ll get online later tonight.

  2. Sounds like a bigger problem for you than me…

    Sounds like a bigger problem for you than me…

  3. Python talk at October Clepy meeting →

    This Monday (October 6th) at Clepy, I’ll be giving a talk about the ingredients for creating DSLs in Python. These ingredients include deferred expressions, the generative/builder pattern, metaclasses, and descriptors. See the meeting announcement for details.

  4. Distributing media with Django apps

    Reusable Django apps have a sore spot right now: media.

    Introduction

    Django takes a hands-off approach to your media files. Your project has two settings, MEDIA_URL (the base URL where your media is located) and MEDIA_ROOT (the filesystem path where media files are stored). The only thing it does with these is put FileField files under MEDIA_ROOT. The rest — collecting the required files, serving them up somehow, and ensuring that they’re loaded from MEDIA_URL in templates — is completely up to the developer. This mostly works out great, since many sites will have a dedicated media server, and developers probably know better than Django which media files should be loaded where. It isn’t perfect, however, because there is one thing Django could help with: collecting the required files.

    The problem

    Some Django apps — batchadmin, for example — distribute media (CSS, images, JavaScript) that is necessary (or recommended) for the app to work. Doing this is currently ad hoc and annoying for both the app developer and the app user.

    Since there is only one MEDIA_ROOT, the app’s files have to get in there somehow. How do they get there? Who knows, but the person installing the app has to do it manually. Okay, now the app should be able to access its media, right? Maybe. It depends, where did the user copy/link/move those files to, anyway? The app must now define its own settings so the user can tell it where they put the app’s media files in MEDIA_ROOT.

    The solution

    The approach I propose is hopefully the simplest thing that could possibly work. I think that approach is to just find all media files in the project’s INSTALLED_APPS and put them in MEDIA_ROOT. This idea has resulted in a collectmedia management command that can be run once by the user at installation or deployment time. This way, if you have apps laid out like:

    app1/media/
    app1/media/app1/
    app1/media/app1/css/style.css
    app1/media/app1/js/forms.js
    app2/media/
    app2/media/app2/
    app2/media/app2/css/fonts.css
    app2/media/app2/img/icon.png
    

    …then running collectmedia will let the apps reference their media like so (in a template, for example):

    {{ MEDIA_URL }}app1/css/style.css
    {{ MEDIA_URL }}app2/css/fonts.css
    

    Just like with reusable app templates, it should be best practice to make a subdirectory under media with the same name as the app. This way, app2 can override app1’s style by including a file with the path app2/media/app1/css/style.css. And just like with templates, when multiple apps provide a file with the same relative path, the app listed in INSTALLED_APPS first is selected.

    This approach avoids having Django try to do any sort of dynamic dispatching of media files, because that would eliminate the advantage of using a media server completely independent of Django.

    Speak up

    If you have ideas or opinions on this matter, and especially if you want to see something like this included in Django, check out the relevant thread on the django-developers mailing list.

  5. django-batchadmin: Batch actions in the admin change list

    I’m releasing a new project tonight: django-batchadmin. It looks like this:

    Django with batchadmin enabled

    Plenty of people have done this before with recipes or patches. But this is a project, and it’s distributed as a Django app, not with copy & paste or a diff. Also, since it’s being released post-1.0, it uses the latest and greatest in newforms-admin features.

    The app is very minimal but customizable. There’s only one action included in the app: delete. Refer to the Getting Started page to add your own actions.

    All functionality is added with a ModelAdmin subclass called BatchModelAdmin. This simply puts a checkbox in each row, specifies a change list template that wraps a form around the list, and makes the view accept POST requests, which it dispatches to the action code.

    Stay tuned for some custom actions.

  6. Texts Rasterization Exposures →

    As the guy who notices when a single pixel is off, I love this article about font rasterization. I’ve had it bookmarked for at least a year and sometimes read it just for fun.

  7. The New Yorker: I have to pay a lot of taxes living in New York City. What about you, are you a taxes guy?
    Chris Onstad: I pay almost all the major taxes, and I’m also part of a local program in my town where we pay experimental taxes to see if we get mad.

  8. Dumb little obscure languages →

    The level of complete misunderstanding here is absolutely hilarious.

  9. Menu buttons in 20 lines of jQuery

    This is my approach to menus using jQuery. I’ll go through it line by line to serve as a mini introduction to events in jQuery.

    Here’s a preview of what we’re making. The desired behavior:

    • Clicking on a button opens its menu
    • Clicking anywhere except inside the menu closes it

    I use two elements per menu: a button and a div. Clicking the button makes the div appear. The button’s name attribute marks the id of the div to open:

    <button name="the-menu" type="button" class="menu">...</button>
    <div id="the-menu" class="menu">...</div>
    

    Now the JavaScript to make it work. Each line is explained after the code:

    jQuery(function($) {
        $('button.menu').one('click', openMenu);
        function openMenu(e) {
            var button = $(this).addClass('active');
            var menu = $('#' + button.attr('name'));
            var offset = button.offset();
            var h = (button.outerHeight) ? button.outerHeight() : button.height();
            menu.addClass('active').css({
                'top': offset.top + h, 'left': offset.left
            }).click(function(e) { e.stopPropagation(); }).show(200, function() {
                $(document).one('click', {button: button, menu: menu}, closeMenu);
            });
        }
        function closeMenu(e) {
            e.data.menu.removeClass('active').hide(100, function() {
                e.data.button.removeClass('active');
            });
            e.data.button.one('click', openMenu);
        }
    });
    

    Here we go:

    jQuery(function($) {
    

    jQuery is an alias for $(document).ready. The callback is passed the jQuery object as an argument, so you can name it whatever you want. This is good practice for getting along with other JavaScript libraries. Name it $ here and it’ll work in the callback even if another library uses $.

        $('button.menu').one('click', openMenu);
    

    Attach the click event handler openMenu to all buttons with class “menu”. one is used to only respond to this event for one click. Why? Because the next click should close the menu, not open it. Why not use toggle? Because the menu should close when clicking on the document too, not just on the button, and this could put the toggle status out of sync.

        function openMenu(e) {
            var button = $(this).addClass('active');
    

    Define the openMenu callback, get the clicked button, and add a class for styling purposes.

            var menu = $('#' + button.attr('name'));
    

    Retrieve the div with the same id as the button’s name.

            var offset = button.offset();
            var h = (button.outerHeight) ? button.outerHeight() : button.height();
    

    Get the button’s position and size, because the menu will appear under it. outerHeight is used if available, provided by the Dimensions plugin, to account for borders and such.

            menu.addClass('active').css({
                'top': offset.top + h, 'left': offset.left
    

    Add a class to the menu for styling purposes, then align it to the left and bottom of the button.

            }).click(function(e) { e.stopPropagation(); }).show(200, function() {
                $(document).one('click', {button: button, menu: menu}, closeMenu);
            });
    

    Set a click handler on the menu that stops the click event from bubbling up to the document. Why? Because after the menu appears using the show method, we attach a click event to the whole document that closes the menu. If the event were allowed to propagate, clicking within the menu would close it, something you don’t necessarily always want. Notice that the call to one passes an object along with the event handler. This will be available as e.data in closeMenu.

        }
        function closeMenu(e) {
            e.data.menu.removeClass('active').hide(100, function() {
                e.data.button.removeClass('active');
            });
    

    Now define the closeMenu callback. It removes the “active” class from the menu, hides it, and also removes the “active” class from the button.

            e.data.button.one('click', openMenu);
        }
    });
    

    Finally, attach the openMenu callback to the button’s click event handler again. Remember, one is only good for one occurrence of the event.

    That’s it! Here’s the final result again. See menu.css for the necessary bits of CSS and how I made everything pretty.

  10. Rendering search results in Django

    This weekend I wrote a Django tag & filter module for rendering search results. The primary feature is truncating a body of text to show the search terms in context, just like Google shows you an excerpt. A decent text indexer will normally provide this, but most of my projects aren’t big enough to justify setting one up, so I make do with normal database queries.

    Examples and documentation are on the snippet page, but here’s how I actually use it in my latest project. I have some flatpages storing documentation and other help for the web site, and a search form just for these pages. The following code can practically be dropped into any Django project using flatpages.

    The view looks like this:

    from django.contrib.flatpages.models import FlatPage
    from django.db.models import Q
    
    def help_search(request):
        query_string = request.GET.get('q', "")
        search_terms = query_string.split()
        results = FlatPage.objects.filter(url__startswith="/help/")
    
        if search_terms:
            query = Q()
            for term in search_terms:
                query &= Q(content__icontains=term) | Q(title__icontains=term)
            results = results.filter(query)
    
        return render_to_response("flatpages/search.html",
            {'query': query_string, 'terms': search_terms, 'results': results}
        )
    

    And the relevant portion of flatpages/search.html looks like this:

    {% load search %}
    
    <h1>Search Results</h1>
    <h2>
        {% with results|length as result_count %}
        {{ result_count|default:"No" }} page{{ result_count|pluralize }} found
        {% if terms %}for &ldquo;{{ query }}&rdquo;{% endif %}
        {% endwith %}
    </h2>
    <ul id="results">
        {% for page in results %}
        <li>
            {% searchexcerpt terms 6 as content %} {# set `content` object #}
                {{ page.content|striptags }}
            {% endsearchexcerpt %}
            {% highlight terms as title %} {# set `title` object #}
                {{ page.title }}
            {% endhighlight %}
            <h3>
                <a href="{{ page.get_absolute_url }}">{{ title.highlighted }}</a>
                {% if terms %}{% with content.hits|add:title.hits as hits %}
                <span class="count">{{ hits }} hit{{ hits|pluralize }}</span>
                {% endwith %}{% endif %}
            </h3>
            <p class="context">{{ content.excerpt|highlight:terms }}</p>
        </li>
        {% endfor %}
    </ul>
    

    Finally, here are some screenshots of the results:

    Search screenshot 1Search screenshot 2Search screenshot 3

Vignelli theme by Robbie Manson