django-cms default menu extend Menu or CMSAttachMenu? - python

I am trying to build a very simple, wiki-type site using django-cms.
I have 1 app, with 2 models defined:
class Subject(models.Model):
label=models.CharField
class Topic(models.Model):
...
cat = models.ForeignKey('topics.Category',
blank=True,
default=None,
help_text=u'Please choose a category for this topic',
null=True
)
I am trying to have the default menu show the Subject classes as the top-level options, with the Topic classes as sub-levels for each Subject. There will be 4 Subjects altogether. E.g.:
Subject 1
-topic1
-topic2
Subject 2
-topic3
-topic4
etc..
I have read all the django-cms docs, and I'm still confused. In my menu.py, should I be extending Menu or CMSAttachMenu? Do I need 4 different generators? How do I reference the ForeignKey field when using the generator?
I am a beginner, any help is greatly appreciated

You could do something like that:
# menu.py
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from cms.menu_bases import CMSAttachMenu
from menus.base import NavigationNode
from menus.menu_pool import menu_pool
from .models import Subject
class SubjectsMenu(CMSAttachMenu):
name = _("Subjects Menu")
def get_nodes(self, request):
nodes = []
cnt = 0
for subject in Subjects.objects.all():
subject_node_id = cnt
node = NavigationNode(
subject.label,
reverse('subject_view_detail', args=(subject.pk,)),
subject_node_id
)
nodes.append(node)
for topic in subject.topics.all():
cnt += 1
node = NavigationNode(
topic.name,
reverse('topic_view_detail', args=(topic.pk,)),
cnt,
subject_node_id # parent
)
nodes.append(node)
cnt += 1
return nodes
menu_pool.register_menu(SubjectsMenu)
Then you can add this menu to your AppHook or attach it from the admin.

Related

Django ORM select_related AttributeError

In my project im trying to use an external .py file for manage data using my django app style like this:
In my django project i create a model:
class temp_test_keywords(models.Model):
main_id = models.ForeignKey(temp_main)
test_id = models.ForeignKey(temp_case)
key_id = models.ForeignKey(temp_keywords)
variable_id = models.ForeignKey(temp_variables, null=True, blank=True)
def __str__(self):
return '%s -> %s' % (str(self.main_id), str(self.test_id))
Well, now in my external rst.py file i start django env like this:
import sys
import os
import django
sys.path.append('core')
os.environ['DJANGO_SETTINGS_MODULE'] = 'core.settings'
django.setup()
ok, at this point i import table and create class for do some thinks with it:
from django.db import models
from django.contrib.contenttypes.fields import
GenericForeignKey,GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.db.models import Count
from frontend.models import temp_test_keywords
class PrepareRst:
def __init__(self,test_id,t_type,log=False):
self.rst = self.mainprep(test_id,t_type)
def mainprep(self,test_id,t_type):
return self.tc_prep(test_id)
#TestCase rst prep method
def tc_prep(self,test_id):
maxpar = temp_test_keywords.objects.filter(main_id = test_id).values('key_id').annotate(total=Count('variable_id')).order_by('-total').first()
totpar = maxpar['total']
#Part1 list creation
count = 0
ltouple = ()
l1 = ["Test Case"]
while (count < totpar):
l1.append("")
count += 1
ltouple += (l1,)
#Query for extract keywords, values
kv = temp_test_keywords.select_related()
but when i run an AttributeError: type object 'temp_test_keywords' has no attribute 'select_related' error raise
if i start python manage.py shell from terminal the "kv = temp_test_keywords.select_related()" command works fine, why in my .py code doesn't?
Thanks in advance
Try,
kv = temp_test_keywords.objects.all().select_related()

Django Update object only when foreign key object will change

I want to update my object's min and max price when Item's foreign key object(Currency) will be updated. In this situation it updates after every refresh of page.
my views.py
for item in object_list:
if item.currency.id == 2:
new_min_price = item.min_price * (dollar_rate.value)
new_max_price = item.max_price * (dollar_rate.value)
item.min_price = new_min_price
item.max_price = new_max_price
item.save()
You can use signals to solve this: https://docs.djangoproject.com/en/1.10/topics/signals/
from django.db.models.signals import post_save
from django.dispatch import receiver
from . models import Currency
# This gets called immediately after any Currency object is saved
#receiver(post_save, sender=Currency)
def update_min_max(sender, **kwargs):
currency = kwargs.get('instance')
item = currency.item_set.get(id=2)
new_min_price = item.min_price * (dollar_rate.value)
new_max_price = item.max_price * (dollar_rate.value)
item.min_price = new_min_price
item.max_price = new_max_price
item.save()
You might need to make some minor adjustments but this is the general idea. pre_save could be better in your case rather than post_save.

Get view pk from django-cms CMSAttachMenu

I have a custom app inside django-cms and have the need to attach a submenu to my app.
I've followed the guides and examples I've found to do this (see the Portfolio example given by Brandon here: custom views within Djangocms?), and managed to get the submenu up and running.
By expanding on the example provided above; What if this Portfolio app presented here, consisted of a small number of different views (create view, detail view and perhaps a couple of other related views). What if I needed to build a submenu to hold the choices related to user navigation in this small app. And what if the navigation should present choices based on selected content in the views ("Edit" only if a portfolio is selected or similar).
The submenu would have to know what Portfolio was selected, right? Or at least that a Portfolio is in fact selected and in view.
How can I transfer to my implementation of CMSAttachMenu what my view allready knows? In my case, I'm implementing an app dealing with meetups or "Events". The example below does not work, because the Event object is obviously not registered in the request, but it illustrates what I want:
# menu.py
from django.core.urlresolvers import reverse
from menus.base import NavigationNode
from menus.menu_pool import menu_pool
from cms.menu_bases import CMSAttachMenu
from App.apps.event.models import Event
from django.utils.translation import ugettext_lazy as _
import logging
logger = logging.getLogger('instant.event')
class EventMenu(CMSAttachMenu):
name = _("Event Sub-Menu")
def get_nodes(self, request):
nodes = []
nodes.append(NavigationNode(_('Create new events'), reverse("admin:event_event_add"), 1 + len(nodes), 0))
if hasattr(request, 'event'):
if request.event.is_registered_to_event(request.user):
nodes.append(NavigationNode(_('Unregister from this event'), reverse("unregister_from_event"), 1 + len(nodes), 0))
else:
nodes.append(NavigationNode(_('Register to participate in this event'), reverse("unregister_from_event"), 1 + len(nodes), 0))
if request.user.is_superuser():
nodes.append(NavigationNode(_('Register other participant to this event'), reverse("register_admin", args=(request.event.id)), 1 + len(nodes), 0))
nodes.append(NavigationNode(_('Back to list of events'), reverse("events"), 1 + len(nodes), 0))
return nodes
menu_pool.register_menu(EventMenu)
This was a hard one, but the following would solve it (showing only relevant parts):
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 EventsApphook(CMSApp):
name = _("Event")
urls = ["App.apps.event.urls"]
apphook_pool.register(EventsApphook)
menu.py
from cms.menu_bases import CMSAttachMenu
from menus.base import NavigationNode
from menus.menu_pool import menu_pool
from django.utils.translation import ugettext_lazy as _
menuNodes = []
class EventMenu(CMSAttachMenu):
name = _("Event Sub-Menu")
def get_nodes(self, request):
return menuNodes
menu_pool.register_menu(EventMenu)
def add_menu_node(text, url):
# only add a given url once
if len(list(n for n in menuNodes if n.url == url)) == 0:
menuNodes.append(NavigationNode(text, url, 1 + len(menuNodes), 0))
menu_pool.clear()
views.py
from django.views.generic.detail import DetailView
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from App.apps.event.menu import add_menu_node
from App.apps.event.models import Event
class EventMenuMixin(object):
def get_context_data(self, **kwargs):
context = super(EventMenuMixin, self).get_context_data(**kwargs)
member = self.request.user
if 'pk' in self.kwargs.keys():
event = Event.objects.get(id=self.kwargs['pk'])
if event.is_registered_to_event(member):
add_menu_node(_('Unregister from this event'), reverse("unregister_from_event"))
else:
add_menu_node(_('Register to participate in this event'), reverse("register_to_event", args=(self.kwargs['pk'])))
add_menu_node(_("Create new events"), reverse("admin:event_event_add"))
return context
class EventDetailView(EventMenuMixin, DetailView):
model = Event
template_name = 'event/event_detail.html'
context_object_name = 'event'
I hope this will help others in the same predicament as me.

Plone: Change default schemata of a content type

I am using the Products.FacultyStaffDirectory product to manage contacts in my website.
One of the content types, i.e. FacultyStaffDirectory, has the description field in the 'categorization' tab when you are in edit mode. Now I need to remove the description field and put it in the default tab.
To do this, I'm using archetypes.schemaextender product to edit the field. In particular, I am using ISchemaModifier.
I have implemented the code but the field is still being shown in the categorization tab. May be something I missed. Below is the code:
This is the class that contains the description field that I want to modify:
# -*- coding: utf-8 -*-
__author__ = """WebLion <support#weblion.psu.edu>"""
__docformat__ = 'plaintext'
from AccessControl import ClassSecurityInfo
from Products.Archetypes.atapi import *
from zope.interface import implements
from Products.FacultyStaffDirectory.interfaces.facultystaffdirectory import IFacultyStaffDirectory
from Products.FacultyStaffDirectory.config import *
from Products.CMFCore.permissions import View, ManageUsers
from Products.CMFCore.utils import getToolByName
from Products.ATContentTypes.content.base import ATCTContent
from Products.ATContentTypes.content.schemata import ATContentTypeSchema, finalizeATCTSchema
from Products.membrane.at.interfaces import IPropertiesProvider
from Products.membrane.utils import getFilteredValidRolesForPortal
from Acquisition import aq_inner, aq_parent
from Products.FacultyStaffDirectory import FSDMessageFactory as _
schema = ATContentTypeSchema.copy() + Schema((
LinesField('roles_',
accessor='getRoles',
mutator='setRoles',
edit_accessor='getRawRoles',
vocabulary='getRoleSet',
default = ['Member'],
multiValued=1,
write_permission=ManageUsers,
widget=MultiSelectionWidget(
label=_(u"FacultyStaffDirectory_label_FacultyStaffDirectoryRoles", default=u"Roles"),
description=_(u"FacultyStaffDirectory_description_FacultyStaffDirectoryRoles", default=u"The roles all people in this directory will be granted site-wide"),
i18n_domain="FacultyStaffDirectory",
),
),
IntegerField('personClassificationViewThumbnailWidth',
accessor='getClassificationViewThumbnailWidth',
mutator='setClassificationViewThumbnailWidth',
schemata='Display',
default=100,
write_permission=ManageUsers,
widget=IntegerWidget(
label=_(u"FacultyStaffDirectory_label_personClassificationViewThumbnailWidth", default=u"Width for thumbnails in classification view"),
description=_(u"FacultyStaffDirectory_description_personClassificationViewThumbnailWidth", default=u"Show all person thumbnails with a fixed width (in pixels) within the classification view"),
i18n_domain="FacultyStaffDirectory",
),
),
))
FacultyStaffDirectory_schema = OrderedBaseFolderSchema.copy() + schema.copy() # + on Schemas does only a shallow copy
finalizeATCTSchema(FacultyStaffDirectory_schema, folderish=True)
class FacultyStaffDirectory(OrderedBaseFolder, ATCTContent):
"""
"""
security = ClassSecurityInfo()
implements(IFacultyStaffDirectory, IPropertiesProvider)
meta_type = portal_type = 'FSDFacultyStaffDirectory'
# Make this permission show up on every containery object in the Zope instance. This is a Good Thing, because it easy to factor up permissions. The Zope Developer's Guide says to put this here, not in the install procedure (http://www.zope.org/Documentation/Books/ZDG/current/Security.stx). This is because it isn't "sticky", in the sense of being persisted through the ZODB. Thus, it has to run every time Zope starts up. Thus, when you uninstall the product, the permission doesn't stop showing up, but when you actually remove it from the Products folder, it does.
security.setPermissionDefault('FacultyStaffDirectory: Add or Remove People', ['Manager', 'Owner'])
# moved schema setting after finalizeATCTSchema, so the order of the fieldsets
# is preserved. Also after updateActions is called since it seems to overwrite the schema changes.
# Move the description field, but not in Plone 2.5 since it's already in the metadata tab. Although,
# decription and relateditems are occasionally showing up in the "default" schemata. Move them
# to "metadata" just to be safe.
if 'categorization' in FacultyStaffDirectory_schema.getSchemataNames():
FacultyStaffDirectory_schema.changeSchemataForField('description', 'categorization')
else:
FacultyStaffDirectory_schema.changeSchemataForField('description', 'metadata')
FacultyStaffDirectory_schema.changeSchemataForField('relatedItems', 'metadata')
_at_rename_after_creation = True
schema = FacultyStaffDirectory_schema
# Methods
security.declarePrivate('at_post_create_script')
def at_post_create_script(self):
"""Actions to perform after a FacultyStaffDirectory is added to a Plone site"""
# Create some default contents
# Create some base classifications
self.invokeFactory('FSDClassification', id='faculty', title='Faculty')
self.invokeFactory('FSDClassification', id='staff', title='Staff')
self.invokeFactory('FSDClassification', id='grad-students', title='Graduate Students')
# Create a committees folder
self.invokeFactory('FSDCommitteesFolder', id='committees', title='Committees')
# Create a specialties folder
self.invokeFactory('FSDSpecialtiesFolder', id='specialties', title='Specialties')
security.declareProtected(View, 'getDirectoryRoot')
def getDirectoryRoot(self):
"""Return the current FSD object through acquisition."""
return self
security.declareProtected(View, 'getClassifications')
def getClassifications(self):
"""Return the classifications (in brains form) within this FacultyStaffDirectory."""
portal_catalog = getToolByName(self, 'portal_catalog')
return portal_catalog(path='/'.join(self.getPhysicalPath()), portal_type='FSDClassification', depth=1, sort_on='getObjPositionInParent')
security.declareProtected(View, 'getSpecialtiesFolder')
def getSpecialtiesFolder(self):
"""Return a random SpecialtiesFolder contained in this FacultyStaffDirectory.
If none exists, return None."""
specialtiesFolders = self.getFolderContents({'portal_type': 'FSDSpecialtiesFolder'})
if specialtiesFolders:
return specialtiesFolders[0].getObject()
else:
return None
security.declareProtected(View, 'getPeople')
def getPeople(self):
"""Return a list of people contained within this FacultyStaffDirectory."""
portal_catalog = getToolByName(self, 'portal_catalog')
results = portal_catalog(path='/'.join(self.getPhysicalPath()), portal_type='FSDPerson', depth=1)
return [brain.getObject() for brain in results]
security.declareProtected(View, 'getSortedPeople')
def getSortedPeople(self):
""" Return a list of people, sorted by SortableName
"""
people = self.getPeople()
return sorted(people, cmp=lambda x,y: cmp(x.getSortableName(), y.getSortableName()))
security.declareProtected(View, 'getDepartments')
def getDepartments(self):
"""Return a list of FSDDepartments contained within this site."""
portal_catalog = getToolByName(self, 'portal_catalog')
results = portal_catalog(portal_type='FSDDepartment')
return [brain.getObject() for brain in results]
security.declareProtected(View, 'getAddableInterfaceSubscribers')
def getAddableInterfaceSubscribers():
"""Return a list of (names of) content types marked as addable using the
IFacultyStaffDirectoryAddable interface."""
return [type['name'] for type in listTypes() if IFacultyStaffDirectoryAddable.implementedBy(type['klass'])]
security.declarePrivate('getRoleSet')
def getRoleSet(self):
"""Get the roles vocabulary to use."""
portal_roles = getFilteredValidRolesForPortal(self)
allowed_roles = [r for r in portal_roles if r not in INVALID_ROLES]
return allowed_roles
#
# Validators
#
security.declarePrivate('validate_id')
def validate_id(self, value):
"""Ensure the id is unique, also among groups globally."""
if value != self.getId():
parent = aq_parent(aq_inner(self))
if value in parent.objectIds():
return _(u"An object with id '%s' already exists in this folder") % value
groups = getToolByName(self, 'portal_groups')
if groups.getGroupById(value) is not None:
return _(u"A group with id '%s' already exists in the portal") % value
registerType(FacultyStaffDirectory, PROJECTNAME)
Below is the portion of code from the class that I have implemented to change the description field's schemata:
from Products.Archetypes.public import ImageField, ImageWidget, StringField, StringWidget, SelectionWidget, TextField, RichWidget
from Products.FacultyStaffDirectory.interfaces.facultystaffdirectory import IFacultyStaffDirectory
from archetypes.schemaextender.field import ExtensionField
from archetypes.schemaextender.interfaces import ISchemaModifier, ISchemaExtender, IBrowserLayerAwareExtender
from apkn.templates.interfaces import ITemplatesLayer
from zope.component import adapts
from zope.interface import implements
class _ExtensionImageField(ExtensionField, ImageField): pass
class _ExtensionStringField(ExtensionField, StringField): pass
class _ExtensionTextField(ExtensionField, TextField): pass
class FacultyStaffDirectoryExtender(object):
"""
Adapter to add description field to a FacultyStaffDirectory.
"""
adapts(IFacultyStaffDirectory)
implements(ISchemaModifier, IBrowserLayerAwareExtender)
layer = ITemplatesLayer
fields = [
]
def __init__(self, context):
self.context = context
def getFields(self):
return self.fields
def fiddle(self, schema):
desc_field = schema['description'].copy()
desc_field.schemata = "default"
schema['description'] = desc_field
And here is the code from my configure.zcml:
<adapter
name="apkn-FacultyStaffDirectoryExtender"
factory="apkn.templates.extender.FacultyStaffDirectoryExtender"
provides="archetypes.schemaextender.interfaces.ISchemaModifier"
/>
Is there something that I'm missing in this?
def fiddle(self, schema):
schema['description'].schemata = 'default'
should be sufficient. The copy() operation does not make any sense here.
In order to check if the fiddle() method is actually used: use pdb or add debug print statements.
It turns out that there was nothing wrong with the code.
I got it to work by doing the following:
Restarted the plone website
Re-installed the product
Clear and rebuilt my catalog
This got it working somehow.

Autocomplete-Light channel name conflict

I have a Django project containing two apps, Expenses and Sales which both have models named Item. I'm using django-autocomplete-light to ease the selection of Item. This works for either Expenses or Sales depending on which channel I register last but the other one wrongly shows the same Items.
autocomplete_light_registry.py
from sales.models import Item as SalesItem
from expenses.models import Item as ExpenseItem
class ExpenseChannel(autocomplete_light.ChannelBase):
def query_filter(self, results):
q = self.request.GET.get('q', None)
if q:
if results.model == ExpenseItem:
results = results.filter(
Q(name__icontains=q)
return results
class SalesChannel(autocomplete_light.ChannelBase):
def query_filter(self, results):
q = self.request.GET.get('q', None)
if q:
if results.model == SalesItem:
results = results.filter(
Q(name__icontains=q)
return results
autocomplete_light.register(ExpenseItem, ExpenseChannel, placeholder='Select an item (e)')
autocomplete_light.register(SalesItem, SalesChannel, placeholder='Select an item (s)')
admin.py
For sales app, similar in expenses
import autocomplete_light
class SalesItemInline(admin.TabularInline):
fields = ('item', )
model = SalesItem
form = autocomplete_light.modelform_factory(SalesItem)
Checking the log when using the autocomplete fields i see the same url being fetched from both views.
"GET /autocomplete/channel/ItemChannel/?q= HTTP/1.1" 200 1416
How do I configure this so list of sales.Item is returned in Admin Sales view and list of expenses.Item is returned in Admin Expenses view?
What's happening is that the channel class is generated in most cases and it's name is generated too. However, you can avoid channel class generation and channel name generation (hopefully, or this would really suck).
From the registry documentation:
Three cases are possible:
specify model class and ModelNameChannel will be generated extending ChannelBase, with attribute model=model
specify a model and a channel class that does not have a model attribute, and a ModelNameChannel will be generated, with attribute
model=model
specify a channel class with a model attribute, and the channel is directly registered
The solution to avoid channel class generation is to be in the third case: register a model and channel class with a model attribute.
autocomplete_light_registry.py
from sales.models import Item as SalesItem
from expenses.models import Item as ExpenseItem
class ExpenseChannel(autocomplete_light.ChannelBase):
placeholder='Select an item (e)'
model = ExpenseItem
def query_filter(self, results):
q = self.request.GET.get('q', None)
if q:
if results.model == ExpenseItem:
results = results.filter(
Q(name__icontains=q)
return results
class SalesChannel(autocomplete_light.ChannelBase):
model = SalesItem
placeholder = 'Select an item (s)'
def query_filter(self, results):
q = self.request.GET.get('q', None)
if q:
if results.model == SalesItem:
results = results.filter(
Q(name__icontains=q)
return results
autocomplete_light.register(ExpenseChannel)
autocomplete_light.register(SalesChannel)
That would work up to 0.7rc2.
Starting 0.7rc3 (to be released when the pending issue is closed), register() has a new keyword argument, channel_name, which you may use.
But you should be careful with your code, it seems like the query_filter() implementation from your classes is the same as the default implementation ...

Categories