Blog entries

  • CubicWeb 3.6 is (almost) out!

    2010/02/10 by Sylvain Thenault

    And that's great news, after several months of development (things started moving in the beginning of august 2009...), it should be available on our Debian repositories and ftp site in the next few hours.

    So, we can say this release contains a (too) large set of improvements and refactorings. I'll talk about the most important ones here.

    Appobject/Entity classes namespace cleanup

    First of all, the namespace cleanup... 3.6 is a step towards cleaning the entity classes (hence more generally appobject), which are used for a lot of things, making it impossible to tell for sure what could be used or not as an attribute or relation name. We decided to declare identifiers starting with \_cw or cw\_ reserved for the core classes. A lot of methods have been deprecated to cleanup the base appobject class namespace. The remaining methods on entity classes will be removed in future version, by the introduction of an ORM for database related methods, and by the (most probable) introduction of ZCA adapters for other aspects. The most notable renaming are:

    • .req -> ._cw
    • .rset -> .cw_rset
    • .row -> .cw_row
    • .col -> .cw_col

    This is probably what you'll see first when upgrading to 3.6: a huge stack of deprecation warnings on your screen :)

    Another step towards a nice and powerful form system

    • cleaner reponsibilities separation between form, field and widget

    • fields and widgets are now responsible for handling POSTed values (the editcontroller was handling this, making things really unflexible). The editcontroller has been rewritten and now properly gets values from fields. Another benefit is that you can now easily have a widget handling multiple inputs (see the new datetime picker for instance, or the custom widget for Bookmark.path)

    • refactored automatic forms:

      • rewrite 'generic relations' as a field
      • inlined forms are now encapsulated into a field

      so you get much more control on these parts of automatic forms by using mechanism provided generally by fields

      • clearer form relations tags: removed autoform_is_inlined, more understandable autoform_field_section

    Hooks refactoring

    Hooks are now regular appobjects, with selectors (don't forget to reuse Hook.__select__, remember that !). They should simply implement __call__ with no argument (well, only self) and will get info previously passed as argument as instance attributes, according to the matching event.

    Test API cleanup

    EnvBasedTC, ControllerTC, WebTest, RepoBasedTC are all gone. Simply use CubicWebTC, with an unified API similar to what you use in cubicweb-ctl shell and in usual development.

    The Bytes File System Storage

    You can now specify a custom storage for attributes of entities stored in the system source. This mechanism is used to provide a way to store Bytes attributes (such as File.data for instance) as files on the file-system instead of BLOBs in the database. You can configure which attributes should use this storage for your instance and then everything is transparent.

    Schema definition changes (yams 0.27)

    In your schema definition file:

    • "symetric" should be correctly spelled "symmetric" :)
    • "permissions" was renamed to "__permissions__"

    Also, permissions for relations are now supported per definition, not per type, at the cost of a visible impact when writing/reading the schema.

    Note about backward compatibility

    We worked hard to keep backward compatibility, but you shouldn't upgrade to 3.6 without checking that everything is fine... Check notably:

    • forms, if you're using custom forms by overriding internal methods
    • import for date functions from cubicweb.utils (they moved to logilab.common.date)

    And also

    CubicWeb 3.6 comes with a set of 37 cubes "3.6"-ready to avoid too much warnings!

    Enjoy!


  • ensure that 2 boolean attributes of an entity never have the same value

    2011/09/08

    I want to implement an entity with 2 boolean attributes, and a requirement is that these two attributes never have the same boolean value (think of some kind of radio buttons).

    Let's start with a simple schema example:

    # in schema.py
    class MyEntity(EntityType):
       use_option1 = Boolean(required=True, default=True)
       use_option2 = Boolean(required=True, default=False)
    

    So new entities will be conform to the spec.

    To do this, you need two things:

    • a constraint in the entity schema which will ring if both attributes have the same value
    • a hook which will toggle the other attribute when one attribute is changed.

    RQL constraints are generally meant to be used on relations, but you can use them on attributes too. Simply use 'S' to denote the entity, and write the constraint normally. You need to have the same constraint on both attributes, because the constraint evaluation is triggered by the modification of the attribute.

    # in schema.py
    class MyEntity(EntityType):
       use_option1 = Boolean(required=True, default=True,
                             constraints = [
                                  RQLConstraint('S use_option1 O1, S use_option2 != O1')
                                           ])
       use_option2 = Boolean(required=True, default=False,
                             constraints = [
                                  RQLConstraint('S use_option1 O1, S use_option2 != O1')
                                           ])
    

    With this update, it is no longer possible to have both options set to True or False (you will get a ValidationError). The nice thing to have is to get the other option to be updated when one of the two attributes is changed, which means that you don't have to take care of this when editing the entity in the web interface (which you cannot do anyway if you are using reledit for instance).

    A nice way of writing the hook is to use Python's sets to avoid tedious logic code:

    class RadioButtonUpdateHook(Hook):
       '''ensure use_option1 = not use_option2 (and conversely)'''
       __regid__ = 'mycube.radiobuttonhook'
       events = ('before_update_entity', 'before_add_entity')
       __select__ = Hook.__select__ & is_instance('MyEntity')
       # we prebuild the set of boolean attribute names
       _flag_attributes = set(('use_option1', 'use_option2'))
       def __call__(self):
           entity = self.entity
           edited = set(entity.cw_edited)
           attributes = self._flag_attributes
           if attributes.issubset(edited):
               # both were changed, let the integrity hooks do their job
               return
           if not attributes & edited:
               # none of our attributes where changed, do nothing
               return
           # find which attribute was modified
           modified_set = attributes & edited
           # find the name of the other attribute
           to_change = (attributes - modified_set).pop()
           modified_name = modified_set.pop()
           # set the value of that attribute
           entity.cw_edited[to_change] = not entity.cw_edited[modified_name]
    

    That's it!