2019-10-18
My little irritation with Firefox's current handling of 'Do-Not-Track'
The Do-Not-Track
proposed HTTP feature was either a noble or naive attempt by various
people to get websites not to track you if you asked them not to.
It worked about as well as you'd expect, which
is to say not at all in practice. Allegedly, for a long time having
your browser send a DNT
header made it easier to fingerprint you
because so few people did it that you stood out all the more.
(This may no longer be the case, for reasons we're about to see.)
For a long time, Firefox provided a setting to send or not send a
DNT
header with requests. Although I already used a variety of
Firefox addons and settings to stop being tracked, I turned this
setting on basically as a gesture to websites to tell them they
had no excuse. I didn't worry about this making me easier to
fingerprint, because even without DNT my particular combination
of User-Agent and other browser attributes was generally very
close to unique (as measured by eg the EFF's Panopticlick).
Recently, two things happened here. The first is that Firefox changed its Do-Not-Track behavior when they added tracking protection as part of their content blocking. After this was added, your two choices with DNT are either sending it all the time or sending it if you have Firefox block tracking; there is no option to have Firefox block tracking but not send a DNT header. At one level this makes perfect sense, but at another level it runs into the the second issue, which is that I found some websites that behave differently in an inconvenient way if DNT is set. Specifically, Medium will block certain embedded content in Medium articles (both on its own site and on sites that just publish with Medium, which is a lot of it), as covered (currently) in Medium's Do Not Track Policy. For me, clicking through often doesn't work very well, so I would like it if Medium didn't do this.
Although it pains me, what I should probably do is turn off Firefox's own tracking protections to whatever degree is required to not trigger this Medium behavior. I'm already relying on uBlock Origin for my anti-tracking protection, so the built in stuff in Firefox is just a backup and may not be doing anything for me in general. Of course, this assumes that I've correctly understood what is going on here with Medium in the first place, because it's always possible that something else about my environment is triggering their 'DNT' stuff (for example, perhaps uBlock Origin is blocking something).
(I was going to be confident about what was going on, but then I started trying to verify that my Firefox was or wasn't sending a DNT header under various circumstances. Now I'm a lot less sure.)
Remembering that Django template code is not quite your Django Python code
Recently I was doing some work with our Django web application where I wanted to do something special
for one particular field of a form. As is usual in Django forms,
we iterate through the form fields with the '{% for %}
' template
tag.
Stripped down, this is something like:
{% for field in form %} {{ field.label_tag }} <br> {{ field }} {% endfor %}
I had to make one field generate extra HTML, which meant that I had
to recognize it somehow. At first I used a '{% if %}
' on the
label, but that felt wrong; the label is text that's shown to the
user and we might want to change it. So I did some Internet searches
and found 'field.name
' (perhaps from here),
which is the actual name of each field. I put it in, everything worked,
I went on, and then a few days later I was struck by a Python question,
namely how did the form field know its name?
Form fields are defined as what looks like class attributes:
class RequestForm(forms.Form): login = forms.CharField([...]) [...]
However, Python objects don't know their names (for good reasons), including attributes on classes or class
instances. At first I thought that Django was using metaclass magic
so that RequestForm.login.name
was set up when the RequestForm
class was defined, but form classes turn out to be more magic
than that and once you extract the relevant
objects, they don't have .name
attributes.
At this point I realized (and remembered) that the pseudo-Python
code you write in Django templates is not the same as the Python
code you write in your app. You can't assume that what works for
one of them (here, referring to 'field.name
') will work for the
other, and it goes both ways. It's possible to rip the covers off
the magic and find an explanation for what Django is doing, but it
will lead you several levels deep and your Django template code is
still not the same as what you'll write in your Python view code.
PS: One bit of magic that is showing already in my example is that
field.label_tag
is a method, not an attribute. The Django
template language automatically calls it and uses the result.
Sidebar: What is going on here
The 'field
' template variable being used here winds up holding a
real Python object, but it is not from the class you think it is.
As covered in Looping over the form's fields,
the 'field
' object is a BoundField
instance. The actual Field
instance, what your view code deals with, is hiding in field.field
.
BoundField
does have a .name
attribute, which is set up when
it's initialized by the overall form code. The overall form code does
know the name of each field (and has to).
(Somewhat to my surprise, .field
is a plain ordinary instance
attribute, not any sort of property or fancy thing. You can see
it being set up in boundfield.py.)