我刚刚碰到你的问题。我最近为几个项目实现了类似的东西,这是我根据 Adam Alton 在这里描述的工作所做的:http: //adamalton.co.uk/blog/displaying-django-genericforeignkey-as-single-form -场地/
然而,这不是一个 CMSPlugin,所以,我知道这并不能直接回答这个问题,但它是我所拥有的,我希望它可以帮助其他人寻找类似的解决方案。
作为概述,我在我的模型中定义了一个“横幅”类型,它代表了我客户网站首页上的横幅。每个横幅都可以链接到其他内容。在这些情况下,链接目标可能是 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 中使用应该不会太难。
编辑:实际上,我刚刚实现了与 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)