A first object model

Defining objects

Objects are defined using CObject. An object always needs a class. It can have a name or not. For example, we can define an address and a customer object based on the previously defined class model as follows:

thomas_home_address = CObject(address, "Thomas Home Address", values={
    "street1": "Waehringer Strasse 29",
    "street2": "",
    "zip": "1090",
    "city": "Vienna",
    "state": "Vienna",
    "country": "Austria"
})

thomas_customer = CObject(customer, "Thomas Customer", values={
    "id": "C001",
    "first name": "Thomas",
    "last name": "Test",
    "address": thomas_home_address,
    "email": "thomas.test@thomastest.com",
    "phone": "+43111111222333",
    "shipping addresses": [thomas_home_address]
})

The values keyword argument is used to define values which are only accepted, if they conform to the class’ attributes (i.e., name and type must match). The address only contains string attribute values, whereas the customer has string values, but also the address value which accepts a CObject of type address, as well as the shipping address which accepts a list of address objects.

To provide a few more examples, lets define a few unnamed product objects and a named shopping cart object:

basic_pen = CObject(product, values={"id": "P001", "name": "Basic Pen", "price": 1.50})
premium_pen = CObject(product, values={"id": "P001", "name": "Premium Pen", "price": 2.50})
basic_paper = CObject(product, values={"id": "P001", "name": "Basic Paper", "price": 2.75})
premium_paper = CObject(product, values={"id": "P001", "name": "Premium Paper", "price": 5.50})

cart1 = CObject(cart, "Thomas Cart")

Recursive relations

The association staff_reports_to_relation is a recursive relation from the class staff_member to itself. Consider we define a number of staff members:

manager = CObject(staff_member, values={
    "id": "S001",
    "first name": "Marie",
    "last name": "Schneider",
    "role": "Manager"
})
crm1 = CObject(staff_member, values={
    "id": "S002",
    "first name": "Joe",
    "last name": "Lang",
    "role": "CRM"
})
crm2 = CObject(staff_member, values={
    "id": "S003",
    "first name": "Fei",
    "last name": "Wong",
    "role": "CRM"
})

Here, we need to be careful: If we just use the association for specifying the links, the direction of link specification and association might be identical or not. For example, the following code:

add_links({manager: [crm1, crm2]}, association=staff_reports_to_relation)

would lead to the following model, which is not the indented link direction:

_images/shopping_instance1_staff_model_wrong.png

This can be corrected by specifying the links in the correct order given by the association, which can be tedious to look up every time a link is specified and is thus error prone. This issue can be avoid by using the role name for recursive associations which is unambiguous if two different role names have been specified for the recursive association:

add_links({manager: [crm1, crm2]}, role_name="managed")

This yields a correct staff model:

_images/shopping_instance1_staff_model.png

Getting and setting values

The values property can get and set the values of an object using the same kind of dict structure used for setting values via the values keyword argument. For example, the following statement gets all values of the basic pen product:

print("Values of basic pen: %s" % basic_pen.values)

This would print:

Values of basic pen: {'name': 'Basic Pen', 'price': 1.5, 'id': 'P001'}

With the values setter we can set multiple values at once:

basic_pen.values = {'price': 1.75, 'id': 'P001A'}

We can now inspect the new values:

print("New values of basic pen: %s" % basic_pen.values)

This would print:

New values of basic pen: {'name': 'Basic Pen', 'price': 1.75, 'id': 'P001A'}

We can get single values with get_value() and set single values with set_value(). For example, after the price change on the product, we might want to update prices in NEW orders and their items. For our model, we could do this using:

print(f"Old basic pen item price: {item2.get_value('price')!s}, " +
      f"old order total: {order1.get_value('total')!s}")
item2.set_value("price", 1.75)
order1.set_value("total", 12.75)
print(f"New basic pen price: {item2.get_value('price')!s}, " +
      f"new order total: {order1.get_value('total')!s}")

This would print:

Old basic pen item price: 1.5, old order total: 12.5
New basic pen price: 1.75, new order total: 12.75

Resulting model

We can use the the Plant UML renderer to draw the resulting model. The result would be:

_images/shopping_instance1_order_model.png

Please note that here we must use the object model renderer, not the class model renderer to generate the models. It is called via the generate_object_models() method and otherwise used in the same way as the class model renderer used before.

This image and the staff image above have been rendered using the following code:

shopping_instance1_order_model = CBundle("shopping_instance1_order_model",
                                         elements=order1.get_connected_elements() +
                                                  [thomas_home_address, today])
shopping_instance1_staff_model = CBundle("shopping_instance1_staff_model",
                                         elements=manager.get_connected_elements())

generator = PlantUMLGenerator()
generator.generate_object_models("shopping_instance1",
                                 [shopping_instance1_order_model,
                                  {"render_association_names_when_no_label_is_given": True},
                                  shopping_instance1_staff_model,
                                  {"render_association_names_when_no_label_is_given": True}])

Links can have optional label specifications, to support specifying a link-specific label. In addition to that, it can be useful to turn the rendering option render_association_names_when_no_label_is_given on. It adds the association labels to links that have no label (done for all figures in this chapter). This can be a useful option, if the meaning of the link arrows might be unclear.

The full code of the examples in this tutorial can be found in the sample Shopping Instances Model 1.