Adding Custom Object Actions

We're nearly ready with the application, but there are two more functions that we need to implement. In the Virtual Host model we have a Boolean flag that indicates whether the host is the default. This information is also conveniently displayed in the listing. However, if we want to change it, we have to navigate to the object's edit page and change the setting there.

It would be nice if this could be done from the object-list screen, by just selecting the appropriate object and using an action from the drop-down menu in the top-left corner of the list. However, the only action that is currently available there is Delete selected Virtual Hosts. Django allows you to define your own action functions and add them to the administration screen menu. There are two steps to get a new function in the actions list. Firs you define a method in the administration class and second you must identify the administration class in whose actions list this method should be listed as an action.

The custom action method is passed three parameters when called. The first is the instance of the ModelAdmin class that called the method. You can define the custom methods outside of the ModelAdmin class, in which case multiple ModelAdmin classes can reuse them. If you define the method within a particular ModelAdmin class, the first parameter will always be the instance of that class; in other words, this is a typical class method ' self' property.

The second parameter is the HTTP request object. It can be used to pass the message back to the user once the action is complete.

The third argument is the query set that contains all objects that have been selected by the user. This is the list of objects you will be operating on. Because there can be only one default Virtual Host, we have to check whether multiple objects have been selected and if so, return an error indicating that. Listing 5-8 shows the modifications to the model administration class that create a new custom object action.

Listing 5-8. A custom action to set the default Virtual Host flag class VirtualHostAdmin(admin.ModelAdmin): [...]

def make_default(self, request, queryset): if len(queryset) == 1:

VirtualHost.objects.all().update(is_default=False) queryset.update(is_default=True) self.message_user(request,

"Virtual host '%s' has been made the default virtual host"

queryset[0]) else:

self.message_user(request, 'ERROR: Only one host can be set as the^

default!')

make_default.short_description = 'Make selected Virtual Host default'

The second custom action that we're going to define is object duplication. This action takes the selected objects and "clones" them. The clones are going to have the same settings and the same set of the configuration directives with the same values, but the following exceptions will apply:

• The virtual host description will get the "(Copy)" string appended to its description.

• The new virtual host will not be the default.

• The new virtual host will not be a template.

The challenge here is to resolve all parent-child dependencies of the VHostDirective objects correctly. In the Apache Virtual Host definition, you can have only one level of encapsulation, so we don't need to do any recursive discovery of the related objects. The duplication method can be split into the following logical steps:

• Create a new instance of the VirtualHost class and clone all properties.

• Clone all directives that do not have any parents.

• Clone all directives that are containers and therefore may potentially contain child directives.

• For each container directive, find all its child directives and clone them. Listing 5-9 shows the duplication function code.

Listing 5-9. The action to duplicate the Virtual Host objects def duplicate(self, request, queryset): msg = ''

for vhost in queryset:

new_vhost = VirtualHost()

new_vhost.description = "%s (Copy)" % vhost.description new vhost.bind address = vhost.bind address new_vhost.is_template = False new_vhost.is_default = False new_vhost.save()

# recreate all 'orphan' directives that aren't parents o=vhost.vhostdirective_set.filter(parent=None).^

filter(directive__is_container=False)

new_vhd = VHostDirective() new_vhd.directive = vhd.directive new_vhd.value = vhd.value new_vhd.vhost = new_vhost new_vhd.save()

# recreate all parent directives for vhd in vhost.vhostdirective_set.filter(directive__is_container=True):

new_vhd = VHostDirective() new_vhd.directive = vhd.directive new_vhd.value = vhd.value new_vhd.vhost = new_vhost new_vhd.save() # and all their children for child_vhd in vhost.vhostdirective_set.filter(parent=vhd): msg += str(child_vhd) new_child_vhd = VHostDirective() new_child_vhd.directive = child_vhd.directive new_child_vhd.value = child_vhd.value new_child_vhd.vhost = new_vhost new_child_vhd.parent = new_vhd new_child_vhd.save() self.message_user(request, msg) duplicate.short_description = 'Duplicate selected Virtual Hosts'

Was this article helpful?

0 0

Post a comment