Django-cms-saq的测试版本为2.4.x.我正在尝试更新程序以使用3.0.X.
到目前为止,我已经更新了所有导入,但遇到了一个不寻常的错误.当我向页面添加问题(插件)并点击发布时,它会在数据库中创建问题的两个副本(可通过管理站点查看).删除任一副本都会从发布的页面中删除这两个副本,但会将问题保留在编辑模式下
我怎么去拍这个呢?
我将在这里包含一些文件.如果您需要任何其他文件,请告诉我.
请注意,我正在尝试添加多项选择题.
来自models.py:
from django.db import models
from django.db.models import Max, Sum
from cms.models import CMSPlugin, Page, Placeholder
from cms.models.fields import PageField
from taggit.managers import TaggableManager
from djangocms_text_ckeditor.models import AbstractText
...
class Question(CMSPlugin):
QUESTION_TYPES = [
('S', 'Single-choice question'),
('M', 'Multi-choice question'),
('F', 'Free-text question'),
]
slug = models.SlugField(
help_text="A slug for identifying answers to this specific question "
"(allows multiple only for multiple languages)")
tags = TaggableManager(blank=True)
label = models.CharField(max_length=512, blank=True)
help_text = models.CharField(max_length=512, blank=True)
question_type = models.CharField(max_length=1, choices=QUESTION_TYPES)
optional = models.BooleanField(
default=False,
help_text="Only applies to free text questions",
)
depends_on_answer = models.ForeignKey(
Answer, null=True, blank=True, related_name='trigger_questions')
def copy_relations(self, oldinstance):
for answer in oldinstance.answers.all():
answer.pk = None
answer.question = self
answer.save()
self.depends_on_answer = oldinstance.depends_on_answer
@staticmethod
def all_in_tree(page):
root = page.get_root()
# Remember that there might be questions on the root page as well!
tree = root.get_descendants() | Page.objects.filter(id=root.id)
placeholders = Placeholder.objects.filter(page__in=tree)
return Question.objects.filter(placeholder__in=placeholders)
@staticmethod
def all_in_page(page):
placeholders = Placeholder.objects.filter(page=page)
return Question.objects.filter(placeholder__in=placeholders)
def score(self, answers):
if self.question_type == 'F':
return 0
elif self.question_type == 'S':
return self.answers.get(slug=answers).score
elif self.question_type == 'M':
answers_list = answers.split(',')
return sum([self.answers.get(slug=a).score for a in answers_list])
@property
def max_score(self):
if not hasattr(self, '_max_score'):
if self.question_type == "S":
self._max_score = self.answers.aggregate(
Max('score'))['score__max']
elif self.question_type == "M":
self._max_score = self.answers.aggregate(
Sum('score'))['score__sum']
else:
self._max_score = None # don't score free-text answers
return self._max_score
def percent_score_for_user(self, user):
if self.max_score:
try:
score = Submission.objects.get(
question=self.slug,
user=user,
).score
except Submission.DoesNotExist:
return 0
return 100.0 * score / self.max_score
else:
return None
def __unicode__(self):
return self.slug
...
来自cms_plugins.py
import itertools
import operator
from django.contrib import admin
from django.utils.translation import ugettext as _
from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool
from cms_saq.models import Question, Answer, GroupedAnswer, Submission, \
FormNav, ProgressBar, SectionedScoring, ScoreSection, BulkAnswer, \
QuestionnaireText, SubmissionSetReview
from djangocms_text_ckeditor.cms_plugins import TextPlugin
from djangocms_text_ckeditor.models import Text
from bs4 import BeautifulSoup
...
class QuestionPlugin(CMSPluginBase):
model = Question
module = "SAQ"
inlines = [AnswerAdmin]
exclude = ('question_type',)
def render(self, context, instance, placeholder):
user = context['request'].user
submission_set = None
triggered = True
depends_on = None
if instance.depends_on_answer:
depends_on = instance.depends_on_answer.pk
try:
Submission.objects.get(
user=user,
question=instance.depends_on_answer.question.slug,
answer=instance.depends_on_answer.slug,
submission_set=submission_set,
)
triggered = True
except:
triggered = False
extra = {
'question': instance,
'answers': instance.answers.all(),
'triggered': triggered,
'depends_on': depends_on,
}
if user.is_authenticated():
try:
extra['submission'] = Submission.objects.get(
user=user,
question=instance.slug,
submission_set=submission_set,
)
except Submission.DoesNotExist:
pass
context.update(extra)
return context
def save_model(self, request, obj, form, change):
obj.question_type = self.question_type
super(QuestionPlugin, self).save_model(request, obj, form, change)
...
class MultiChoiceQuestionPlugin(QuestionPlugin):
name = "Multi Choice Question"
render_template = "cms_saq/multi_choice_question.html"
question_type = "M"
exclude = ('question_type', 'help_text')
...
plugin_pool.register_plugin(SingleChoiceQuestionPlugin)
plugin_pool.register_plugin(MultiChoiceQuestionPlugin)
plugin_pool.register_plugin(DropDownQuestionPlugin)
plugin_pool.register_plugin(GroupedDropDownQuestionPlugin)
plugin_pool.register_plugin(FreeTextQuestionPlugin)
plugin_pool.register_plugin(FreeNumberQuestionPlugin)
plugin_pool.register_plugin(FormNavPlugin)
plugin_pool.register_plugin(SubmissionSetReviewPlugin)
plugin_pool.register_plugin(SectionedScoringPlugin)
plugin_pool.register_plugin(ProgressBarPlugin)
plugin_pool.register_plugin(BulkAnswerPlugin)
plugin_pool.register_plugin(SessionDefinition)
plugin_pool.register_plugin(QuestionnaireTextPlugin)
plugin_pool.register_plugin(TranslatedTextPlugin)
来自cms_app.py:
from cms.app_base import CMSApp
from cms.apphook_pool import apphook_pool
from django.utils.translation import ugettext_lazy as _
class CMSSaq(CMSApp):
name = _("Self Assessment")
urls = ["cms_saq.urls"]
apphook_pool.register(CMSSaq)
附加信息:
当通过其slug Question.objects.get(slug = question_slug)尝试获取问题对象时,这种重复观察会产生问题.这样的查询应该只返回一个问题.我们得到的是两个问题.
import re
from django.http import HttpResponse, HttpResponseBadRequest
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_POST, require_GET
from django.views.decorators.cache import never_cache
from django.utils import simplejson, datastructures
from django.conf import settings
from cms_saq.models import Question, Answer, Submission, SubmissionSet
ANSWER_RE = re.compile(r'^[\w-]+(,[\w-]+)*$')
@require_POST
def _submit(request):
post_data = datastructures.MultiValueDict(request.POST)
submission_set_tag = post_data.pop('submission_set_tag', '')
for question_slug, answers in post_data.iteritems():
# validate the question
try:
question = Question.objects.get(
slug=question_slug,
#placeholder__page__publisher_is_draft=False,
)
except Question.DoesNotExist:
return HttpResponseBadRequest(
"Invalid question '%s'" % question_slug,
)
# check answers is a list of slugs
if question.question_type != 'F' and not ANSWER_RE.match(answers):
return HttpResponseBadRequest("Invalid answers: %s" % answers)
# validate and score the answer
try:
score = question.score(answers)
except Answer.DoesNotExist:
return HttpResponseBadRequest(
"Invalid answer '%s:%s'" % (question_slug, answers)
)
# save, but don't update submissions belonging to an existing set
filter_attrs = {
'user': request.user.id,
'question': question_slug,
'submission_set': None,
}
attrs = {'answer': answers, 'score': score}
rows = Submission.objects.filter(**filter_attrs).update(**attrs)
if not rows:
attrs.update(filter_attrs)
Submission.objects.create(**attrs)
# Create submission set if requested
if submission_set_tag:
submission_set_tag = submission_set_tag[0]
if submission_set_tag:
_create_submission_set(
request, submission_set_tag
)
return HttpResponse("OK")
最佳答案 如果您在CMS中创建一个页面并向其添加插件,一旦您点击发布,CMS就会创建这些插件的副本,就像它们在那个时间点一样.这为页面提供了实时版本,然后您可以对其进行更改,这些更改将保留在草稿模式中,直到您再次点击发布.
这使您可以拥有网站的草稿版本,在该版本中可以进行编辑/更改并最终确定,然后再将其公开.
因此,为每个插件查看两个副本不是问题.
另外,如果您正在开发CMS网站,我强烈建议您加入Google Plus上的CMS用户组. https://plus.google.com/communities/107689498573071376044
更新
好的,所以CMS中的插件附加到页面上的占位符,它是具有两个版本的页面.因为每个插件都附加到页面,所以您可以使用该关系来过滤插件.
如果您使用admin注册您的插件,或者对其进行对象调用,您只需查看表中存储的内容,正如我所说,它包含插件的草稿和实时版本.
所以,当你查询你的插件时,这样做;
questions = Question.objects.filter(placeholder__page__publisher_is_draft=True)
这将为您提供草稿页面附带的所有问题.
唯一的缺点是插件保证附加到Page()对象,除非插件设置为page_only = True但我只对附加到页面的插件感兴趣,所以它对我有用.有关详细信息,请参阅docs.
此外,如果您要将CMS页面链接添加到任何插件,请不要忘记在模型字段中添加类似的参数,以确保仅将页面对象限制为草稿;
page_link = models.ForeignKey(
Page,
limit_choices_to={'publisher_is_draft': True},
help_text=_("Link to another page on the site."),
on_delete=models.SET_NULL,
related_name='myapp_page_link'
)