Republished from the Celerity blog.
Attention Django developers! When soliciting input from users with an HTML form, you want to design forms to be as flexible as possible. That means you build in multiple form fields to support multiple input options, even if you only want the user to fill in one of them.
Having two or more mutually exclusive fields in a form on a webpage/webapp is a pretty frequent scenario developers have to deal with. Unfortunately, Django's forms have no builtin support for this. In this blog post, I will cover the two simple, but ugly, solutions we Django developers often find ourselves using and introduce a library that makes working with such scenarios much simpler.
The most important part of working with mutually exclusive fields is ensuring
that only one of the fields is filled in. This can be achieved by a simple
check in the form's clean
method. The following code snipped demonstrates
that check.
def clean(self):
cleaned_data = super(TestForm, self).clean()
if not (bool(self.cleaned_data['test_field1']) !=
bool(self.cleaned_data['test_field2'])):
raise forms.ValidationError, 'Fill in only one of the two fields'
return cleaned_data
And here it is in action:
As you can see, the main issue with the barebones approach is that the user experience is pretty awful and relies on help text to explain the situation to the user.
A way to simplify the user experience is to add radio buttons infront of the two fields and only allow one field to be enabled at a time. Here is a simple implementation of that using jQuery.
$(document).ready(function() {
$('#id_test_field1,#id_test_field2').each(function(i, e) {
$('<input name="test_field" type="radio" value="'+i+'">')
.insertBefore(e)
.click(function() {
$('#id_test_field1,#id_test_field2').attr('disabled', 'disabled');
$(e).removeAttr('disabled');
})
.next('#id_test_field1').prev().click();
});
});
And here it is in action:
The third approach involved developing a custom field & widget that renders as two widgets in the DOM and has the requisite JavaScript to manage the disabling of unused fields.
The library I developed is available on Github and PyPI.
It consists of two main parts, the MutuallyExclusiveRadioWidget
and the
MutuallyExclusiveValueField
.
The MutuallyExclusiveRadioWidget
accepts
a list of django widgets and renders them with a radio button in front of each
widget. The widgets are all rendered with the same name
parameter in the DOM
because since all but one of them is disabled by the JavaScript, only one value
gets POSTed to the server.
The MutuallyExclusiveValueField
must be passed an iterable of fields and a
MutuallyExclusiveRadioWidget
built with the respective widgets for those
fields. The field validates that only one of the subfields is populated and
that is the value of the field.
An example of using this field/widget combo looks like this:
test_field = MutuallyExclusiveValueField(
fields=(forms.IntegerField(), forms.IntegerField()),
widget=MutuallyExclusiveRadioWidget(widgets=(
forms.Select(choices=[(1,1), (2,2), (3,3)]),
forms.TextInput(),
)))
And here it is in action:
The main usecase that prompted developing the django-xor-formfields package was
to allow users to upload files by either submitting an URL or a file. So I
created the FileOrURLField
. It is a subclass of MutuallyExclusiveValueField
and automatically uses FileOrURLWidget
, a subclass of
MutuallyExclusiveRadioWidget
.
It supports 3 forms of normalization through the to
kwarg:
None
(passthrough)'url'
'file'
The later two options allow the code using your form to be completely unaware
of how the user submitted their file or URL. If 'url' is specified and the user
uploads a file, the field with download the file and store it as media and
return the URL for that file. If 'file' is specified and the user submits an
URL, the field will download that file into an InMemoryUploadedFile
.An
example of using this field which normalized to files is:
And here it is in action:
The code for the demos is available on Github.
Made with data from bechdeltest.com. Sources in Gist 5121543
The data is very sparse before 1930ish, leading to some 100% pass rates
Synology DSM 4.1 is vulnerable to BEAST and the Lucky Thirteen attacks out of the box. Switching to RC4 ciphers makes these attacks, and any other future CBC-targeting attacks, not work. To fix this these 2 files need to be updated:
/usr/syno/apache/conf/extra/httpd-alt-port-ssl-setting.conf
/usr/syno/apache/conf/extra/httpd-ssl.conf-common
Update them such that the line starting with SSLCipherSuite
is replaced with these two lines:
SSLHonorCipherOrder On
SSLCipherSuite RC4-SHA:HIGH:!ADH:!SSLv2
Restart Apache:
/usr/syno/etc/rc.d/S97apache-sys.sh restart
/usr/syno/etc/rc.d/S97apache-user.sh restart
Double check that no other Apache configs contain SSLCipherSuite options:
grep SSLCipher /usr/syno/apache/conf/extra/*
I discovered today that Tomato's Namecheap Dynamic DNS updater support use HTTP with passwords in the GET parameters.
This means your passwords are super easy to sniff on the wire. Don't use it.
Namecheap shouldn't be accepting this sort of request with out HTTPS and Tomato shouldn't be using it.
Here's how I found it:
Password has obviously already been regenerated.
Curl on any linux box will serve the purpose nicely (note the https://):
curl "https://dynamicdns.park-your-domain.com/update?host=$HOST&domain=$DOMAIN&password=$PASSWORD&ip=`curl -s http://my-ip.heroku.com/`"
/bin/ash
in /etc/passwd
su - <user>
mkdir gmvault&&cd gmvault
wget https://raw.github.com/pypa/virtualenv/develop/virtualenv.py
python virtualenv.py venv
./venv/bin/pip install gmvault
ash ./venv/bin/gmvault sync [email protected]