I have an application with a single method called member_create that presents a form to collect membership information and save it to the database. Depending on the project configuration and user I have different membership forms, so I have a line of code like this in member_create():
member_edit_form_class = get_member_edit_form_class (membership_type) Up until recently, get_member_edit_form would return either GenericMemberEditForm or SpecialMemberEditForm, where the latter inherited from the former. All of that was relatively straightforward. Just to be clear, get_member_edit_form_class was returning a class, not an instance, since member_create would instantiate the class later as needed: if request.method == 'POST': form = member_edit_form_class(request.POST, request.FILES) ... else: form = member_edit_form_class() Things got a little more interesting, though, when our "Special" customer wanted further refinements in the form's behavior based on membership type. Trying to keep member_create() generic, I put logic like this in the SpecialMemberEditForm class: def __init__(self, *args, **kwargs): membership_type = kwargs.pop('membership_type', None) super(SpecialMemberEditForm, self).__init__(*args, **kwargs) if membership_type.name == 'whatever': self.fields['options'].queryset = ... else: self.fields['options'].queryset = ... I wanted the member_create() method not to have any knowledge of this extra parameter, for a few reasons: 1) I wanted to keep member_create() as generic as possible. 2) I already had a factory method called get_member_edit_form_class (), so I wanted the factory method to configure the classes as needed. (I am using "factory" in the loose sense--I don't know the exact terminology for a method that returns a class, not an instance.) 3) I didn't want member_create() to pass in the extra parameter to any class other than SpecialMemberEditForm, since GenericMemberEditForm had no use for that parameter, and it would be brittle to have all member edit forms follow the contract of popping off unused parameters. 4) To elaborate on the prior point, the forms all have BaseModelForm as an ancestor, and BaseModelForm's __init__ method cannot deal gracefully with extraneous keyword arguments, perhaps for good reason. In order to allow get_member_edit_form_class to configure SpecialMemberEditForm as needed, I wrote code like this: def special_get_member_edit_form_class(membership_type): # we curry membership_type here so that other code never needs to # deal with extra parms def curry(*args, **kwargs): kwargs['membership_type'] = membership_type return SpecialMemberEditForm(*args, **kwargs) return curry Essentially, the method above is not returning a class--instead it's returning a method, but that method generally provides the illusion of being a class in a pythonic sort of way, since it has the same behavior when used as a callable (it returns an instance). This worked exactly as expected in member_create(), but then I got around to updating member_update(), which works like this: member_edit_form_class = get_member_edit_form_class (membership_type) return create_update.update_object(request, post_save_redirect=..., form_class=member_edit_form_class, template_name='member_form.html', **kwargs) Here is where the illusion broke down. The update_object method really wants member_edit_form_class to be a subclass of Form, not just a callable that produces a Form subclass instance. In particular, this line of code fails inside get_model_and_form_class (), which gets called by update_object(): return form_class._meta.model, form_class Basically what is happening at this point is that form_class is the same as curry above, and curry does not have an attribute called _meta, since curry is not exactly a subclass of Form; instead, it is a method that returns an instance of Form. So I added one line of code to solve the problem: curry._meta = SpecialMemberEditForm._meta So now everything works, but it still feels a little dirty and brittle, and I'm wondering if others have solved similar problems using different design patterns. The situation is not as esoteric as perhaps it looks--really, it's just a scenario where we are trying to keep one method DRY with a couple different forms that can be plugged in, and one form class needs to be configured before instantiation. I know that django has some helpers related to partial functions, but I haven't had luck tracking down documentation, so any pointers there are welcome, even if they don't exactly solve my problem. I think I am doing something a little different from the normal partial/currying use case, but I also bet somebody has solved my problem before. Thanks, Steve http://www.djangosnippets.org/users/showell/ --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Django users" group. To post to this group, send email to django-users@googlegroups.com To unsubscribe from this group, send email to django-users+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/django-users?hl=en -~----------~----~----~----~------~----~------~--~---