Wandering Thoughts archives

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.)

web/FirefoxDNTIrritation written at 22:43:49; Add Comment

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.)

python/DjangoTemplatesNotQuitePython written at 00:15:31; Add Comment


Page tools: See As Normal.
Search:
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.