Computed field for Title [SOLVED]

Is it possible to have 'Title" as a computed field

From  first_name + last_name:


    def title(self):
        if hasattr(self, 'first_name') and hasattr(self, 'last_name'):
            return self.first_names + ', ' + self.last_name
        else:
            return 'No Body'

If it is: would it be possible to do it from a behavior, or do I need to add a (python) content type?

If you want to override or redefine the implementation Title() of an existing content type then I would possibly use Python money patching for providing a custom implementation. If the Title() implementation come from a base class then you need to perform extra checks inside your own implementation that your own magic only applies to the desired content-types. I doubt that it is possible to provide a custom Title implementation through a behavior based on my recent struggle to extend and modify existing behaviors.

You can use the strategy detailed by the blog below:

https://davidjb.com/blog/2010/04/plone-and-dexterity-working-with-computed-fields/

I cheap way (probably not the most elegant) is to inherit from Item or Container (depends on your need), override the Title method (and return what you need in there) and use the new class in your profiles/default/types/yourtype.xml as base <property name="klass">.

1 Like

Thanks

I was hoping to do it from a behavior, but from the answers and the code it looks like I need to do it from the content type.

If you have the content-type code in your own hands then do it of course inside the class itself. Using a behavior in such a case would be nonsense and overkill.

Probably, but I might need it a few places (Actor, Director, etc) and maybe later in life…

Inheritance is your friend

The complication here is that you want the title field computed from names. If you really want just a behavior solution, you can consider just creating a full_name method in your behavior with name fields. Then you call the method in your views if you need the full name.

If you use a custom class solution (PersonItem/PersonContainer) you can also reuse it. But it is not as convenient as a behavior.

Yes, I discovered that that is possible (so now I learned something new).
That is what I am doing now, since it makes it possible for me to make 'a couple of alternatives to show my customer which 'have no idea about how he want it to look / work)...

        # -*- coding: utf-8 -*-
    from plone.dexterity.content import Container
    from plone.supermodel import model
    from zope.interface import implementer
    from datetime import date

    # from medialog.casting import _


    class IActor(model.Schema):
        """ Marker interface and Dexterity Python Schema for Actor    """
        # model.load('actor.xml')


    @implementer(IActor)
    class Actor(Container):
        """
        """
        #@property
        def title(self):
            if hasattr(self, 'first_name') and hasattr(self, 'last_name'):
                return '{0} {1}'.format(self.first_name, self.last_name)
            else:
                return 'NN'

        #def setTitle(self, value):
        #    actor-1 etc for url is better so dont do anything here
        #    return

        @property
        def age(self):
            days_in_year = 365.2425
            if hasattr(self, 'born'):
                age = int((date.today() - self.born).days / days_in_year)
                return age
                #return str("%02d" % age)

I don't think that your age calculation is accurate.

Check https://stackoverflow.com/questions/2217488/age-from-birthdate-in-python

I added a comment to this blogpost with another way of solving this
https://davidjb.com/blog/2010/04/plone-and-dexterity-working-with-computed-fields/

2 Likes

Is there some working solution that i can see on github?
I want to have a "Person" content-type with two separate fields (name and surname) that will generate both title and id.

I'm trying @spereverde solution but i can't save my new content.

I have a content-type like this:

@implementer(IPerson, INameFromTitle)
class Person(Container):
    """ custom ct"""

    @property
    def title(self):
        if hasattr(self, "name") and hasattr(self, "surname"):
            return self.name + " " + self.surname
        else:
            return ""

Then i:

  • added title (not a required field, need to hide it later), name and surname fields in its schema
  • disabled plone.basic behavior and keep plone.namefromtitle (to not have the title required field)

But when i try to save the content, i have this traceback:

Traceback (innermost last):
  Module ZPublisher.WSGIPublisher, line 162, in transaction_pubevents
  Module ZPublisher.WSGIPublisher, line 371, in publish_module
  Module ZPublisher.WSGIPublisher, line 266, in publish
  Module ZPublisher.mapply, line 85, in mapply
  Module ZPublisher.WSGIPublisher, line 63, in call_object
  Module plone.z3cform.layout, line 63, in __call__
  Module plone.z3cform.layout, line 47, in update
  Module plone.dexterity.browser.add, line 138, in update
  Module plone.z3cform.fieldsets.extensible, line 65, in update
  Module plone.z3cform.patch, line 30, in GroupForm_update
  Module z3c.form.group, line 145, in update
  Module plone.app.z3cform.csrf, line 22, in execute
  Module z3c.form.action, line 98, in execute
  Module z3c.form.button, line 315, in __call__
  Module z3c.form.button, line 170, in __call__
  Module plone.dexterity.browser.add, line 116, in handleAdd
  Module z3c.form.form, line 263, in createAndAdd
  Module plone.dexterity.browser.add, line 67, in create
  Module zope.component._api, line 224, in createObject
  Module plone.dexterity.factory, line 40, in __call__
  Module plone.dexterity.content, line 763, in __init__
  Module plone.folder.ordered, line 249, in __init__
  Module Products.CMFCore.PortalFolder, line 77, in __init__
AttributeError: can't set attribute

Same error with the solution from the blogpost.

If i remove the "def title", saving process ends succesfully.
I'm sure that I'm missing something really stupid but I can't see it :frowning:

1 Like

That makes (readonly) property. Make sure that title in your schema is readonly, or make the code something like this:

@property
def title(self):
  return f"My fancy title and my name is {self.name}"

@title.setter
def title(self, value):
  pass
3 Likes

damn..that was the missing piece! Thanks @jaroel!

Maybe this: medialog.casting/src/medialog/casting/content/actor.py at master · espenmn/medialog.casting · GitHub