Hi, thanks for taking time to provide such a detailed answer.
> You can and should split up your code into separate functions however
> you like. Form validation can be done in a separate function that
> returns a dictionary that is used to update the context of the calling
> view, for example. Your alternate solution sounds a bit complex for
> working around the things you've described, but c'est la vie...
While it may sound complex, it allows me to write the actual views and
handlers really simply.
For example, I have this one-statement view dispatcher:
@access_restricted
def brochure_page(request, brochure_id, page):
return item_or_404({
"property_address" : show_form_property,
"property_building" : show_form_property,
"documents" : show_form_brochure,
"pdf" : show_form_pdf,
...
}, page)(
request,
enforce_editable(get_object_or_404(models.brochure, pk=brochure_id),
request.user),
page,
)
and this one-statement function powers several pages at once:
def show_form_property(request, brochure, page):
return response_from_main(brochure, page, request,
form=forms.by_page(page)(data=attempted_post(request),
instance=brochure.property))
this one, on the contrary, shows two forms on one page:
def show_form_pdf(request, brochure, page):
return response_from_main(brochure, page, request,
form_brochure_xslfo=forms.brochure_xslfo(data=attempted_post(request),
instance=brochure),
form_pdf_map_new=forms.brochure_pdf_map(data=attempted_post(request)),
)
and this one-statement handler processes submissions generated by the former:
@post_handler(action="update_property", next=redirect_to_console_page())
@access_restricted
@autoload_require_editable(models.property, pk="property_id")
def do_update_property(request, property):
enforce_valid(forms.by_page(request.POST["page"])(request.POST,
instance=property)).save()
While this handler uses request.POST["page"] to know where to redirect, the
following one creates an object and returns a URL to it explicitly:
@post_handler(action="create_photo")
@access_restricted
@autoload_require_editable(models.feature, pk="feature_id")
def do_create_photo(request, feature, property):
enforce_valid(forms.photo(request.POST, request.FILES,
instance=models.photo(feature=feature))).save()
return url_from_view(feature_edit, brochure_id=
request.POST["brochure_id"], feature_id=feature.pk)
So my views contain not a single statement that would be related to form
_processing_, and handlers are likewise agnostic of views.
> I don't really understand all the stuff about ignoring the URL in your
> solution
The idea is that every form does its POST to its own URL, so the POST contains
two pointers: one, contained in the “submission” parameter, tells the
application what to do with the data submitted, and the URL tells it what to
do in case validation fails. If it succeeds, the target of the redirect is
deduced from POST. So, for example, a form looks like this in HTML:
<form action="" method="post">
<input type="hidden" name="submission" value="add_tag" />
<input type="hidden" name="brochure_id" value="4" />
<input type="hidden" name="property_id" value="6" />
<input type="hidden" name="page" value="brochure_tags" />
<input type="text" name="tag" />
<input type="submit" />
</form>
If everything’s OK, the app never looks at the request URI. Only if there’s a
validation failure, the middleware lets Django find the view that corresponds
to the URI--which is the same view that caused the POST--and the view will
display the form(s) again, noting the errors.
I used to have all POSTs directed to a single URI and pass the needed
parameters in the request body. However, that did not allow for user-friendly
form validation, so I made all //form/@action empty, but the parameters
stayed where they had been before. There’s no need for user-friendly URLs
when doing POST, they aren’t even shown to the user anyway (as long as
submissions succeed), they are only relevant in the case the POST handler
can’t do a redirect.
While that’s not too semantic, it preserves shareable/bookmarkable URLs in all
cases. Every URL is guaranteed to return the same form. For a simple example,
let there be two pages that have similar forms that allow editing something.
/admin/posts/2009/01/04/hello-world
<form action="???" method="post">
<input type="hidden" name="post_id" value="42" />
Title: <input type="text" name="title" value="Hello world!" />
Text: <textarea>...</textarea>
</form>
/admin/posts/2009/01/
<form action="???" method="post">
<input type="hidden" name="post_id" value="41" />
Title: <input type="text" name="title" value="First post!" />
Text: <textarea>...</textarea>
</form>
<hr />
<form action="???" method="post">
<input type="hidden" name="post_id" value="42" />
Title: <input type="text" name="title" value="Hello world!" />
Text: <textarea>...</textarea>
</form>
What should be in the action field? If there’s the same URL,
maybe /admin/posts/edit, then a failed form validation would show that URL in
the address field, hardly helpful. What if a user wants to ask someone for
help, how can he/she indicate which page is causing problems? But if each
form has action="", then the URL will be meaningful.
> Since #6094 is about handling exceptions in middleware, and you want
> exception handling in middleware to work as described in #6094, the
> answer to your question is "yes".
OK. Until then, I’ve added this to my middleware, copied from Django source:
except:
if django.db.transaction.is_dirty():
django.db.transaction.rollback()
django.db.transaction.leave_transaction_management()
if not settings.DEBUG or settings.DEBUG_PROPAGATE_EXCEPTIONS:
raise
return django.views.debug.technical_500_response(request,
*sys.exc_info())
Hopefully it’s not too dirty a hack?
--
TIA
Roman.
smime.p7s
Description: S/MIME cryptographic signature

