CSS (Step 1: Basics)

Now that we’ve got content out of the way (I know, I know, content is hard, and pulling it out of clients—or even your own brain—can be even harder), it’s time to make your website look good. We’ll consider a semi-standard blog type site, with posts as excerpts from classic works of fiction. Don’t worry about where the content comes from, or how to pull it from a database or any fancy stuff yet—we’ll get there.

Again, I won’t be covering the details of syntax, rather, I’ll be giving an overview of design elements and how to use them such that you won’t be too surprised at the results. Also, I’ll cover popular designs and how to implement them.

Step one implements some basics, but it’s kind of a hack job. Let’s see how to make it better, and better looking.

Notice that the gradient fill, from a whitish to a blue tint, gets darker as you scroll the page. It might look better if we could fix it, so that the text would scroll but the background would be stable.

Notice that, while the header stays fixed, its background is matched to the original white color. And the footer is matched to the original bluish color, but doesn’t extend all the way to the edges of the browser window.

I used the newish details element to display/hide the author’s name and publication date, but it automatically inserts the word “Details” and a glyph for showing and hiding the info—we can do better.

Let’s make a complete list of what we’ll update:

  • Stable gradient fill background
  • Retain gradient fill for header, but keep it sticky
  • Fade text into header when scrolling
  • Modify “details” element styling
  • Floating “back-to-top” button
  • Sticky chapter titles
  • Simpler, or more flexible, or at least easier to comprehend, drop-cap styling (or raised-cap, or block cap)
  • Use of relative units instead of pixels

The first bit–the fixed gradient–turns out to be easy to fix! Just add

background-attachment: fixed;

But the header looks more out of place with a solid background. The reason it’s solid in the first place is that, without setting a background for the header (comment out that line and try it yourself), the text scrolls right through the header and it looks even worse. What if we copy the body styling for the background image? Turns out, this works fairly well, but now we have a new problem, or at least something we can probably improve upon: the text just disappears as you scroll, a sharp line across the screen.

This, as you might guess, requires a bit more work. Also in the header, add this:

-webkit-mask-image: linear-gradient(rgba(0,0,0,1) 125px,transparent);
mask-image: linear-gradient(rgba(0,0,0,1) 125px,transparent);

This one still requires a prefix in Chrome, at least. What these lines do is add a mask: black at the top down to 125 pixels, fading to transparent. But as it is a mask, the black acts as a filter to block content underneath from showing, save the content to which the mask has been applied, which, in this case, is the original gradient background.

The “Details” element. Could use a bit more flair, I think! First, we add a “summary” tag inside to change the text “Details” to whatever we want—in this case, “Story Info” is as good as anything.

For the rest, we’re going to make things simple. Ish. Instead of the “twisty”—the turning triangle that indicates whether the details element is open or closed—we’ll use a plus and a minus sign, bold, and in red. In Firefox that’s fairly simple, but Chrome makes things a tad difficult.

details > summary {

details > summary::-webkit-details-marker {
  display: none;

summary:before {

details[open] summary:before {
  content: '-';

Highlighting the important bits here, the list style after the details > summary selector is the standards-compliant way of removing the twisty; the pseudo-element -webkit-details-marker is for Chrome. The rest should be fairly self-explanatory: placement of the content within the summary element and its size, and what to show when the element is in its open state. For our example, I also threw a border around it and added some basic styling to the book title, author, and publication date.

How about a “back to top” button? The link href is fairly well-established: "#", but how do we place an element so you can click on it no matter how far down (or up) the page you’ve scrolled? Can it even be done with just CSS?

The answer to that is usually, “Of course!” But, again, as usual, if you want to do slightly fancier things, you have to use JavaScript. We’ll stick to the CSS for now. And this can be done (almost) entirely in CSS, too! Add this code:

#toTop {
#toTop:hover {

The highlighted lines are the important ones. Everything else is looks. We want it to be in the same position, thus “fixed” there, and we want it on top of anything else we put on the page, so a z-index of 99 should do the trick. If you’ve got more layers, maybe tack on another 9 or two.

Nothing will show up until you add an appropriately id’d div in the html also:

<a href="#"><div id="toTop">Top</div></a>

You can put the anchor inside or outside—in this case I prefer outside so I can click anywhere on the element to take me to the top.

You’ve probably seen it before and wondered, “how can I do that”? Every time you scroll to a new article, the title stays at the top, until a new one comes into view. As usual, this is something you can do with only CSS!

article > h3 {
  position: sticky;

At this point, I’ll leave it as an exercise to the reader to handle the fading and text overlapping. There are a few solutions, and the neatest is probably to make the chapter title/heading a full-width banner, and to handle the fading below that. This article also shows off some other cool things you can do with position: sticky.

Nearing the end! Drop caps are a bit of a tricky thing in CSS. You might be tempted to throw in just

article p:first-of-type:first-letter {
  float: left;
  font-size: 5em;

and call it a day, but if you pay attention (and this will vary by font), the letter isn’t aligned with the top of the paragraph, and it looks a bit tacky.


won’t do the trick either, unless you throw in an extra element before the paragraph for the first letter (it should work inside, but it’s not doing what I expect, at least on CodePen). Honestly, for now the best you can do here is experiment. I added


and got what I was looking for.

In the far-flung future, people will be able to use


to achieve roughly the same result.

If you want to be ready for that, use CSS feature queries!

@supports (initial-letter:1) or (-webkit-initial-letter:1) {
  article p:first-of-type:first-letter {

The main reason I’m so insistent on styling with :first-letter (contrary to the advice given in this article) is because I do have visually impaired friends and relatives. While I’m fairly certain these individuals use a computer minimally, if at all, I’m sensitive to the ease of use for them. I do intend to make one of these articles focused specifically on accessibility, but for now, take the exhortation to use semantic elements as much as possible, and ensure your site is usable sans any styling.

The last thing I’m going to cover in this “basics” article is length units. Despite the fact that I used pixels throughout my stylesheet, it’s kind of bad form. With “retina” displays and different browser interpretations, a pixel isn’t always a pixel anymore. Also, with modern CSS, we have a lot more units available to us! The first 7 of the long list below are the ones to avoid, especially when defining font sizes. Some of them are more reliable than others. Nobody supports “cap”, “ic”, “ih”, “vb”, or “vi” yet; support for “Q” is spotty, and support for “vmin” and “vmax” is inconsistent. Even so, you have a lot of options!

  • px (pixel, often, but not always, defined as 1/96in)
  • in (inch)
  • cm (centimeter)
  • mm (millimeter)
  • Q (1/4mm)
  • pc (pica, defined 12pt)
  • pt (point, defined as 1/72in)
  • em (calculated font size of the element)
  • ex (ideally the height of lowercase (Latin) letters in the current font, often equal to 0.5em)
  • cap (cap height of the current font)
  • ch (advance measure of “0” in the current font)
  • ic (advance measure of “水” in the current font)
  • lh (calculated line height of the current font)
  • rem (calculated font size of the root element, <html>)
  • rlh (calculated line height of the root element, <html>)
  • vh (1% of the viewport height)
  • vw (1% of the viewport width)
  • vi (1% of the initial containing block in the direction of the inline axis)
  • vb (1% of the initial containing block in the direction of the block axis)
  • vmin (smaller of vh and vw)
  • vmax (larger of vh and vw)
  • % (percentage)

HTML Basics (First Steps)

HTML is (typically) what browsers interpret to display web pages. I’m not going to cover syntax in (many of) these documents, the purpose is to give a sense of structure. Also, don’t get peeved if I play a bit fast and loose with the terminology. If you want to know the deets, check out w3.org.

While HTML used to contain presentational markup like font and center tags, those are gone. b, i, hr, s, small, and u have been redefined “to be media-independent.” People realized that not everyone has access to two working eyes, and screen readers may have difficulty figuring out what, on some cluttered page, might be important.

Thus, HTML is now almost entirely semantic The style attribute and style element exist, but should be considered developer tools rather than production tools.

When writing a new, modern, compliant HTML document, one thing to keep in mind is that it should be usable without any styles beyond the browser’s defaults. Heck, take a look at it using lynx or some other text-based browser. Is it still understandable? Does the most important content still jump out at you? If it’s buried under menus and polls and input boxes and RSS feeds, you might want to move some things around.

If you worked with DTDs “back in the day,” you might recall starting all your HTML documents with something like

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "https://www.w3.org/TR/html4/strict.dtd">

This helped during the big browser wars on whether or not to use “quirks mode” to render a page. Now that things have settled down and we’re left with little browser skirmishes instead, HTML 5 simplified things to just

<!DOCTYPE html>

For a little bit of historical context,  browsers used to use the DTD to validate HTML as a subset of SGML. Browsers don’t implement HTML as SGML, though, so who needs a DTD?

There are rules specifying whether or not some elements are required, whether the start or end element can be omitted, but for sanity’s sake, just use them.

Things that are encouraged:

  • Include a “lang” attribute in the HTML element (see here, here, and here for more information)
  • Include a meta element to define character encoding (this tag must be closed within the first 1024 bytes of the beginning of the file)
  • Include a meta element defining viewport, but do not restrict users from the ability to zoom! (The latter part is just bad form, but I’ll explain the why for the first part in the next article)

Thus, your minimal document should look a bit like this:

<!doctype html>
<html lang="en-US">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Document Title</title>

There are lots of other things you might want to add to your document header. Make it look good on Google, have a shortcut icon when people bookmark your page, including open graph data for Facebook, titles for Twitter, and more. I might touch on them later, but if you’re interested, they might be worth a look.

Sins of My Youth

This will be the last post for a while (I think) that won’t have a song title as the post title. The focus of the blog hasn’t changed, but it’s tough to find anything.

I’m going through a journey now. Whether that leads me out of my current career into another one, into entrepreneurship, or staying put with a side gig, I don’t know yet, but I’m not going to go it alone. I may begin by shouting into the void, but I hope someone, someday, takes the journey with me.

Just a preview of what’s to come:

I’m an “old timer” of sorts when it comes to web development. I started with HTML 3.2, Netscape Navigator was king, <blink> and <marquee> were all the rage. I’m sure there are some who have me beat by several years yet. But college and work pushed most of that to the side, and I haven’t done much in the past 18 years. I fiddled with the odd website here and there, even designed and managed a few, but anyone hiring for a web dev job wants experience in technologies that I’ve only heard of in passing.

This is my journey of reinventing myself. Of learning the arcane arts of Node, Angular, React, Typescript, etc. Of perhaps, someday, combining my enjoyment of mathematical beauty, machine learning, the internet, and food into something amazing.

Supposedly Bruce Lee said “Notice that the stiffest tree is most easily cracked while the bamboo survives by bending with the wind.”

It’s time to adapt. To move from “old and busted” to the “new hotness”, (without sacrificing utility for popularity—maybe “old and busted” has an anti-gravity button and an Elvis 8-track up his sleeve). Everyone might say that their paradigm or framework or package manager is the best, but they probably all have strengths and weaknesses. Let’s find out what they are together, and maybe make the world a better place!

Алфавит мы уже знаем

Þe Olde Alphabet

We used to have a bunch of other cool letters, and, of course, thorn (Þþ) was one of them. I didn’t know before today that the “Y” in “Ye Olde ____” was from the abbreviation, “þe“. It was also used in a couple of other nifty abbreviations: “þt” for “that” and “þs” for “this”. We even had an entire letter for “that”: ꝥ.

The ampersand “&” used to be considered a letter unto itself, much like “ꝥ”. While it has (generally) retained its meaning (used to, more correctly, mean “and per se and”; elision converted it to “ampersand”).

I personally think a decent case can be made to make the apostrophe a letter. It can be used to distinguish “its”, “it’s”, “your”, “you’re”, “there” and “their” vs “they’re”. There are rules governing its use which are widely applicable, but they still fail sometimes, and if it was used as a letter, it might be better.

We also have (had) eth (Ðð) for use as the unvoiced dental fricative (the soft “th”).

There are the complicated rules governing the use of the long s: “ſ”, which only has a lowercase form, and is only used if it’s either the only “s” in a word and that “s” is not at the end of the word, or if it’s the first “s” in a word with two (or more?) “s”s.

There’s wynn (Ƿƿ) which is much cooler than “uu”, or as is now more commonly written, “w”, for…well, the sound that “w” makes.

The semi-pretentious ath (ニæ) and ethel (Œœ), and, while the first is supposed to denote a sound “somewhere between ‘A’ and ‘E'” (like, well, æ in the IPA) the second has many sounds (like ɛ, e, i, or iː in the IPA).

Then there’s yogh (Ȝȝ) which probably went away because of how much it looks like the number 3. Or is that a Ȝ… can you even tell? Maybe the font gives it away, but still. This could have been used for things like Baȝ, or Loȝ Ness, or in German or Klingon transcription… The demise of this letter, which has been replaced with “gh”, may explain why many of the “gh” combos in English are now silent.

And eng (Ŋŋ, not the right half of the original Siamese twins) which a guy named “Alexander Gill the Elder” (we know the name of a guy who made up a letter!) created to replace the combination “ng”. Sometimes it was written as ⅁ or g̶, or as G with a descender.


There are now tons of meal kit delivery services out there: Blue Apron, Green Chef, Hello Fresh, Plated, Munchery, Terra’s Kitchen, Fresh Direct, Peach Dish, Chef’d, Takeout Kit, Purple Carrot, and probably more, but despite the variety on offer, few of their menu items are appetizing to me.

A selection from this week’s menu: Khao Soi Burmese Curry Noodles? Rainbow quinoa with pickled red beets and crispy bay-spiced sole? Corn and summer squash risotto with basil and gruyere cheese? Wild swordfish with chimichurri, butternut squash and roasted vegetables? Coconut jasmine rice with fried plantains and corn pico de gallo? Sheet pan-roasted chicken with lemon-arugula potato salad? Beef and eggplant stir-fry with roasted shishito peppers? Soba noodles with snow peas and marinated enoki mushrooms? Chickpea-powered Mediterranean couscous with zucchini and heirloom grape tomatoes?

Some (apparently quite a few) people find this type of variety perfect for their lifestyle. I prefer simple: Meatloaf. Sloppy joe’s. Mac & cheese. Pepperoni pizza. There’s no “with quinoa and arugula tahini and açai zucchini sauce” or any of that nonsense. There’s rarely a “with” involved at all, save perhaps a bit of salt and pepper to taste. Now, I also might be willing to go a bit more adventurous on a few things, like the lemon-herb salmon with Greek feta rice, but—problem—this serves two. I highly doubt I could get my wife to eat it. She’s quite satisfied with a grilled cheese sandwich, or even peanut butter and jelly, with a handful of potato chips and a bottle of water.

My life may be getting a bit more complicated. As of this writing I am…let’s just say overweight, but willing to do something about it. Going to a restaurant where the average item is 1200 calories for the entree (with unlimited french fries) certainly doesn’t help matters, even if it is an occasional thing. My wife is wont to box half of it up and eat it the next day. I’ll eat it all. If I were to box it up, it would be in the refrigerator for 1–3 weeks before winding up in the trash anyway.

What I’d love to see is a meal delivery service that caters to people who are, perhaps, less adventurous, with more of a pick-and-choose ideal, along with recipes for one. I’d be more willing and able to expand my palate if my wife can stick with a can of soup. And make the ingredients such that they’ll survive an extra day or two in the fridge, or so that they can be frozen (with appropriate defrosting instructions) because life happens, and schedules change at the last minute. Some people might think a particular dish sounds great, if they just left out the mushrooms. No opting out as it stands, they go in the trash can when they arrive. How about options for the true novice, who wants to practice in the kitchen. They’re in the “how to boil water” category, not the “stir-fry the chicken with the quinoa and shallots” (“What’s quinoa?” “What’s a shallot?” How do I stir-fry something?” “How do I know when it’s done?”) category. Some people have a rice cooker and garlic press, most probably don’t. Some people have an oven, some only have a crock-pot, or even just a hot-plate and/or microwave. These people are left out.

And another thing! These meal kit delivery services, even though most have their own gimmicks, pretty much all go “wild-caught”, “GMO-free”, “certified organic”, “no antibiotics ever” with their ingredients. I call BS. Stuff your “wild-caught” and go for sustainable. Fuck that “GMO-free” and use what’s reasonable. Those promoting “certified organic” are certifiable. While I agree that antibiotics shouldn’t be misused with livestock, “no antibiotics ever” mean that if any animal gets sick, it may as well be killed and left to rot (away from the herd) because it’s of no use to the farmer. If it can be returned to health with appropriate use of antibiotics, put it back with the herd, no problems.


Just going to throw some music recommendations out there. As usual, they aren’t for everyone, but there recent discoveries I’ve made through various places. I’ll add more later, but my headphone battery was low, so I stopped here.

  • Garbage (reminded of this band by Claudia and Claire Donovan on Warehouse 13) – alt-rock/punk
  • Quiet Company (a blog post introduced me to this band and their album, “We Are All Where We Belong”) – folk rock
  • Larkin Poe (from a different blog post: “Rebecca and Megan Lovell are sisters, and they claim descent from Edgar Allan Poe.”) – southern rock/blues
  • Jukebox the Ghost (suggested by Google Play Music)

Peter Percival Patterson’s Pet Pig Porky

π! I can rattle off 36 significant figures on demand, but how accurate do you really need to be? The JPL uses 15 digits for interplanetary navigation. What is needed, though?

Let’s start with a 1 m diameter circle, and add successive digits to the approximation. How many digits before you’re being absurd? I’ll give a definition of absurd, too: a difference in the circumference of less than the Planck length, approximately \(\LARGE{1.616229(38)\times10^{-35} {\rm m}}\)

  • For 1 m, the answer is fairly obvious: 36 significant figures
  • For d, 43 significant figures are needed
  • For the distance from the sun to Voyager 1, ~141 AU at time of writing, just 49 significant figures
  • For the Solar System, out to the distance where we believe the Oort cloud ends, ~100000 AU (i.e., the edge of the solar system), 53 significant figures
  • For the size of our observable universe, roughly 93000000000 light years, 63 is enough

What if the Bohr radius (approximate size of a hydrogen atom) was our standard, at \(\LARGE{5.2917721067(12)\times10^{-11}{\rm m}}\)? For the size of the observable universe, we only need 36 figures. Handily, that’s what I know! How much would one need for a circle the size of the orbit of Neptune? 23 digits.

So how accurate is JPL if they were measuring the circumference of a circle at the orbit of Neptune? They could be off by as much as a centimeter! Which, assuming appropriate feedback systems, isn’t going to be anything near significant.

How many do we know? 22459157718361 digits. Why? As George Leigh-Mallory put it, “Because it’s there”.

Who Are You

There seems to be a dearth of articles on setting up full-fledged “automatic” user registration systems for Django 2.0. Yes, there’s documentation, but there’s a lot to wade through, and it’s far from a step-by-step guide, which is what this aspires to be.

The problem for developing many web applications using Django is that adding a user requires logging in through the admin interface to do so. For smaller groups, say, 20 or so, this might be manageable, though annoying. If you develop for deployment to a wide audience, say the entire English speaking world, you don’t want people to email you requests all the time to be added so they can do whatever in your app.

I’ll be assuming that you have a passing familiarity with Django, and have made it through the tutorial and polls app. I’m also assuming that this is among the first things you’re setting up, and you won’t mind blowing away any data you have, because that’s the simplest way to do things in Step 3.

Step 1: Settings

You can customize these as your needs dictate, but here’s a bunch more things to add to your mysite.settings:

LOGIN_URL = '/accounts/login/'
## Possible email backends. The uncommented one, also the default, is the one we want to use
##   by the end, but the others can be used in development. I find console the simplest for
##   that.
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
## Console
#EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
## File
#EMAIL_BACKEND = 'django.core.mail.backends.file.EmailBackend'
#EMAIL_FILE_PATH =  '/tmp/app-messages'
## In Memory (django.core.mail.outbox is a list of EmailMessage instances)
#EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'
## Dummy (does nothing--same as /dev/null)
#EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend'

## All options below are the default values. Unless you run your own SMTP server on localhost
##   with no authentication required (not recommended), you'll probably want to tweak these.
EMAIL_HOST = 'localhost'
EMAIL_HOST_USER = '' #If this is the empty string, no authentication will be attempted
## The next two options are mutually exclusive.
EMAIL_SSL_KEYFILE = None #If EMAIL_USE_TLS or EMAIL_USE_SSL are True, specify the path to a
                         #  PEM formatted private key.
EMAIL_SSL_CERTFILE = None #If EMAIL_USE_TLS or EMAIL_USE_SSL are True, specify the path to a
                          #  PEM formatted certificate chain
EMAIL_USE_LOCALTIME = False #The default here will timestamp your message with UTC.

Step 2: Some Templates

The next thing we want to do is make a bunch of templates. It’s a tad backward from how Django is normally developed, but trust me. As always, customize as you wish.

In your templates directory, create a “registration” subdirectory. All these templates will live in there.

{% extends "base.html" %}

{% block content %}

{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}

{% if next %}
    {% if user.is_authenticated %}
    <p>Your account doesn't have access to this page. To proceed,
    please login with an account that has access.</p>
    {% else %}
    <p>Please login to see this page.</p>
    {% endif %}
{% endif %}

<form method="post">
{% csrf_token %}
    <td>{{ form.username.label_tag }}</td>
    <td>{{ form.username }}</td>
    <td>{{ form.password.label_tag }}</td>
    <td>{{ form.password }}</td>

<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />

{# Assumes you setup the password_reset view in your URLconf #}
<p><a href="{% url 'password_reset' %}">Lost password?</a></p>

{% endblock %}
{% extends "base.html" %}

{% block content %}

<form method="post">
{% csrf_token %}
    <td>{{ form.email.label_tag }}</td>
    <td>{{ form.email }}</td>

<input type="submit" value="reset" />
<input type="hidden" name="next" value="{{ next }}" />

{% endblock %}
Someone asked for password reset for email {{ email }}. Follow the link below:
{{ protocol}}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
[Django] Password Reset
{% extends "base.html" %}

{% block content %}
<p>You should receieve an email shortly (if an account exists with the e-mail you've
entered) with instructions to reset your password. If you don't receive the email, make sure
you entered the email you registered with, and check your spam folder.</p>
{% endblock content %}
{% extends "base.html" %}

{% block content %}

<form method="post">
{% csrf_token %}
    <td>{{ form.new_password1.label_tag }}</td>
    <td>{{ form.new_password1 }}</td>
    <td>{{ form.new_password2.label_tag }}</td>
    <td>{{ form.new_password2 }}</td>

<input type="submit" value="reset" />
<input type="hidden" name="next" value="{{ next }}" />

{% endblock %}
{% extends "base.html" %}

{% block content %}

<p>Your password has been reset. <a href="{% url 'login' %}">Log in?</a></p>

{% endblock %}

Play around with the workflow regarding resetting passwords with your superuser, and make sure all these parts play together. If you want to try the same thing with a “regular” user (there won’t be a visible difference unless you make one in the templates), add one through the admin interface.

Step 3: Altering the User model

Personally, I prefer websites that ask me to log in using my email address. On the wider Internet, it’s guaranteed to be unique so I don’t have to come up with a username like james53401, and I won’t have to remember if I used james53401 on this website or james2557 instead. Yes, I have a password manager that does most of that heavy lifting, but that’s still something that annoys me.

Anyway, the first thing to do would be change the user model to accept an email address instead of a username for login. Easier said than done, though. We get to create a brand new model! You can create an app specifically for the user (./manage.py startapp testuser, which I will assume below) or add the model to an existing app, that’s up to you. Either way, the app will need to be added to the INSTALLED_APPS list in mysite.settings. Also, you’ll need to add this to mysite.settings:



from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
import datetime

class MyUserManager(BaseUserManager):
    def create_user(self, email, password=None):
        if not email:
            raise ValueError('Users must have an email address')
        user = self.model(
        return user
    def create_superuser(self, email, password):
        user = self.create_user(
        user.is_admin = True
        return user
class MyUser(PermissionsMixin,AbstractBaseUser):
    is_active = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)
    def __str__(self):
        return self.email
    def is_staff(self):
        return self.is_admin
    def is_superuser(self):
        return self.is_admin
from django import forms
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField

from testuser.models import MyUser

class UserCreationForm(forms.ModelForm):
    password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
    password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
    class Meta:
        model = MyUser
        fields = ('email',)
    def clean_password2(self):
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        if password1 and password2 and password1 != password2:
            raise forms.ValidationError("Passwords don't match")
        return password2
    def save(self, commit=True):
        user = super().save(commit=False)
        if commit:
        return user
class UserChangeForm(forms.ModelForm):
    #this prevents anyone from accidentally screwing up the password hash
    password = ReadOnlyPasswordHashField()
    class Meta:
        model = MyUser
        fields = ('email', 'password', 'is_active', 'is_admin')
    def clean_password(self):
        #this prevents anyone from accidentally screwing up the password hash
        return self.initial["password"]
class UserAdmin(BaseUserAdmin):
    form = UserChangeForm
    add_form = UserCreationForm
    list_display = ('email', 'is_admin')
    list_filter = ('is_admin',)
    fieldsets = (
        (None, {'fields': ('email', 'password')}),
        ('Permissions', {'fields': ('is_admin',)}),
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('email', 'password1', 'password2')}
    search_fields = ('email',)
    ordering = ('email',)
    filter_horizontal = ()

# Now register the new UserAdmin...
admin.site.register(MyUser, UserAdmin)

Now, we delete everything. If you’re using a MySQL, PostgreSQL, etc. type database, drop all tables. If you’re using sqlite3, delete the database file. Delete all migrations folders from the apps. We “get” to start over.

Now run the following:

./manage.py makemigrations testuser
./manage.py migrate

If you run your server now and go to the login page, there’s still no way to create a user. Let’s fix that.

Step 4: Adding Registration

Add the following to the appropriate views.py:

from django.views.generic.edit import CreateView
from django.urls import reverse_lazy
from .admin import UserCreationForm

class UserCreationView(CreateView):

And this to your urls.py:

urlpatterns = [

Nearly there!

Step 5: Verifying the Email Address

I’ve also found in my long years trawling the Internet that verifying email addresses is a necessity. I’ve wound up on a mailing list for a travel agency in South Africa, a Botox clinic in Chicago, a Mazda dealership in Texas, and a home remodelling firm in Denver, associated with people named Johann, Joni, Jacci, and others. Instagram was nice enough to send me an e-mail a few dozen times when a Jessica attempted to use my email address to sign up for an account (to the point where I signed up for one just to stop the emails). But to avoid being a bad apple, let’s add in a way to verify the person signed up for an account intentionally, and entered the right email address.

The way we’ll do this is to not allow the user to set a password until they have verified the email address. This sounds a lot like resetting a password, right? So we’ll take advantage of that.

First thing, change UserCreationForm to the following:

class UserCreationForm(forms.ModelForm):
    class Meta:
        model = MyUser
        fields = ('email',)
    def save(self, commit=True):
        user = super().save(commit=False)
        if commit:
        return user

At the moment, it’ll automatically pop the user to the login form, where they can click “Forgot password?” and get a link that way, but we’ll try to streamline that.


So a Gaussian integer is simply a complex number with real and imaginary parts that are both integers, i.e. \(\LARGE{{\mathbb Z}[i]=\left\{ a + bi\ |\ a,b \in {\mathbb Z}\right\}}\). It has a “norm”, which is the square of its absolute value, so \(\LARGE{\left|a+bi\right|^2=a^2+b^2}\). The “units” of Gaussian integers are \(\LARGE{1, -1, i, -i}\). Nothing new.

I’m trying to understand Gaussian primes, and am finding a dearth of information out there. I find that, for instance, Gaussian primes include the Gaussian units multiplied by primes if and only if it is congruent to \(\LARGE{3\mod4}\). Why? This is not explained. Here’s my investigation into this and other questions.

It seems fairly intuitive that a Gaussian prime can be a prime number multiplied by a Gaussian unit, e.g. \(\LARGE{(2,3,5,7,11…)\times(\pm1,\pm i)}\). But \(\LARGE{2}\) (and, by extension, \(\LARGE{-2}\), \(\LARGE{2i}\), and \(\LARGE{-2i}\), are not. What’s the deal?

Well, a prime cannot be factored in the domain of its definition. Since we’re dealing with Gaussian integers, \(\LARGE{2}\) must have a factorization within those Gaussian integers. What could that  be?Turns out, \(\LARGE{(1+i)(1-i)=1^2+i-i-i^2=1+1=2}\). That’s of course a special case.

What about 5, 13, 17, 29, etc.? There must be factorizations within Gaussian integers for these. Because we’re dealing with complex integers, if you multiply conjugates, you can add a 1 pretty easily. 5 is 4+1, or \(\LARGE{2^2-i^2}\), therefore \(\LARGE{(2+i)(2-i)=2^2+i-i-i^2=4-(-1)=5}\). We have to get slightly more creative with 13: \(\LARGE{(3+2i)(3-2i)=3^2+2i-2i-(2i)^2=9-(-4)=13}\). \(\LARGE{17=4^2+1}\), which, as mentioned, is simple. \(\LARGE{29=5^2+4}\). 37 is easy. 41? Trickier, but we’re still talking sums of squares: 25+16=41. 53=49+4; 61=25+36; 73=9+64; 89=25+64; 97=16+81. Turns out, you can do this for any prime that is congruent to \(\LARGE{1\mod4}\). Fermat even had one of his famous unproven theorems stating so. It was proven as early as 1747. I won’t pretend to understand those proofs yet, but I will accept that this is true. The one that I understand best is the one explained in this video, and was originally stated in a single sentence by Zagier.

But wait! Our factorizations aren’t unique! What if we went with \(\LARGE{(1-2i)(1+2i)=1^2-2i+2i-(2i)^2=1+4=5}\)? This factorization is the same as multiplying each term in the original factorization shown by \(\LARGE{i}\), which, as previously discussed, is a unit. Likewise, one can factor \(\LARGE{6}\) into \(\LARGE{2}\) and \(\LARGE{3}\) or into \(\LARGE{-2}\) and \(\LARGE{-3}\), and these aren’t (typically) considered unique factorizations.

A way to deal with this is to “normalize” one’s choices. The two suggested methods I’ve found are to choose \(\LARGE{p=a+bi}\) such that \(\LARGE{a}\) is always odd and positive and \(\LARGE{b}\) is always even, or to choose \(\LARGE{p}\) such that \(\LARGE{p\mod(2+2i)=i}\).

Even so, coming up with factorizations is cumbersome and perhaps the simplest thing to do is multiply the integers together and find the gaps to get the primes; an analog of the Sieve of Eratosthenes for Gaussian integers. Thus, we have the following:


  • \(\LARGE{\pm1,\pm i}\)


  • \(\LARGE{1+i}\)
  • \(\LARGE{1+2i}\)
  • \(\LARGE{1+4i}\)
  • \(\LARGE{2+i}\)
  • \(\LARGE{2+3i}\)
  • \(\LARGE{2+5i}\)
  • \(\LARGE{3}\)
  • \(\LARGE{3+2i}\)

All the results from multiplying the units by the primes can also be considered prime.

Because of the above, a Gaussian prime \(\LARGE{a+bi}\) fits one of the following scenarios:

  • \(\LARGE{a=0}\), and \(\LARGE{b\equiv3\mod4}\)
  • \(\LARGE{b=0}\), and \(\LARGE{a\equiv3\mod4}\)
  • \(\LARGE{a^2+b^2}\) is prime

See this video for more. I hope he puts together one on Fermat’s theorem on sums of two squares at some point.

Hot Stuff

With Donald Trump officially announcing the US withdrawal from the Paris Climate Agreement, I feel compelled to write a post about climate change. I do find it amusing that the earliest possible effective date of the US withdrawal is a day after the 2020 election, but Donald Trump (presumably) will be president until 20 January 2021.

Anyway, brass tacks.

Anthropogenic Global Warming Is Real

I know few who deny anymore that global warming is happening (people like this guy are obvious exceptions). The possibility was raised in 1824. Humans were producing, in 1896, as much CO2 as natural sources (e.g. volcanoes), but the same scientist who calculated this concluded that humans were, through the burning of coal, a major contributor to climate change, and in the global warming direction.

Numerous scientists began to postulate as to why the temperature of the globe seemed to be increasing, even early in the 20th century. Sunspot cycles were proposed in the 20s, but rejected by most scientists by the 30s. Milanković proposed changes in the Earth’s orbit, but these cycles were determined to be far too slow to be the major contributor to what was observed at the time.

I will note that it wasn’t until the late 50s that a majority of scientists in the field were concerned about potentially “radical” effects on the climate. While in 1968, some remained unsure whether moderate cooling effects of particulate pollution (smog) or the warming effects of CO2 emissions would dominate, a mere seven out of 51 articles published around that time predicted global cooling—the rest sided with the warming hypothesis.

1981 seems to mark the turning point where an overwhelming majority of scientists agreed with the idea that, not only was the globe warming, but that humans were the primary cause of that warming.

ExxonMobil has a nice list of documents and publications from the 70s and 80s indicating that they were/are aware of the warming impact of CO2 release. And here’s 104 more publications they contributed to. Every last one of them says increasing atmospheric carbon dioxide is causing global warming, and that humans burning fossil fuels are causing the increase in atmospheric carbon dioxide (to be fully honest, I have only read a few of these, but it gives me some confidence that I’m not entirely mischaracterizing the tenor of these papers).

I was going to make a nice long essay out of this, but I’ll just stick to saying this: It’s happening, people! Do something about it!