- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
在来这里寻求建议之前,我总是确保我已经尝试了所有可能的途径。
也就是说,这就是我目前正在努力解决的问题;创建多级/嵌套类别。顺便说一句,如果 wagtail 核心开发人员能够实现一种创建多级类别的简单方法,而无需我们为其编写一些 vanilla-django hack,那就太好了。
我已经在这个应用程序上工作了几个星期,一切运行顺利,除了现在,有一个要实现嵌套类别的业务决策。
我最初的 M.O 是创建一个 ServiceCategoryIndex 页面,一个 ServiceCategoryPage,然后使 ServiceIndex 页面成为 ServiceCategoryIndex 页面的后代或可订购的 ServiceCategoryPage,这似乎不对。
经过几次迭代,我回到我的默认模型,然后尝试使用 View 和 url 的类别 url,如 vanilla-django,问题是,我无法通过直通查询外键模板上的关系,所以我仍然无法将服务页面的内容作为列表查询集呈现出来。
下面是我的模型代码,对此的任何建议或解决方法都绝对有帮助。 P.S:我几乎要用 vanilla-django 重写整个项目,因为我在接下来的几天内找不到解决方案。
def get_service_context(context):
context['all_categories'] = ServiceCategory.objects.all()
context['root_categories'] = ServiceCategory.objects.filter(
parent=None,
).prefetch_related(
'children',
).annotate(
service_count=Count('servicepage'),
)
return context
class ServiceIndexPage(Page):
header_image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
heading = models.CharField(max_length=500, null=True, blank=True)
sub_heading = models.CharField(max_length=500, null=True, blank=True)
body = RichTextField(null=True, blank=True)
def get_context(self, request, category=None, *args, **kwargs):
context = super(ServiceIndexPage, self).get_context(request, *args, **kwargs)
services = ServicePage.objects.child_of(self).live().order_by('-first_published_at').prefetch_related('categories', 'categories__category')
if category is None:
if request.GET.get('category'):
category = get_object_or_404(ServiceCategory, slug=request.GET.get('category'))
if category:
if not request.GET.get('category'):
category = get_object_or_404(ServiceCategory, slug=category)
services = services.filter(categories__category__name=category)
# Pagination
page = request.GET.get('page')
page_size = 10
if hasattr(settings, 'SERVICE_PAGINATION_PER_PAGE'):
page_size = settings.SERVICE_PAGINATION_PER_PAGE
if page_size is not None:
paginator = Paginator(services, page_size) # Show 10 services per page
try:
services = paginator.page(page)
except PageNotAnInteger:
services = paginator.page(1)
except EmptyPage:
services = paginator.page(paginator.num_pages)
context['services'] = services
context['category'] = category
context = get_service_context(context)
return context
@register_snippet
class ServiceCategory(models.Model):
name = models.CharField(max_length=250, unique=True, verbose_name=_('Category Name'))
slug = models.SlugField(unique=True, max_length=250)
parent = models.ForeignKey('self', blank=True, null=True, related_name="children")
date = models.DateField(auto_now_add=True, auto_now=False, null=True, blank=True)
description = RichTextField(blank=True)
class Meta:
ordering = ['-date']
verbose_name = _("Service Category")
verbose_name_plural = _("Service Categories")
panels = [
FieldPanel('name'),
FieldPanel('parent'),
FieldPanel('description'),
]
def __str__(self):
return self.name
def clean(self):
if self.parent:
parent = self.parent
if self.parent == self:
raise ValidationError('Parent category cannot be self.')
if parent.parent and parent.parent == self:
raise ValidationError('Cannot have circular Parents.')
def save(self, *args, **kwargs):
if not self.slug:
slug = slugify(self.name)
count = ServiceCategory.objects.filter(slug=slug).count()
if count > 0:
slug = '{}-{}'.format(slug, count)
self.slug = slug
return super(ServiceCategory, self).save(*args, **kwargs)
class ServiceCategoryServicePage(models.Model):
category = models.ForeignKey(ServiceCategory, related_name="+", verbose_name=_('Category'))
page = ParentalKey('ServicePage', related_name='categories')
panels = [
FieldPanel('category'),
]
class ServicePage(Page):
header_image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
verbose_name=_('Header image')
)
service_title = models.CharField(max_length=300, null=True, blank=True)
body = StreamField([
('h1', CharBlock(icon="title", classanme="title")),
('h2', CharBlock(icon="title", classanme="title")),
('h3', CharBlock(icon="title", classanme="title")),
('h4', CharBlock(icon="title", classanme="title")),
('h5', CharBlock(icon="title", classanme="title")),
('h6', CharBlock(icon="title", classanme="title")),
('paragraph', RichTextBlock(icon="pilcrow")),
('aligned_image', ImageBlock(label="Aligned image", icon="image")),
('pullquote', PullQuoteBlock()),
('raw_html', RawHTMLBlock(label='Raw HTML', icon="code")),
('embed', EmbedBlock(icon="code")),
])
date = models.DateField("Post date")
service_categories = models.ManyToManyField(ServiceCategory, through=ServiceCategoryServicePage, blank=True)
feed_image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
verbose_name=_('Feed image')
)
search_fields = Page.search_fields + [
index.SearchField('body'),
index.SearchField('service_title'),
index.SearchField('title'),]
def get_absolute_url(self):
return self.url
def get_service_index(self):
# Find closest ancestor which is a service index
return self.get_ancestors().type(ServiceIndexPage).last()
def get_context(self, request, *args, **kwargs):
context = super(ServicePage, self).get_context(request, *args, **kwargs)
context['services'] = self.get_service_index().serviceindexpage
context = get_service_context(context)
return context
class Meta:
verbose_name = _('Service page')
verbose_name_plural = _('Services pages')
parent_page_types = ['services.ServiceIndexPage']
ServicePage.content_panels = [
FieldPanel('title', classname="full title"),
FieldPanel('service_title'),
ImageChooserPanel('header_image'),
FieldPanel('date'),
InlinePanel('categories', label=_("Categories")),
StreamFieldPanel('body'),
ImageChooserPanel('feed_image'),
]
最佳答案
我一直在研究类似的问题 - 除了我们将它们称为 Topic
而不是 Category
但希望这对您有所帮助。
get_children
或 is_root
之类的东西。base_form_class
override 完成。 .我有一个专用的 Topics
应用程序,但您可以将它放在任何 models.py 中。请参阅解释代码的注释。
from __future__ import unicode_literals
from django import forms
from django.core.exceptions import PermissionDenied
from django.db import models
from treebeard.mp_tree import MP_Node
from wagtail.contrib.modeladmin.options import ModelAdmin
from wagtail.wagtailadmin.edit_handlers import FieldPanel
from wagtail.wagtailadmin.forms import WagtailAdminModelForm
# This is your main 'node' model, it inherits mp_node
# mp_node is short for materialized path, it means the tree has a clear path
class Topic(MP_Node):
"""
Topics can be nested and ordered.
Root (id 1) cannot be deleted, can be edited.
User should not edit path, depth, numchild directly.
"""
name = models.CharField(max_length=30)
is_selectable = models.BooleanField(default=True) # means selectable by pages
# any other fields for the Topic/Category can go here
# eg. slug, date, description
# may need to rework node_order_by to be orderable
# careful - cannot change after initial data is set up
node_order_by = ['name']
# just like any model in wagtail, you will need to set up panels for editing fields
panels = [
FieldPanel('parent'), # parent is not a field on the model, it is built in the TopicForm form class
FieldPanel('name', classname='full'),
FieldPanel('is_selectable'),
]
# this is just a convenience function to make the names appear with lines
# eg root | - first child
def name_with_depth(self):
depth = '— ' * (self.get_depth() - 1)
return depth + self.name
name_with_depth.short_description = 'Name'
# another convenience function/property - just for use in modeladmin index
@property
def parent_name(self):
if not self.is_root():
return self.get_parent().name
return None
# a bit of a hacky way to stop users from deleting root
def delete(self):
if self.is_root():
raise PermissionDenied('Cannot delete root topic.')
else:
super(Topic, self).delete()
# pick your python string representation
def __unicode__(self):
return self.name_with_depth()
def __str__(self):
return self.name_with_depth()
class Meta:
verbose_name = 'Topic'
verbose_name_plural = 'Topics'
# this class is the form class override for Topic
# it handles the logic to ensure that pages can be moved
# root pages need to be treated specially
# including the first created item always being the root
class TopicForm(WagtailAdminModelForm):
# build a parent field that will show the available topics
parent = forms.ModelChoiceField(
required=True,
empty_label=None,
queryset=Topic.objects.none(),
)
def __init__(self, *args, **kwargs):
super(TopicForm, self).__init__(*args, **kwargs)
instance = kwargs['instance']
all = Topic.objects.all()
is_root = False
if len(all) == 0 or instance.is_root():
# no nodes, first created must be root or is editing root
is_root = True
if is_root:
# disable the parent field, rename name label
self.fields['parent'].empty_label = 'N/A - Root Node'
self.fields['parent'].disabled = True
self.fields['parent'].required = False
self.fields['parent'].help_text = 'Root Node has no Parent'
self.fields['name'].label += ' (Root)'
else:
# sets the queryset on the parent field
# ensure that they cannot select the existing topic as parent
self.fields['parent'].queryset = Topic.objects.exclude(
pk=instance.pk)
self.fields['parent'].initial = instance.get_parent()
def save(self, commit=True):
parent = self.cleaned_data['parent']
instance = super(TopicForm, self).save(commit=False)
all = Topic.objects.all()
is_new = instance.id is None
is_root = False
if is_new and len(all) == 0:
is_root = True
elif not is_new and instance.is_root():
is_root = True
# saving / creating
if is_root and is_new and commit:
# adding the root
instance = Topic.add_root(instance=instance)
elif is_new and commit:
# adding a new child under the seleced parent
instance = parent.add_child(instance=instance)
elif not is_new and instance.get_parent() != parent and commit:
# moving the instance to under a new parent, editing existing node
# must use 'sorted-child' - will base sorting on node_order_by
instance.move(parent, pos='sorted-child')
elif commit:
# no moving required, just save
instance.save()
return instance
# tell Wagtail to use our form class override
Topic.base_form_class = TopicForm
class TopicAdmin(ModelAdmin):
model = Topic
menu_icon = 'radio-empty'
menu_order = 200
add_to_settings_menu = False
list_display = ['name_with_depth', 'parent_name']
search_fields = ['name']
wagtail_hooks.py
这确保了前面代码中的 TopicAdmin 在 Wagtail Admin 中使用。你会知道它是有效的,因为它会出现在左侧管理侧边栏上 modeladmin register docs .
from wagtail.contrib.modeladmin.options import modeladmin_register
from .models import TopicAdmin
modeladmin_register(TopicAdmin)
现在是进行迁移和运行迁移的好时机,请记住 node_order_by
在构建模型后不容易更改。如果您想添加 child 的自定义排序,例如。重新排序容量或按其他字段排序,请在迁移之前执行此操作。
然后进入管理并创建第一个根节点。
这是一个快速而讨厌的示例,让您可以将一个主题链接到一个页面,而不需要任何花哨的东西。请注意,我们在这里限制了选择,这可以扩展为根据您在主题中设置的字段进行更复杂的限制。
topic = models.ForeignKey(
'topics.Topic',
on_delete=models.SET_NULL,
blank=True,
null=True,
limit_choices_to={'is_selectable': True},
related_name='blog_page_topic',
)
关于python - 鹡鸰中的多级分类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46995540/
我想做的是让 JTextPane 在 JPanel 中占用尽可能多的空间。对于我使用的 UpdateInfoPanel: public class UpdateInfoPanel extends JP
我在 JPanel 中有一个 JTextArea,我想将其与 JScrollPane 一起使用。我正在使用 GridBagLayout。当我运行它时,框架似乎为 JScrollPane 腾出了空间,但
我想在 xcode 中实现以下功能。 我有一个 View Controller 。在这个 UIViewController 中,我有一个 UITabBar。它们下面是一个 UIView。将 UITab
有谁知道Firebird 2.5有没有类似于SQL中“STUFF”函数的功能? 我有一个包含父用户记录的表,另一个表包含与父相关的子用户记录。我希望能够提取用户拥有的“ROLES”的逗号分隔字符串,而
我想使用 JSON 作为 mirth channel 的输入和输出,例如详细信息保存在数据库中或创建 HL7 消息。 简而言之,输入为 JSON 解析它并输出为任何格式。 最佳答案 var objec
通常我会使用 R 并执行 merge.by,但这个文件似乎太大了,部门中的任何一台计算机都无法处理它! (任何从事遗传学工作的人的附加信息)本质上,插补似乎删除了 snp ID 的 rs 数字,我只剩
我有一个以前可能被问过的问题,但我很难找到正确的描述。我希望有人能帮助我。 在下面的代码中,我设置了varprice,我想添加javascript变量accu_id以通过rails在我的数据库中查找记
我有一个简单的 SVG 文件,在 Firefox 中可以正常查看 - 它的一些包装文本使用 foreignObject 包含一些 HTML - 文本包装在 div 中:
所以我正在为学校编写一个 Ruby 程序,如果某个值是 1 或 3,则将 bool 值更改为 true,如果是 0 或 2,则更改为 false。由于我有 Java 背景,所以我认为这段代码应该有效:
我做了什么: 我在这些账户之间创建了 VPC 对等连接 互联网网关也连接到每个 VPC 还配置了路由表(以允许来自双方的流量) 情况1: 当这两个 VPC 在同一个账户中时,我成功测试了从另一个 La
我有一个名为 contacts 的表: user_id contact_id 10294 10295 10294 10293 10293 10294 102
我正在使用 Magento 中的新模板。为避免重复代码,我想为每个产品预览使用相同的子模板。 特别是我做了这样一个展示: $products = Mage::getModel('catalog/pro
“for”是否总是检查协议(protocol)中定义的每个函数中第一个参数的类型? 编辑(改写): 当协议(protocol)方法只有一个参数时,根据该单个参数的类型(直接或任意)找到实现。当协议(p
我想从我的 PHP 代码中调用 JavaScript 函数。我通过使用以下方法实现了这一点: echo ' drawChart($id); '; 这工作正常,但我想从我的 PHP 代码中获取数据,我使
这个问题已经有答案了: Event binding on dynamically created elements? (23 个回答) 已关闭 5 年前。 我有一个动态表单,我想在其中附加一些其他 h
我正在尝试找到一种解决方案,以在 componentDidMount 中的映射项上使用 setState。 我正在使用 GraphQL连同 Gatsby返回许多 data 项目,但要求在特定的 pat
我在 ScrollView 中有一个 View 。只要用户按住该 View ,我想每 80 毫秒调用一次方法。这是我已经实现的: final Runnable vibrate = new Runnab
我用 jni 开发了一个 android 应用程序。我在 GetStringUTFChars 的 dvmDecodeIndirectRef 中得到了一个 dvmabort。我只中止了一次。 为什么会这
当我到达我的 Activity 时,我调用 FragmentPagerAdapter 来处理我的不同选项卡。在我的一个选项卡中,我想显示一个 RecyclerView,但他从未出现过,有了断点,我看到
当我按下 Activity 中的按钮时,会弹出一个 DialogFragment。在对话框 fragment 中,有一个看起来像普通 ListView 的 RecyclerView。 我想要的行为是当
我是一名优秀的程序员,十分优秀!