开发者

Help understanding a Django view

I am trying to follow the code listed on https://github.com/alex/django-ajax-validation/blob/master/ajax_validation/views.py

I have been able to understand a small chunk of it. I have added comments stating my understanding of what is happening.

I would really appreciate some assistance on questions I listed in comments next to the lines I couldn't quite follow.

def validate(request, *args, **kwargs):

    # I thing it is some sort of initializations but I cannot really understand what's happening
    form_class = kwargs.pop('form_class')
    defaults = {
        'data': request.POST
    }
    extra_args_func = kwargs.pop('callback', lambda request, *args, **kwargs: {})
    kwargs = extra_args_func(request, *args, **kwargs)
    defaults.update(kwargs)
    form = form_class(**defaults)

    if form.is_valid(): #straightforward, if there is no error then the form is valid
        data = {
            'valid': True,
        }
    else:
    # if we're dealing with a FormSet then walk over .forms to populate errors and formfields
        if isinstance(form, BaseFormSet):  #I cannot really understand what is BaseFromSet
            errors = {}
            formfields = {}
            for f in form.forms: # I am guessing that this is for when there are multiple form submitted for validation
                for field in f.fields.keys(): # I think he is looping over all fields and checking for error. what does add_prefix () return? and what is formfields[]?
                    formfields[f.add_prefix(field)] = f[field]
                for field, error in f.errors.iteritems():
                    errors[f.add_prefix(field)] = error
            if form.non_form_errors():
                errors['__all__'] = form.non_form_errors() # what is the '__all__'?
        else:
            errors = form.errors
            formfields = dict([(fieldname, form[fieldname]) for fieldname in form.fields.keys()])

    # if fields have been specified then restrict the error list
        if request.POST.getlist('fields'): # I am having a hard time understanding what this if statement does.
            fields = request.POST.getlist('fields') + ['__all__']
            errors = dict([(key, val) for key, val in errors.iteritems() if key in fields])

        final_errors = {} # here the author of this code totally lost me.
        for key, val in errors.iteritems():
            if '__all__' in key: 
                final_errors[key] = val
  开发者_运维知识库          elif not isinstance(formfields[key].field, forms.FileField):
                html_id = formfields[key].field.widget.attrs.get('id') or formfields[key].auto_id
                html_id = formfields[key].field.widget.id_for_label(html_id)
                final_errors[html_id] = val
        data = {
            'valid': False or not final_errors,
            'errors': final_errors,
        }

    json_serializer = LazyEncoder() # Why does the result have to be returned in json?
    return HttpResponse(json_serializer.encode(data), mimetype='application/json')

validate = require_POST(validate) # a decorator that requires a post to submit

LazyEncoder

class LazyEncoder(JSONEncoder):
    def default(self, obj):
        if isinstance(obj, Promise):
            return force_unicode(obj)
        return obj


form_class = kwargs.pop('form_class')

This is simply pulling the keyword argument, form_class, that was passed in via the URL conf.

(r'^SOME/URL/$', 'ajax_validation.views.validate', 
      {'form_class': ContactForm}, # this keyword argument.
       'contact_form_validate') 

BaseFormSet is simply the formset class doing the work behind the scenes. When you don't know, search the source! grep -ri "baseformset" . It's an invaluable tool.

Take a look at at django.forms.formsets to see how formset_factory produces new "formset" classes based on the BaseFormSet, hence the factory part!


I am guessing that this is for when there are multiple form submitted for validation

Yes, that's exactly what a formset is for (dealing with multiple forms)


I think he is looping over all fields and checking for error. what does add_prefix () return? and what is formfields[]?

Yes, that would be looping through the field names.

add_prefix() is for prefixing form field names with a specific form. Because a formset repeats form elements multiple times, each field needs a unique prefix, such as 0-field1, 1-field1, etc.

formfields is just an empty dictionary defined a few lines above.


what is the 'all'?

__all__ is defined at the top of django.forms.forms

NON_FIELD_ERRORS = '__all__'

It's just what non field specific errors (such as constraints across 2 fields) are stored under in the errors dictionary as opposed to errors[fieldname].


I am having a hard time understanding what this if statement does.

The author has left a note:

# if fields have been specified then restrict the error list   
if request.POST.getlist('fields'): 

It's checking if you specified any specific fields to validate in your URLConf, this is not django but ajax_validation.

You can see that he's overwriting his errors dictionary based on only the fields specified, thus passing on the validation only for those fields.

 errors = dict([(key, val) for key, val in errors.iteritems() if key in fields])

here the author of this code totally lost me.

The author has mapped a custom errors and fields dictionary to specific field names with prefixes, (as opposed to the usual FormSet with each form having its own errors dictionary, unaware of the formset itself) which he presumably uses in the AJAX response to validate all fields.

Normally, you can iterate over a formset and go through the errors on a form by form basis, but not so if you need to validate all of them through ajax.

The line pulling html_id should be straight forward most of the time, but it's there because form widgets CAN add interesting things to the end of the ID's based on whether or not the widget is a radio select for example.

From source comments :

# RadioSelect is represented by multiple <input type="radio"> fields,
# each of which has a distinct ID. The IDs are made distinct by a "_X"
# suffix, where X is the zero-based index of the radio field. Thus,
# the label for a RadioSelect should reference the first one ('_0').

Why does the result have to be returned in json?

Because it's an ajax request and javascript easily eats json.


2- could you go through these lines of code...

extra_args_func = kwargs.pop('callback', lambda request, *args, **kwargs: {})

Either return a keyword argument named 'callback' (which if passed in, is supposed to be a function that accepts request and return a dictionary), and if it wasn't, return a lambda function that only returns an empty dictionary.

I'm not sure what the specific use is for the extra context. You could use it to run arbitrary snippets of code without modifying or subclassing ajax_validation...


It might help you to run this code, and put a debugger breakpoint in somewhere so you can step through and examine the variables and methods. You can do this by simply putting this line where you want to break:

import pdb; pdb.set_trace()

and you will be dumped into the debugger in the console.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜