Django admin widget for displaying movies

Recently I had to make a widget for Django admin to display movies (H.264 encoded, MPEG-4 and FLV). We already have a widget for displaying the movie inline within the admin change form, but I had to create a widget that opens separate entity (lightbox-like overlay or popup browser window). For a start I took the popup browser — this seemed easier because one of the requirements was that the movie player will not load with the page (I am not that good in javascript, mind you...). What it takes to have a widget that shows some content in separate browser window?

  • you can open window by manually appropriate javascript code, but I prefer to use some ready-made plugin for jQuery, like jquery-popupwindow;
  • you'll need Django view (and template) that displays actual page with embedded player;
  • to embed the player (which is Flash object) you'd need swfobject and the player itself, like JW Player (it plays both mp4 and flv movies — but watch out, it's free only for strictly non-commercial use);
  • a Django's forms widget that will have something that can be clicked to open new window.

Now for the fun part: actual code. The widget code is a mish-mash of my previous experiences in writing custom Django widgets:

class AdminPopupMovieWidget(forms.FileInput):

    class Media:
        js = (
            'js/jquery.js',
            'js/jquery.popupwindow.js',
        )

    def render(self, name, value, attrs=None):
        output = []
        if value and hasattr(value, 'url'):
            width = getattr(value, 'width', 600)
            height = getattr(value, 'height', 500)
            ctx = {
                'url': value.url,
                'href': '%s?url=%s' % (reverse('movie-show'), value.url),
                'class': 'popupwindow',
                'rel': 'width:%(width)d,height:%(height)d' % locals(),
            }
            output.append(u'<a href="%(href)s" class="%(class)s" rel="%(rel)s">%(url)s</a>' % ctx)
            output.append(u'<br />')
        output.append(super(AdminPopupMovieWidget, self).render(name, value, attrs))
        output.append('''<script type="text/javascript">
            $(".popupwindow").popupwindow();
        </script>''')
        return mark_safe(u''.join(output))

The code above produces a widget that has a clickable link with the title of movie URL. The anchor is linked to the URL of the view that produces HTML to display, like the code below:

@require_GET
def view_movie(request):
    movie_url = request.GET.get('url')
    if not movie_url:
        raise Http404
    ctx = {
        'url': movie_url,
    }
    return render_to_response('movie/view.html', ctx,
        context_instance=RequestContext(request))

The only variable that is passed in the context is the URL of the movie, but you are free to include as much as you'd like. Of course, nothing forces you to use urls, you can call the view with a PK to the movie-holding object, but this will require changes to the above view code.

But that's not all. You have to register your model in Django admin application. If you just register it with default modelform, the instances will display themselves with default admin widget. Two more classes are required:

class MovieAdminModelForm(forms.ModelForm):
    movie = forms.CharField(widget=widgets.AdminPopupMovieWidget)

    class Meta:
        model = Movie

class MovieAdmin(admin.ModelAdmin):
    form = MovieAdminModelForm

Is this all? Yes, I think.

Introducing django-confirmation

I'm pleased to announce my first (hopefully) reusable app for Django: django-confirmation. The idea for this app came from my personal need to handle confirming object's creation on one of my sites. I found few apps performing similar tasks, but both are targeting single classes of objects, while I needed more generic approach (I hate this word...) — there are several classes of objects that need to be confirmed separately but using the same mechanism.

The use case for this app is as follows:

  • non-registered/not-logged-in user creates an object;
  • application sends an email asking user to click (within configurable period of time) on provided link to make the object "active";
  • user clicks a link and makes the object "active" or
  • key expires after specified amount of time.

The implementation is based on what I found in other apps, specially django-registration and django-email-confirmation, but with added support for confirming generic Django model instances (as long as they can have different activity states, that is).

Django feeds (twisted way)

Yesterday on #django-newbie IRC channel somebody asked, if it is possible to create syndication feed that gets items filtered by some parameter, that does not come from the URL, but comes from the request instead. Barely remembering my previous work with feeds I replied that there has to be some way to achieve such effect by wrapping feed view in custom view and manipulate call parameters. After long thinking I came to following code (tested and working):

Here's a snippet of the basic urlconf module before applying any changes:

feeds = {
    'label': feeds.LatestEntriesByUser,
}
urlpatterns = patterns('django.contrib.syndication.views',
    (r'^feeds/(?P<url>.*)/$', 'feed', {'feed_dict': feeds}),
)

Using this urlconf, everything that is requested from the /feeds url, is being served by default syndication framework's view, but for the sake of completeness I'll wrap the default view with my own, customized view function. To achieve this, the above urlconf line must be changed to:

(r'^feeds/(?P<url>.*)/$', myapp.views.feeds, {'feed_dict': feeds}),

Then comes my view function in myapp.views:

from django.contrib.syndication.views import feed as orig_feed
def feeds(request, url, feed_dict):
    username = request.user.username
    url = '%s/%s' % (url, username)
    return orig_feed(request, url, feed_dict)

As shown above, I'm modifying the url parameter to contain an extra bit: the username I got from request. In the case of the feed 'articles' and username 'joe', the url that gets passed to built-in view will be /feeds/articles/joe/. So how do I handle this extra bit in my feed class?

Not surprisingly, this case is described in Django documentation (did I say the docs for Django are awesome?) - the feeds reference has a chapter on advanced feeds that covers the exactly identical case. Following this example, I'll add get_object method to my LatestEntriesByUser class:

def get_object(self, bits):
    return Entry.objects.get(user__username=bits[0])

Obvious? No. Easy? Yes. Documented? Yes!

Technikalia

  • XP-Dev.com: Free Subversion Hosting
  • A Django joint.
  • Python powered