How to get fields of djangocms-blog post extension from Post object? - django-cms

I have a post extension loaded in my django/django-cms/djangocms-blog project, that adds a few fields.
How can I get those fields from a Post object?
Here's an example of post:
$ python3 manage.py shell
>>> from djangocms_blog.models import Post
>>> post = Post.objects.first()
>>> post
<Post: Test post>

I just found that there's an extension "reverse many to one" available in the Post object:
>>> dir(post) # vvvvvvvvv I found this
['DoesNotExist', 'Meta', '[... truncated ...]', 'extension', '[... truncated ...]']
>>> post.extension
<django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager.<locals>.RelatedManager object at 0xd34db33f>
>>> post.extension.first()
<PostExtension: PostExtension object (1)>
So I just need to call it directly using post.extension.first():
>>> post.extension.first().my_custom_field
1
I don't think my Posts will have multiple post_extensions in this project so I'll stick with .first(), but I think each app that adds a post_extension to the Posts will add another post_extension object to the object.
Edit: I can add others instances of the same post extension field to the Post object, I didn't know that post extension worked this way. I don't know how to limit the number of post extension per Post, though.
Second edit: I just found this function in the Django Admin Inline code:
def get_max_num(self, request, obj=None, **kwargs):
"""Hook for customizing the max number of extra inline forms."""
return self.max_num
So I added my own function in my PostExtensionInline(admin.TabularInline) class:
def get_max_num(self, request, obj=None, **kwargs):
return 1

Related

DjangoCMS: How to check for uniqueness of a field in a plugin model (CMSPlugin)?

I build a djangoCMS plugin which contain a slug. I want this slug to be unique.
Since djangoCMS store page content in draft and public versions (with a copy of plugins data), I can't do it at database level because it is normal two have at two plugins (draft and public versions) with the same slug.
I'm pretty sure there is an idiom for this but can't find it.
I've tried to make a check in the clean method of the model, but I can't access instance.placeholder which is None when being in clean method…
class MyPlugin(CMSPlugin):
slug = models.SlugField(
verbose_name=_("Slug"),
db_index=True,
max_length=255,
)
Any idea ?
I think I've found a solution by excluding objects where page is not draft:
def clean(self, *args, **kwargs):
sames_slug = MyPlugin.objects.filter(slug=self.slug).exclude(
placeholder__page__publisher_is_draft=False
)
if self.pk:
sames_slug = sames_slug.exclude(pk=self.pk)
if sames_slug.exists():
raise ValidationError(
{"slug": "There is already one with the same slug"}
)

Plone 4.2 formwidget contenttree permissions

I have a dexterity content type in Plone 4.2.4, that uses the contenttree widget to manage referenced objects, but only in the editForm.
I realized, that the referenced items had to be external_visible to get displayed by the widget, which means that anonymous users could View and AccessContentsInformation. Thats not what I wanted. So I dug in the contenttree widget source and added the following to my products browser/configure.zcml
<include package="Products.CMFCore" file="permissions.zcml"
zcml:condition="installed plone.app.upgrade" />
<browser:page
for="*"
name="contenttree-fetch"
class="my.product.content.bikemetamodel.EditForm"
permission="cmf.ModifyPortalContent"
/>
<adapter factory="my.product.browser.widgets.MetamodellContenttreeAdapter" />
and an adapter
class MetamodellContenttreeAdapter(object):
implements(IBikeMetaModel)
adapts(Interface)
def __init__(self, context):
self.context = context
def _get_allowed_modeltypes(self):
return None
def _set_allowed_modeltypes(self, value):
print "setting", value
allowed_modeltypes = property(_get_allowed_modeltypes, _set_allowed_modeltypes)
[...]
But this seems to be not enough. The underlying catalog search returns no result, If the permissions are set to deny View and AccessContentsInformation to anonymous users. So I guess, I must use the view permission to construct some kind of proxy user.
Would it be ok, to use a SecurityManager in the newly created view to fetch the results as a different user? Or am I just missing something?
Ok, here is how I solved the mystery.
After digging around a while, I realized, that I missed the point with my previous idea to override the ##contenttree-fetch view. The solution I came up with is quite simple and seems elegant (enough) for me. I do a sudo style sidestep now, to gather the required items.
Class EditForm(dexterity.EditForm):
grok.context(IBikeMetaModel)
# If it would be another than the edit view, we could manage
# permisssions here. Not neccessary in edit view, because the
# edit permission defined in this content types *.xml counts
# grok.require("cmf.ModifyPortalContent")
#property
def acl_users(self):
return getToolByName(getSite(), 'acl_users')
def updateWidgets(self):
# This is the magic. A sudo style sidestep to a user
# with the id "system" that has permission to gather
# the required lists in the updateWidgets function of
# the base class
proxy_user = self.acl_users.getUserById("system")
oUser = getSecurityManager()
newSecurityManager(self.request, proxy_user)
super(EditForm, self).updateWidgets()
# custom widget updates
self.widgets['title'].mode = DISPLAY_MODE
self.widgets['year'].mode = HIDDEN_MODE
self.widgets['brand'].mode = HIDDEN_MODE
self.widgets['model'].mode = HIDDEN_MODE
# Very Important! Switch back to the original user.
setSecurityManager(oUser)

How can I ask the state for a content object?

At the end of this tutorial several object attributes are listed. But I need access to the state (published, private,...). I also search that attribute using dir() but I don't see an attribute named as state or something similar. i.e, I need something like this:
>>> app.Plone.foo.bar.state
"published"
Or to keep your code more readable and not having to remember strange method names, you can use plone.api to do this:
from plone import api
api.content.get_state(obj=your_object)
Of course, you need to add plone.api to your eggs first, and re-run buildout.
You can always use the plone_workflow to determine current status:
workflowTool = getToolByName(self.portal, "portal_workflow")
status = workflowTool.getStatusOf("plone_workflow", object)
# where "object" is your content object
print (status)
Unfortunately there is no "state" attribute. Instead, check review_state using the workflow tool e.g.:
>>> app.Plone.portal_workflow.getInfoFor(app.Plone.foo.bar, "review_state")

Why does my content object not show up in the portal_catalog?

I am trying to implement a basic Zope2 content type directly without using dexterity or Archetypes because I need this to be extremely lean.
from OFS.SimpleItem import SimpleItem
from Products.ZCatalog.CatalogPathAwareness import CatalogAware
from persistent.list import PersistentList
class Doculite(SimpleItem, CatalogAware):
""" implement our class """
meta_type = 'Doculite'
def __init__(self, id, title="No title", desc=''):
self.id = id
self.title = title
self.desc = desc
self.tags = PersistentList()
self.default_catalog = 'portal_catalog'
def add_tags(self, tags):
self.tags.extend(tags)
def Subject(self):
return self.tags
def indexObject(self):
self.reindex_object()
From an external method I am doing this:
def doit(self):
pc = self.portal_catalog
res1 = pc.searchResults()
o1 = self['doc1']
o1.add_tags(['test1', 'test2'])
o1.reindex_object()
res2 = pc.searchResults()
return 'Done'
I clear the catalog and run my external method. My object does not get into the catalog. But from the indexes tab, when I browse the Subject index, I can see my content item listed with the values. Both res1 and res2 and empty.
Why is my content item not showing up inside the searchResuts() of the catalog?
Plone is a full-fat content management system, if you're after something lean it's probably not the right choice (perhaps try Pyramid.)
For your content type to be a full part of a Plone site it has to fulfil a number of requirements across the Zope2, CMF and Plone layers. plone.app.content.item.Item is about the simplest base class you can get for a content item for a Plone site, though a simpler base class in itself will not really make instances of your content type any more 'lean' - an instance of a class in Python is basically just a dict and a pointer to it's class.
Most of the work on a page view will be rendering the various user interface features of a site. Rendering the schema based add/edit forms of frameworks like Archetypes and Dexterity is also relatively expensive.
I'd spend a little time profiling your application using one of the supported content type systems before putting time into building your own.
In order to see your objects in the "Catalog" tab of the portal_catalog your objects need to have a "getPhysicalPath()" method that returns a tuple representing their path (ex. ('','Plone','myobject')).
Also try to use this:
from Products.CMFCore.CMFCatalogAware import CMFCatalogAware
as base class.
You need to register your type with the catalog multiplexer. Look at the configuration in the zmi -> archetypes_tool.
I'm not sure, but you may also need a portal_type registration also...
Like Lawrence said though, you're better off just using one of the current content type frameworks if you want to be able to catalog your data with plone's portal catalog. If you can deal with a separate catalog, take a look at repoze.catalog.
Plone needs every content object to provide an "allowedRolesAndUsers" index to return the object in searchResults.
There is probably a zcml snippet that will enable this for my content type. But I was able to get things working by adding another method as follows:
def allowedRolesAndUsers(self):
return ['Manager', 'Authenticated', 'Anonymous']
CatalogAware will be removed in Zope 4 and then can't be used any more.
cf https://github.com/zopefoundation/Products.ZCatalog/issues/26

Archetypes and Schemata() method

I am customizing Plone Archetypes content type by overriding Schemata() method to have vocabulary contents dynamically:
def Schemata(self):
""" Overrides field definitions in fly.
"""
# XXX: Cache this method?
schemata = getSchemata(self).copy()
settings = self.getResearchSettings()
for row in settings.getFieldCustomizations():
name = row.get("fieldName", None)
vocab = row.get("vocabToUse", None)
field = schemata.get(name, None)
if field and vocab:
# Modify field copy ion
field.vocabulery = vocab
return schemata
Do I need to use cache decorator for Schemata() function or is Archetypes handling Schemata() internally so smart so that it calls it only once per request?
Plone 4.0.
forgot Schemata... you should not touch that stuff.
you can have dynamic vocabularies using object methods or zope vocabularies.
first hit on google
You definitely want to cache the results of Schema() and Schemata(), as at least Schema() is called multiple times during the same request.

Resources