mirror of
https://expo.survex.com/repositories/expoweb/.git/
synced 2025-12-07 22:34:37 +00:00
110 lines
9.0 KiB
HTML
110 lines
9.0 KiB
HTML
<html><head><title>Handbook Troggle and HTML Forms</title></head><body>
|
||
<style>body { background: #fff url(/images/style/bg-system.png) repeat-x 0 0 }</style>
|
||
<h2 id="tophead">CUCC Expedition Handbook</h2>
|
||
<h1>Troggle and Django Forms</h1>
|
||
|
||
<h3>HTTP resquest/response</h3>
|
||
<p>OK the following assumes that you already have experience in creating and using HTML forms and that you know your way around HTTP requests (GET and POST) and HTTP responses (status + body). You may have used PHP, JSP or ASP for this in the past. If you haven't, then <a href="https://duckduckgo.com/?q=http+get+and+response&atb=v457-1&ia=web">start here</a>.
|
||
|
||
<h4>Django Forms History</h4>
|
||
<p>Django has several generations of quite different clever mechanisms to make creating HTML forms "easier". Yes, making them might be easier, but maintaining this opinionated stuff is a nightmare without adequately educating yourself how the architecture works. This will take time: do not hurry.
|
||
|
||
<p>If you are a professional Django programmer, read <a href="https://www.loopwerk.io/articles/2025/django-views/?_bhlid=6ece59f0c3de24278c50871556723844d8ab734a">this article</a> for why Class Based Views are not a good fit for the community we have maintaining troggle:
|
||
<em>"Just look at the documentation of DetailView. To understand this one class,... 11 methods spread across 5 classes and mixins. Debugging a view or figuring out exactly which method to override to make the view behave in a certain way quickly becomes a case of opening way too many files and jumping back and forth between different method declarations. It’s just too much".</em>
|
||
If you are still not convinced, read this <a href="https://spookylukey.github.io/django-views-the-right-way/index.html">much longer article</a>.
|
||
|
||
<p>WARNING: when reading the Django documentation on <a href="https://docs.djangoproject.com/en/5.1/topics/forms/">Forms</a> (unbound and <a href="https://docs.djangoproject.com/en/5.1/ref/forms/api/">bound</a>) and <a href="https://docs.djangoproject.com/en/dev/topics/forms/modelforms/">ModelForms</a>, do not get diverted into looking at Formsets or ModelFormsets. We do not use any formsets in troggle - of any kind[<a href="#bugger">*</a>].
|
||
|
||
<h3>Django 'Form' object</h3>
|
||
<ul>
|
||
<li>In <em>all</em> our data entry webpages we use a Django Form object to work with the response that the form sends back to the server, where we have to interpret the data and make an update to the database. The form is <em>bound</em> to the data that the user has provided.
|
||
|
||
<li>In <em>many</em> (but not all) we use a Django Form object in a Django template to render [some or all of the] the HTML which displays the input fields for the user. An empty form can be rendered to the user using an <em>unbound</em> form.
|
||
</ul>
|
||
|
||
<p>This is now a hint that you need to refresh your knowledge of <a href="https://docs.djangoproject.com/en/5.1/topics/templates/">the Django template system</a>. Fortunately you only need to know a tiny part of this to work with forms, basically just this idiom:
|
||
<code>{{ my_object.attribute }}</code>
|
||
and maybe also
|
||
<code>{{ my_dict.key }}<br>
|
||
{{ my_variable }}<br><br><br>
|
||
{% if my_variable %} ... {% endif %} </code>
|
||
|
||
<p>Now have a look at the Django templates for some of the forms you will already be familiar with from using them as a caver entering data, e.g. <a href="/logbookedit/2024-08-01c">Logbook entry editing</a> which uses the Django template <a href="/repositories/troggle/.git/tree/templates/logbookform.html">logbookform.html</a>...
|
||
(This is one of those webpages where we do not use a Form object for writing the HTML, only for interpreting the results).
|
||
|
||
<p>Hah, that was a nasty shock wasn't it? OK, most of that stuff is not the stuff which manages the data entry form. Concentrate on just the bits between the <form> ....</form> tags.
|
||
|
||
<p>It might help you at this point to remind yourself how an ordinary webpage works, without a form. So look at <a href="/logbookentry/2024-08-01/2024-08-01c">a logbook entry</a> which just displays the data and the corresponding template <a href="/repositories/troggle/.git/tree/templates/logbookentry.html">logbookentry.html</a>... OK, not such a good idea: that is quite complicated. See <a href="#forloop">For loop</a> below.
|
||
|
||
<h4>HTML <form> and fields</h4>
|
||
But if you haven't worked with HTML forms before, then you actually have <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form">a whole lot of HTML</a> you will need to learn from scratch: how the <code><form> ....</form> </code> tag works, and how fields and labels and stuff works, and "input" and "textarea" and "submit", and how tag attributes such as "disabled" or "required" work.
|
||
<p>
|
||
You will also need to know about <a href="https://docs.djangoproject.com/en/5.1/ref/forms/widgets/">Django Widgets</a>.
|
||
|
||
<p>So that's enough to get you started. Now you are on your own, apart from help on the Website room of the Matrix chat and the nerd email list of course.
|
||
|
||
<h4>ModelForms</h4>
|
||
<p>A few of our data entry pages use <a href="https://docs.djangoproject.com/en/5.1/topics/forms/modelforms/">ModelForms</a>, these are where the Form object is automagically created from a Model class. If you can't find where something is initialised, it is probably because it was done automatically and invisibly by instantiating a ModelForm.
|
||
|
||
<h4>Form views: the code that handles form input</h4>
|
||
<p>The python code which handles the presentation of a form and handles the user responses is called a Django "View" and in troggle these functions are all in python files in the folder <var>troggle/core/views/</var>. There are two ways of doing it: Class-based views (CBV) and Function-based views (FBV). Troggle only uses function-based views for the reasons described very well in <a href="https://www.loopwerk.io/articles/2025/django-views/?_bhlid=6ece59f0c3de24278c50871556723844d8ab734a">this article</a> on why Class Based Views are not a good fit for the community we have maintaining troggle.
|
||
|
||
<p>The <a href="https://docs.djangoproject.com/en/5.2/topics/forms/#get-and-post">documented method</a> for handling the "GET" and "POST" http interactions in Django FBVs are to use a large if statement:
|
||
<pre><code>if request.method == "POST": # POST
|
||
....
|
||
else: # GET
|
||
....
|
||
</code></pre>
|
||
We use a different layout as these if statements can get very deep and long. We use a layout like this (simplified):
|
||
<pre><code>def dwgupload(request, folder):
|
||
|
||
def _setup(folder_arg):
|
||
# initialize common state and return context dictionary ctx
|
||
......
|
||
return ctx
|
||
def _get(ctx):
|
||
# build page and prepare form using data in the supplied context ctx
|
||
......
|
||
return ctx
|
||
def _post(ctx):
|
||
# handle POST -- validate form and save files, update ctx in-place
|
||
......
|
||
return ctx
|
||
|
||
# main flow: setup, then POST or GET handlers, then render
|
||
ctx = _setup(folder)
|
||
|
||
if request.method == "POST":
|
||
ctx = _post(ctx)
|
||
else:
|
||
ctx = _get(ctx)
|
||
|
||
response = render(request, .....) # rendered form to display on screen
|
||
</code></pre>
|
||
|
||
<p>
|
||
Not all our forms use this structure yet.
|
||
<p>
|
||
We looked at using the <a href="https://www.loopwerk.io/articles/2025/django-views/?_bhlid=6ece59f0c3de24278c50871556723844d8ab734a">extremely simplified Class-based View structure</a> where all our view functions would inherit from Django <var>View</var> class, but it has no advantages over this pure function structure and many disadvantages in readability and maintainability.
|
||
|
||
<h3 id="forloop">For loop</h3>
|
||
<figure class="onright">
|
||
<a href="../l/trogclass-1.html"><img src="../t/trogclass-2.jpg" ></a>
|
||
<br><figcaption>Class Diagram<br />(Click to enlarge)</figcaption>
|
||
</figure>_
|
||
[This section to be moved to a generic Django Templating page]
|
||
<p> In <a href="/repositories/troggle/.git/tree/templates/logbookentry.html">logbookentry.html</a> you will see the Django template code
|
||
<code>{% for personlogentry in logbookentry.personlogentry_set.all %}</code>
|
||
which illustrates the for loop syntax, but also the my_object.attribute syntax, where the attribute is a one-to-many link to other Objects (instances of a Class) and has the <a href="https://dnmtechs.com/understanding-the-_set-attribute-in-django-querysets/"><em>function</em> "_set"</a> applied followed by the <a href="https://docs.djangoproject.com/en/5.1/ref/models/querysets/#django.db.models.query.QuerySet.all">QuerySet function</a> ".all". The effect of the for loop is to iterate through all the "personlogentry" instances referenced by the specific "logbookentry" the page is looking at.
|
||
<p>For the relationship between LogBookEntry and PersonLogEntry click on
|
||
<a href="../l/trogclass-1.html">the Class diagram</a> on the right.
|
||
|
||
<h4 id="bugger">Oh bugger</h4>
|
||
<p>Yes there is one FormSet, sorry. The joint Cave-and-Entrance thing.
|
||
<hr />
|
||
Go on to: <a href="trogdjangup.html">Troggle: updating Django</a><br />
|
||
Return to: <a href="trognotes.html">Troggle programmers' guide</a><br />
|
||
Troggle index:
|
||
<a href="trogindex.html">Index of all troggle documents</a><br />
|
||
<hr /></body></body></html>
|