ManyToManyFieldをちょっとdel.icio.usっぽく

endless 氏と、

DjangoManyToManyFielddel.icio.us っぽくしたいね。」

という話になり色々と試行錯誤してみたよ。

そのまま as_text とかやると、期待した通りに動かないため、ソースと睨めっこしながら一応形になった。

なので、とりあえずメモ。

models.py はこんな感じ。

# -*- coding: utf-8 -*-

from datetime import datetime
from django.db import models


class Tag(models.Model):
    """
    ブックマークのタグ。
    """
    label = models.SlugField(_('Label'), maxlength=50, default=None)

    class Meta:
        pass

    class Admin:
        pass

    def __str__(self):
        return self.label

    def get_absolute_url(self):
        """
        インスタンスに対してのユニークなURLを返す。
        """
        return '/bookmark/tag/%s/' % self.label


class Entry(models.Model):
    """
    ブックマーク。
    """
    title       = models.CharField(_('Title'), maxlength=100, default=None)
    url         = models.URLField(_('Url'), default=None)
    description = models.TextField(_('Description'), default=None)
    created_at  = models.DateTimeField(_('Created at'), editable=False, default=datetime.now)
    updated_at  = models.DateTimeField(_('Updated at'), editable=False)
    is_active   = models.BooleanField(_('Is Active'), editable=False, default=True)
    tags        = models.ManyToManyField(Tag)

    class Meta:
        pass

    class Admin:
        list_display = ('title', 'url', 'created_at', 'updated_at', 'is_active')

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        """
        インスタンスに対してのユニークなURLを返す。
        """
        return self.url

    def save(self):
        """
        データを登録する。
        """
        self.updated_at = datetime.now()
        super(Entry, self).save()

    def delete(self):
        """
        論理削除の為のオーバーライド。
        """
        # Turn off Activation flag.
        self.is_active = False
        self.save()

で、同階層に作った forms.py がコレ。

# -*- coding: utf-8 -*-

from django.db.models.query import QuerySet
from django import newforms as forms
from tested.bookmark.models import Tag

EMPTY_VALUES = (None, '',)
ENTRY_FORMFIELDS = {}

class MultiTagInput(forms.TextInput):
    """
    ManyToMany2Charなウィジェット。
    """

    def render(self, name, value, attrs=None):
        if isinstance(value, QuerySet):
            value = ' '.join([t.label for t in value])
        return super(MultiTagInput, self).render(name, value, attrs)


class MultiTagField(forms.Field):
    """
    ManyToMany2Charなフィールド。逆もしかり。
    """

    def __init__(self, **kwargs):
        kwargs.update({
            'widget' : MultiTagInput(),
        })
        super(MultiTagField, self).__init__(**kwargs)


    def clean(self, value):
        super(MultiTagField, self).clean(value)
        values = []
        for label in value.replace('\xe3\x80\x80', ' ').split(' '):
            if len(label):
                tag, created = Tag.objects.get_or_create(label=label)
                values.append(tag.id)

        if not len(values):
            raise forms.ValidationError(_('This field is required.'))
        return values


ENTRY_FORMFIELDS['tags'] = MultiTagField

def entry_form_callback(f, **kwargs):
    """
    bookmark.Entry用のformfield_callback.
    """
    try:
        # セットされてればそれを使う。
        return ENTRY_FORMFIELDS[f.name](**kwargs)
    except:
        # 無ければデフォルト。
        return f.formfield(**kwargs)

これで views.py とかで、

f = newforms.form_for_model(Entry, formfield_callback=entry_form_callback)

とかすると、テンプレートでは input@type=text になっております。果たしてコレで合ってるのかは解りませんが。

手練の方からの突っ込みをお待ちしております!

Posted at: 
2007/08/03 02:22:55
0 Comments
0 TrackBacks
Tags: 
Django
Python
Trackback: 
http://humming.via-kitchen.com/2007/08/03/manytomanyfield-2-delicious/trackback/

TrackBacks

まだ登録されていません。

Comments

まだ登録されていません。

Add Comment

Add Comment