11

シリアル化のためにすべてのモデルにスラッグを追加しているので、 django_autoslug の AutoSlugField を使用する抽象基本クラスを定義しました。

class SluggerModel(models.Model):
    slug = AutoSlugField(unique=True, db_index=False) 

    class Meta:
        abstract=True

また、カスタム マネージャーと natural_key メソッドを定義しました。この時点で約 20 の子クラスがあるため、フィールドを定義する 1 行だけでなく、抽象基本モデルを使用する価値のあることがいくつかあります。

AutoSlugFieldただし、抽象基本クラスを引き続き利用しながら、子モデルの一部を初期化するためのデフォルト引数のいくつかを切り替えられるようにしたいと考えています。たとえばpopulate_from、特定のモデルからフィールドを指定してオプションを利用する人もいればdb_index=True、私のデフォルト ( False) の代わりに使用する人もいます。

各子モデルの内部メタ クラスで定義されたカスタム オプションを利用して、カスタム メタクラスでこれを実行しようとしましたが、それはネズミの巣になります。そのアプローチに関するガイダンスやその他の提案をお待ちしています。

4

2 に答える 2

13

1 つの解決策は、抽象基本クラスを動的に構築することです。例えば:

def get_slugger_model(**slug_kwargs):
    defaults = {
        'unique': True,
        'db_index': False
    }
    defaults.update(slug_kwargs)

    class MySluggerModel(models.Model):
        slug = AutoSlugField(**defaults)

        class Meta:
            abstract = True

    return MySluggerModel


class MyModel(get_slugger_model()):
    pass


class MyModel2(get_slugger_model(populate_from='name')):
    name = models.CharField(max_length=20)
于 2013-03-02T20:42:27.330 に答える
0

更新:私は醜い次の解決策から始め、ダニエルの解決策に切り替えましたが、そうではありません。参考までに私のものをここに残しておきます。

これは、動作しているように見える私の Metaclass ラット トラップです (まだ大規模なテストは行っていません)。

class SluggerMetaclass(ModelBase):
    """
    Metaclass hack that provides for being able to define 'slug_from' and 
    'slug_db_index' in the Meta inner class of children of SluggerModel in order to set 
    those properties on the AutoSlugField
    """
    def __new__(cls, name, bases, attrs):
        # We don't want to add this to the SluggerModel class itself, only its children
        if name != 'SluggerModel' and SluggerModel in bases:
            _Meta = attrs.get('Meta', None)
            if _Meta and hasattr(_Meta, 'slug_from') or hasattr(_Meta, 'slug_db_index'):
                attrs['slug'] = AutoSlugField(
                    populate_from=getattr(_Meta, 'slug_from', None),
                    db_index=getattr(_Meta, 'slug_db_index', False),
                    unique=True
                )
                try:
                    # ModelBase will reject unknown stuff in Meta, so clear it out before calling super
                    delattr(_Meta, 'slug_from')
                except AttributeError: 
                    pass
                try:
                    delattr(_Meta, 'slug_db_index')
                except AttributeError: 
                    pass
            else:
                attrs['slug'] = AutoSlugField(unique=True, db_index = False)  # default

        return super(SlugSerializableMetaclass, cls).__new__(cls, name, bases, attrs)

基本的には次のSlugModelようになります。

class SluggerModel(models.Model):
    __metaclass__ = SluggerMetaclass
    objects = SluggerManager()
    # I don't define the AutoSlugField here because the metaclass will add it to the child class.
    class Meta:
        abstract = True

そして、次の方法で目的の効果を達成できます。

class SomeModel(SluggerModel, BaseModel):
    name = CharField(...)
    class Meta:
        slug_from = 'name'
        slug_db_index = True

複数の抽象親モデルを持つモデルの継承リストの最初にSluggerModel を配置する必要があります。そうしないと、フィールドが他の親モデルによって取得されず、検証が失敗します。しかし、私はその理由を解読できませんでした。

うまくいくので、これを自分の質問への答えにすることができると思いますが、少し醜い側にあるので、より良い方法を望んでいます。繰り返しになりますが、hax は hax です。何ができるのでしょうか。これが答えかもしれません。

于 2013-03-12T04:58:08.310 に答える