My favorites | Sign in
Project Logo
                
Search
for
Updated Feb 19, 2007 by djnesh
Labels: Deprecated
ManipulatorsMultipleModels  

Manipulators with multiple models

Here are examples (from one of my projects). I hope that this can help someone.

The code is mostly hacked out from django.contrib.admin.

Two model add form:

#!python
@staff_member_required
@user_passes_test(lambda u: u.has_perm('nekretnine.add_realestate'))
@transaction.commit_manually
def add_rs(request, rs_type):
    rs_type = int(rs_type)
    rs = RealEstate.AddManipulator() #IGNORE:E1101
    detail_model = get_detail_model(rs_type)
    detail = detail_model.AddManipulator() #IGNORE:W0621
    inline_related_objects = RealEstate._meta.get_followed_related_objects(rs.follow) #IGNORE:E1101

    if request.POST:
        # If data was POSTed, we're trying to create a new object(s).
        new_data = request.POST.copy()
        new_data['type'] = rs_type

        if RealEstate._meta.has_field_type(models.FileField): #IGNORE:E1101
            new_data.update(request.FILES)

        # Check for errors.
        errors = {}
        errors.update(rs.get_validation_errors(new_data))
        errors.update(detail.get_validation_errors(new_data))
        rs.do_html2python(new_data)
        detail.do_html2python(new_data)
        
        if not len(errors):
            # No errors. This means we can save the data!
            try:
                new_rs = rs.save(new_data)
                new_data['parent'] = new_rs._get_pk_val()
                new_det = detail.save(new_data)
                _log(request, new_rs, ADDITION)
                _log(request, new_det, ADDITION, add_msg=False)
                transaction.commit()
            except:
                # something is wrong, rollback
                transaction.rollback()
                raise
            return HttpResponseRedirect("../../../")
    else:
        # No POST, so we want a brand new form without any data or errors.
        errors = {}
        new_data = {'type': rs_type}
        new_data.update(rs.flatten_data())
        new_data.update(detail.flatten_data())

    # Create the FormWrapper, template, context, response.
    rs_form = forms.FormWrapper(rs, new_data, errors)
    detail_form = forms.FormWrapper(detail, new_data, errors)
    
    ctx_dict = {
        'rs_type': rs_type,
        'num_errors': len(errors),
        'form': rs_form, # main form admin tags
        'detail': detail_form, # add-on form
        'detail_template': 'nekretnine/detail-%d.html' % rs_type,
        'title': CATEGORY_NAMES[rs_type],
        'inline_related_objects': inline_related_objects,
    }
    return render_to_response('nekretnine/rs_form.html', ctx_dict, context_instance=Context(request))

and this is paired with some template tags:

#!python
FTPL = """
    {%% if %(field)s.errors %%}
        <ul class="errorlist">{%% for err in %(field)s.errors %%}<li>{{ err }}</li>{%% endfor %%}</ul>
    {%% endif %%}
    <label for="{{ %(field)s.get_id }}" class="%(inline)s{%% if %(field)s.formfield.is_required %%} required{%% endif %%}">%(label)s:</label>
    {{ %(field)s }}
    %(addlink)s
"""

ROFTPL = """
    <!-- READONLY -->
    <label for="{{ %(field)s.get_id }}" class="%(inline)s{%% if %(field)s.formfield.is_required %%} required{%% endif %%}">%(label)s:</label>
    <input disabled class="vTextField" id="{{ %(field)s.get_id }}" value="{{ %(wrapper)s.data.%(orig_field)s|default:"%(default)s" }}" />
    %(addlink)s
"""

# TODO: help
class FormFieldNode(Node):
    def __init__(self, field, label, readonly=False, inline=False, single=False, filter_=None):
        super(FormFieldNode, self).__init__()
        self.label = label
        self.readonly = readonly
        self.wrapper, self.field = field.split('.')
        self.inline = inline
        self.single = single
        self.filter = filter_

    def render(self, context):  #IGNORE:R0912
        wrapper = self.wrapper #IGNORE:W0612
        orig_field = self.field #IGNORE:W0612
        label = self.label #IGNORE:W0612
        field = '%s.%s' % (self.wrapper, self.field) #IGNORE:W0612
        user = get_current_user()
        addlink = '' #IGNORE:W0612
        js = None
        if self.inline:
            inline = 'inline' #IGNORE:W0612
        else:
            inline = ''
        try:
            if not self.readonly:
                if user is not None and not user.is_anonymous():
                    model = resolve_variable(self.wrapper, context).manipulator.model #IGNORE:E1101
                    try:
                        fld = model._meta.get_field(self.field)
                    except FieldDoesNotExist:
                        raise TemplateSyntaxError, 'Field "%s" does not exists in model "%s"' % (self.field, model._meta.object_name)
                    #
                    if fld.rel:
                        to = fld.rel.to._meta
                        if user.has_perm('%s.%s' % (to.app_label, to.get_add_permission())):
                            alt = gettext('Add Another')
                            module = to.module_name
                            app_label = to.app_label
                            addlink = '\n<a href="/admin/%(app_label)s/%(module)s/add/" class="add-another" id="add_{{ %(field)s.get_id }}" onclick="return showAddAnotherPopup(this);"> <img src="/admin-media/img/admin/icon_addlink.gif" alt="%(alt)s" height="10" width="10"></a>\n' % locals()
                     #
                    if self.filter is not None:
                        if self.filter == 'horizontal':
                            dir = 0
                        else:
                            dir = 1
                        js = Template('<script type="text/javascript">addEvent(window, "load", function(e) { SelectFilter.init("{{ %(field)s.get_id }}", "{{ %(field)s.get_member_name }}", %(dir)d, "/admin-media/"); });</script>' % locals())

                t = Template(FTPL % locals())
            else:
                # XXX escape field data??
                default = gettext('Empty')
                t = Template(ROFTPL % locals())
            if not self.single:
                return t.render(context)
            else:
                if js is None:
                    return '<div class="form-row">%s</div>' % t.render(context)
                else:
                    return '<div class="form-row">%s</div>%s' % (t.render(context), js.render(context))
                    
        except TemplateSyntaxError, e:
            if settings.TEMPLATE_DEBUG:
                raise
        return ''

I18NRE = re.compile(r'^_\((?:\'|")(.*?)(?:\'|")\)$')

# TODO: better syntax check
@register.tag
def formfield(parser, token):
    """
    Create form field.

    Example::

        {% formfield <field> <label> [filter horizontal|vertical] [as [readonly] [inline] %}
    """
    class Parser(TokenParser):
        def top(self):
            field = None
            label = None
            readonly = False
            inline = False
            single = False
            filter = None
            ct = 0

            while self.more():
                if ct == 0:
                    field = self.value()
                elif ct == 1:
                    val = self.value().strip('"')
                    val = val.strip("'")
                    r = I18NRE.match(val)
                    if r:
                        label = gettext(r.group(1))
                    else:
                        label = val
                else:
                    tag = self.tag()
                    if tag == 'filter':
                        filter = self.value()
                        single = True # force single
                    elif tag == 'as':
                        while self.more():
                            tag = self.tag()
                            if tag == 'readonly':
                                readonly = True
                            elif tag == 'inline':
                                inline = True
                            elif tag == 'single':
                                single = True
                            else:
                                raise TemplateSyntaxError, "'formfield' syntax error (got %r)" % tag
                    else:
                        raise TemplateSyntaxError, "'formfield' syntax error"
                ct += 1
            # while
            return field, label, readonly, inline, single, filter
    field, label, readonly, inline, single, filter = Parser(token.contents).top()
    return FormFieldNode(field, label, readonly, inline, single, filter)

class FormRowNode(Node):
    def __init__(self, nodelist):
        super(FormRowNode, self).__init__()
        self.nodelist = nodelist

    def render(self, context):
        try:
            return '<div class="form-row">%s</div>' % self.nodelist.render(context)
        except Exception:
            raise
        return ''


@register.tag
def formrow(parser, token):
    """
    Create form row template.

    Example::

        {% formrow %}
            {% formfield <field> with <label> [as readonly] %}
        {% endformrow %}
    """

    nodelist = parser.parse(('endformrow',))
    parser.delete_first_token()
    return FormRowNode(nodelist)

Sign in to add a comment
Hosted by Google Code