3

私は Django 1.4 と Django-CMS 2.3 を使用しています。

apphooks を備えたいくつかのアプリケーションがあり、django-CMS でアプリケーションのオブジェクトを参照できるようにしたいと考えています。

持続可能なリンクを作成するために、django-CMS メニューまたは一般的な外部キーを使用するプラグインを見つけようとしています。

このようなものが存在しない場合、最適な一般的な外部キー アプリケーションは何ですか? メソッドを使用してモデルの選択を制限する django.contrib.contenttypes を使用して小さなプラグインを作成しましたget_absolute_urlが、それはひどいものです。

ありがとう。

4

1 に答える 1

3

たまたまあなたの質問に出くわしました。私は最近、いくつかのプロジェクトに同様のものを実装しました。ここで説明されている Adam Alton の作業に基づいて私が行ったことは次のとおりです。 -分野/

ただし、これはCMSPluginではないため、これが質問に直接答えないことはわかっていますが、私が持っているものであり、同様のソリューションを探している他の人に役立つことを願っています.

概要として、顧客のサイトのフロント ページのバナーを表す "Banner" タイプをモデルで定義しています。各バナーは他のコンテンツにリンクできます。このような場合、リンク先は Django-CMS ページ、または他の多くのタイプのいずれかになります。すべてに get_absolute_url メソッドが定義されていますが、これを決定するためにイントロスペクションを使用していません。ここに表示されるすべてのタイプに get_absolute_url を実装しただけです。とにかく、ここに行きます:

まず、Banner の単純なモデルを次に示します。

class Banner(models.Model):
  short_name       = models.CharField(max_length=64, unique=True)
  html             = models.TextField()
  link_text        = models.CharField(max_length=128, default='Learn more')
  destination_type = models.ForeignKey(ContentType, null=True, blank=True,
    limit_choices_to={"model__in": ("Page", "Project", "Person", "Client")}
  )
  destination_id   = models.PositiveIntegerField(null=True, blank=True)
  destination      = generic.GenericForeignKey('destination_type', 'destination_id')
  published        = models.BooleanField(blank=True, default=False)

  def __unicode__(self):
    return self.short_name

これが私のforms.pyです:

import re
from django.forms import ModelForm, ChoiceField
from cms.models import Page
from django.contrib.contenttypes.models import ContentType

from apps.your_application.models import Project, Person, Client

class BannerAdminForm(ModelForm):
  class Meta:
    model = Banner
    fields = ("short_name", "html", "link_text", "destination", "link_hash", "published",)

  # GenericForeignKey form field, will hold combined object_type and object_id
  destination = ChoiceField(required=False)  # Note the 'required=False' here.

  def __init__(self, *args, **kwargs):
    super(BannerAdminForm, self).__init__(*args, **kwargs)

    # Combine object_type and object_id into a single 'destination' field
    # Get all the objects that we want the user to be able to choose from

    # Note: The user is going to locate these by name, so we should
    # alphabetize all of these

    available_objects  = list(Page.objects.all().order_by('title_set__title'))
    available_objects += list(Project.objects.all().order_by('title'))
    available_objects += list(Person.objects.all().order_by('name'))
    available_objects += list(Client.objects.all().order_by('name'))

    # Now create our list of choices for the <select> field
    object_choices = []
    object_choices.append(["", "--"])
    for obj in available_objects:
      type_class = ContentType.objects.get_for_model(obj.__class__)
      type_id = type_class.id
      obj_id = obj.id
      form_value = "type:%s-id:%s" % (type_id, obj_id)  # e.g."type:12-id:3"
      display_text = "%s : %s" % (str(type_class), str(obj))  # E.g. "Client : Apple, Inc."
      object_choices.append([form_value, display_text])

    self.fields['destination'].choices = object_choices

    # If there is an existing value, pre-select it
    if self.instance.destination:
      type_class = ContentType.objects.get_for_model(self.instance.destination.__class__)
      type_id = type_class.id
      obj_id = self.instance.destination.id
      current_value = "type:%s-id:%s" % (type_id, obj_id)
      self.fields['destination'].initial = current_value

  def save(self, *args, **kwargs):
    try:
      #get object_type and object_id values from combined destination field
      object_string = self.cleaned_data['destination']
      matches = re.match("type:(\d+)-id:(\d+)", object_string).groups()
      object_type_id = matches[0]  # get 45 from "type:45-id:38"
      object_id = matches[1]       # get 38 from "type:45-id:38"
      object_type = ContentType.objects.get(id=object_type_id)
      self.cleaned_data['destination_type'] = object_type_id
      self.cleaned_data['destination_id'] = object_id
      self.instance.destination_id = object_id
      self.instance.destination_type = object_type
    except:
      # If anything goes wrong, leave it blank,
      # This is also the case for when '--' is chosen
      # In the drop-down (tsk, tsk, bad code style =/)
      self.cleaned_data['destination_type'] = None
      self.cleaned_data['destination_id'] = None
      self.instance.destination_id = None
      self.instance.destination_type = None

    return super(BannerAdminForm, self).save(*args, **kwargs)

{% if banner.destination %}{{ banner.destination.get_absolute_url }}{% endif %} その後、テンプレートを呼び出して、宛先オブジェクトの URL を取得でき ます。

うまく機能し、CMSPlugin で使用するのが難しくないはずです。

これは、Django Admin でどのように見えるかです

編集: 実際、CMSPlugin フォームとまったく同じものを実装しました。本質的にゼロの違いがあります。次のように、cms_plugins.py ファイルのプラグイン クラスにフォームを追加することを忘れないでください。

class CMSBannerPlugin(CMSPluginBase):
  form = BannerAdminForm  #   <==== Don't forget this part
  model = Banner
  name = _("Banner Plugin")
  render_template = "apps/your_application/_banner.html"

  def render(self, context, instance, placeholder):
    ...
    return context

plugin_pool.register_plugin(CMSBannerPlugin)
于 2012-11-09T23:39:56.413 に答える