Site works great yet why can't remove these MultipleChoiceField exceptions?

I have a form with a MultipleChoiceField. Everything works great.
Nevertheless I set logging to DEBUG and keep seeing lots of exceptions like this
for this MultipleChoiceField… (The field is called rec_cus_d.)

Why?

Exception while resolving variable ‘class’ in template ‘django/forms/widgets/checkbox_select.html’.
Traceback (most recent call last):
File “/usr/lib/python3/dist-packages/django/template/base.py”, line 829, in _resolve_lookup
current = current[bit]
KeyError: ‘class’

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File “/usr/lib/python3/dist-packages/django/template/base.py”, line 837, in _resolve_lookup
current = getattr(current, bit)
AttributeError: ‘dict’ object has no attribute ‘class’

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File “/usr/lib/python3/dist-packages/django/template/base.py”, line 843, in _resolve_lookup
current = current[int(bit)]
ValueError: invalid literal for int() with base 10: ‘class’

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File “/usr/lib/python3/dist-packages/django/template/base.py”, line 848, in _resolve_lookup
raise VariableDoesNotExist("Failed lookup for key "
django.template.base.VariableDoesNotExist: Failed lookup for key [class] in {‘id’: ‘id_rec_cus_d’}
Exception while resolving variable ‘class’ in template ‘django/forms/widgets/checkbox_select.html’.
Traceback (most recent call last):
File “/usr/lib/python3/dist-packages/django/template/base.py”, line 829, in _resolve_lookup
current = current[bit]
KeyError: ‘class’

Is the form working, but you’re just concerned about those exceptions? Or is the form not working at all?

What version of Django and what version of Python?

If the form’s not working, it’ll be helpful to see the model, view, and form involved here.

When posting code (or tracebacks), enclose each file between lines of three backtick - ` characters. This means you’ll have a line of ```, then your code, then another line of ```. (The lines of ``` must be on lines by themselves.)
This allows the forum software to keep your code properly formatted, which is particularly important with Python.

If the form is working properly, then I wouldn’t be worried about these messages. My guess would be that they’re caused by the template attempting to reference or render a variable not supplied in the context, which is valid within the Django template system.

Ken

Thanks for asking! The version of Django is 2:2.2.12-1ubuntu0.7 for Python 3 on
a Ubuntu Linux 20.04 LTS AWS VM.

Everything is working perfectly but I would still love to know why these error messages
still appear. At first I assumed they were happening in a try block somewhere but that
isn’t the case. These messages are actually a real problem because they flood my log files with noise which may prevent me noticing something else I need to see in there in the future.

Can you elaborate what you mean by “My guess would be that they’re caused by the template attempting to reference or render a variable not supplied in the context, which is valid within the Django template system.” ? I want to make sure I understand.

Here is my forms.py. The culprits are the few places the MULT_CH variable is used
which makes a MultipleChoiceField.

import bighelp.models
import django.forms
import django.core.validators
import django.contrib.auth.models
import captcha.fields
import datetime
import re

CUST       = bighelp.models.Customer
APPT       = bighelp.models.Appointment
INV        = bighelp.models.Invoice
ACCT       = bighelp.models.Account
PROV       = bighelp.models.Provider
SERV       = bighelp.models.Service
ASERV      = bighelp.models.AccountService
USER       = django.contrib.auth.models.User
MIN        = django.core.validators.MinValueValidator
MAX        = django.core.validators.MaxValueValidator
HIDD_INP   = django.forms.HiddenInput
PASS_INP   = django.forms.PasswordInput
CHECK_BOX  = django.forms.CheckboxSelectMultiple
MULT_CH    = django.forms.MultipleChoiceField
RADIO      = django.forms.RadioSelect
TEXT_AREA  = django.forms.Textarea
VAL_ERR    = django.forms.ValidationError
AM_OR_PM   = django.forms.Select(choices = [("AM", "AM"), ("PM", "PM")])
BILLING    = ["monthly", "weekly", "irregular"]
BILLING    = django.forms.Select(choices = [(e, e) for e in BILLING])
DAYS       = [("6", "Sunday"),
              ("0", "Monday"),
              ("1", "Tuesday"),
              ("2", "Wednesday"),
              ("3", "Thursday"),
              ("4", "Friday"),
              ("5", "Saturday")]
INT_UNITS  = ["day(s)", "week(s)", "month(s)", "year(s)"]
INT_UNITS  = django.forms.Select(choices = [(e, e) for e in INT_UNITS])
STATES     = ["AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DE", "FL", "GA",
              "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MD",
              "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ",
              "NM", "NY", "NC", "ND", "OH", "OK", "OR", "PA", "RI", "SC",
              "SD", "TN", "TX", "UT", "VT", "VA", "WA", "WV", "WI", "WY"]
STATES     = django.forms.Select(choices = [(e, e) for e in STATES])
RECURRING  = ["none", "daily", "weekly", "weekdays", "monthly", "yearly",
                                                                       "custom"]
RECURRING  = django.forms.Select(choices = [(e, e) for e in RECURRING])
APPT_SET   = [("1", "Modify this appointment only."),
              ("2", "Modify this and the following appointments in the set."),
              ("3", "Modify all the appointments in the set.")]
WINDOW     = [("0.0",  "none"),
              ("0.25", "15 minutes"),
              ("0.5",  "30 minutes"),
              ("0.75", "45 minutes"),
              ("1.0",  "1 hour")]
WINDOW    += [(f"{n}.0", f"{n} hours") for n in range(2, 13)]
WINDOW     = django.forms.Select(choices = WINDOW)
CUR_YEAR   = datetime.date.today().year
INIT_FOOT  = \
"""
Payment Methods: Zelle Venmo Check Cash

Thank you for your business!
""".strip()

def fix_phone(phone):
        for e in "()- ":
                phone = phone.replace(e, "")
        if re.fullmatch(10 * "\d", phone):
                phone = f"({phone[:3]}) {phone[3:6]}-{phone[6:]}"
        else:
                phone = None

        return phone

def rec_end_checks(beg, earliest, latest, cleaned_data):
        try:
                end = datetime.date(cleaned_data["rec_end_y"],
                                    cleaned_data["rec_end_m"],
                                    cleaned_data["rec_end_d"])
        except (ValueError, TypeError):
                raise VAL_ERR("invalid recurring end date")
        if (end <= beg) or not (earliest <= end <= latest):
                raise VAL_ERR("invalid recurring end date")

def rec_cus_check(cleaned_data):
        if (cleaned_data["rec_type"]  == "custom")  and                        \
           (cleaned_data["rec_cus_u"] == "week(s)") and                        \
           not cleaned_data["rec_cus_d"]:
                raise VAL_ERR("missing recurring custom days")

def rec_type_check(cleaned_data):
        if cleaned_data["rec_end_m"] and cleaned_data["rec_end_d"] and         \
                                                    cleaned_data["rec_end_y"]:
                raise VAL_ERR("missing recurring type")



class AppointmentNewForm(django.forms.Form):
        def __init__(self, *args, **kwargs):
                user   = kwargs["user"]
                del(kwargs["user"])
                super().__init__(*args, **kwargs)
                aservs = ASERV.objects.filter(account__user = user)
                servs  = [2 * (e.service.service,) for e in aservs]
                servs  = django.forms.Select(choices = servs)
                self.fields["service"] = django.forms.CharField(widget = servs)

        customer  = django.forms.IntegerField()
        provider  = django.forms.IntegerField()
        month     = django.forms.IntegerField(validators = [MIN(1), MAX(12)])
        day       = django.forms.IntegerField()
        year      = django.forms.IntegerField()
        hour      = django.forms.IntegerField(validators = [MIN(1), MAX(12)])
        minute    = django.forms.IntegerField(validators = [MIN(0), MAX(59)])
        am_or_pm  = django.forms.CharField(widget = AM_OR_PM, initial = "PM")
        window    = django.forms.FloatField(widget = WINDOW, initial  = 0)
        price     = django.forms.DecimalField()
        remind    = django.forms.BooleanField(required = False)
        rem_text  = django.forms.CharField(widget   = TEXT_AREA,
                                           required = False)
        rec_type  = django.forms.CharField(widget   = RECURRING,
                                           required = False)
        rec_end_m = django.forms.IntegerField(validators = [MIN(1), MAX(12)],
                                              required   = False)
        rec_end_d = django.forms.IntegerField(validators = [MIN(1), MAX(31)],
                                              required   = False)
        rec_end_y = django.forms.IntegerField(validators = [MIN(CUR_YEAR)],
                                              required   = False)
        rec_cus_n = django.forms.IntegerField(validators = [MIN(1)],
                                              initial    = 1,
                                              required   = False)
        rec_cus_u = django.forms.CharField(widget   = INT_UNITS,
                                           initial  = "week(s)",
                                           required = False)
        rec_cus_d = MULT_CH(choices  = DAYS,
                            widget   = CHECK_BOX,
                            required = False)
        account   = django.forms.IntegerField(widget = HIDD_INP)
        original  = django.forms.IntegerField(widget   = HIDD_INP,
                                              required = False)
        back_to   = django.forms.CharField(widget   = HIDD_INP,
                                           required = False)
        appt_set  = django.forms.ChoiceField(widget   = RADIO,
                                             choices  = APPT_SET,
                                             initial  = "1",
                                             required = False)

        def clean(self):
                for e in ["month", "day", "year"]:
                        if e not in self.cleaned_data:
                                raise VAL_ERR("invalid appointment date")
                try:
                        beg = datetime.date(self.cleaned_data["year"],
                                            self.cleaned_data["month"],
                                            self.cleaned_data["day"])
                except ValueError:
                        raise VAL_ERR("invalid appointment date")
                today    = datetime.date.today()
                earliest = today - datetime.timedelta(days = 90)
                latest   = today + datetime.timedelta(days = 366)
                if not (earliest <= beg <= latest):
                        raise VAL_ERR("invalid appointment date")
                if not self.cleaned_data["original"]:
                        if self.cleaned_data["rec_type"] != "none":
                                rec_end_checks(beg,
                                               earliest,
                                               latest,
                                               self.cleaned_data)
                                rec_cus_check(self.cleaned_data)
                        else:
                                rec_type_check(self.cleaned_data)



Here is my models.py:

import django.db.models
import django.conf

USER     = django.conf.settings.AUTH_USER_MODEL
CASCADE  = django.db.models.CASCADE
SET_NULL = django.db.models.SET_NULL

class Account(django.db.models.Model):
        user       = django.db.models.OneToOneField(USER, on_delete = CASCADE)
        business   = django.db.models.CharField(max_length = 10000)
        phone      = django.db.models.CharField(max_length = 10000)
        add_top    = django.db.models.CharField(max_length = 10000)
        city       = django.db.models.CharField(max_length = 10000)
        state      = django.db.models.CharField(max_length = 10000)
        zip_code   = django.db.models.CharField(max_length = 10000)
        remind_0   = django.db.models.BooleanField()
        update_0   = django.db.models.BooleanField()
        send_inv_0 = django.db.models.BooleanField()

class AppointmentSet(django.db.models.Model):
        account  = django.db.models.ForeignKey(Account,  on_delete = CASCADE)
        customer = django.db.models.ForeignKey(Customer, on_delete = CASCADE)

class Appointment(django.db.models.Model):
        account   = django.db.models.ForeignKey(Account,  on_delete = CASCADE)
        customer  = django.db.models.ForeignKey(Customer, on_delete = CASCADE)
        provider  = django.db.models.ForeignKey(Provider,
                                                null      = True,
                                                on_delete = SET_NULL)
        invoice   = django.db.models.ForeignKey(Invoice,
                                                null      = True,
                                                on_delete = SET_NULL)
        appt_set  = django.db.models.ForeignKey(AppointmentSet,
                                                null      = True,
                                                on_delete = SET_NULL)
        date      = django.db.models.DateField()
        time      = django.db.models.TimeField()
        window    = django.db.models.FloatField()
        service   = django.db.models.CharField(max_length = 10000)
        price     = django.db.models.FloatField()
        remind    = django.db.models.BooleanField()
        rem_text  = django.db.models.CharField(max_length = 1000)
        reminded  = django.db.models.BooleanField()
        completed = django.db.models.BooleanField()
        updated   = django.db.models.BooleanField()

class Service(django.db.models.Model):
        service = django.db.models.CharField(max_length = 10000)

class AccountService(django.db.models.Model):
        account = django.db.models.ForeignKey(Account, on_delete = CASCADE)
        service = django.db.models.ForeignKey(Service, on_delete = CASCADE)

Here are my views. The problem is the one called “appts” for the appointments page…

import bighelp.forms
import bighelp.models
import bighelp.add_months
import invoices.handle_pdf
import django.forms
import django.contrib.auth
import django.contrib.auth.models
import django.contrib.auth.decorators
import django.shortcuts
import django.http
import calendar
import subprocess
import datetime
import secrets
import string
import os

CUST     = bighelp.models.Customer
APPT     = bighelp.models.Appointment
INV      = bighelp.models.Invoice
ACCT     = bighelp.models.Account
PROV     = bighelp.models.Provider
SERV     = bighelp.models.Service
ASERV    = bighelp.models.AccountService
APPTSET  = bighelp.models.AppointmentSet
USER     = django.contrib.auth.models.User
AUTH     = django.contrib.auth.authenticate
CLOCK    = 12
SATURDAY = 5
SUNDAY   = 6
SEND     = "/home/cs/Ws/bighelp/send_email"
SEND_INV = "/home/cs/Ws/bighelp/invoices/send_invoice"
INV_DIR  = "/home/var/www/bighelp/static/invoices"
PHONE    = "({}) {}-{}"
UPDATE   = \
"""
{}
{} Services
Date: {}
Time: {}

{}
""".strip()
UPDATE_  = "Appointment successfully completed."
USERNAME = \
"""
Your BigHelp username is: {}

You can use it to sign in at https://bighelp.business/sign_in .
"""
PASSWORD = \
"""
Your new BigHelp password is: {}

You can use it to sign in at https://bighelp.business/sign_in .

It is recommended that you go to the settings page and create a new secure
password after you have signed in.
"""



@django.contrib.auth.decorators.login_required
def appts(request):
        acct  = ACCT.objects.get(user = request.user)
        custs = sorted(list(CUST.objects.filter(account = acct)),
                       key = lambda e : e.last)
        custs = [(f"{e.first} {e.last}", e) for e in custs]
        provs = [e for e in PROV.objects.filter(account = acct)]
        provs = sorted(provs, key = lambda e : e.user.last_name)
        provs = [(f"{e.user.first_name} {e.user.last_name}", e) for e in provs]
        if request.method == "POST":
                n_form = bighelp.forms.AppointmentNewForm(request.POST,
                                                          user = request.user)
                c_form = bighelp.forms.AppointmentCompForm(request.POST,
                                                           user = request.user)
                if   ("n_form" in request.POST) and n_form.is_valid():
                        appts_proc_an_form(n_form, request.user)
                        reply = django.shortcuts.redirect("/appts")
                elif ("c_form" in request.POST) and c_form.is_valid():
                        appts_proc_ac_form(c_form, request.user)
                        reply = django.shortcuts.redirect("/appts")
                else:
                        appts_ = appts_get_list(c_form, request.user)
                        if "c_form" in request.POST:
                                n_form = appts_make_an_form(acct)
                        reply  = django.shortcuts.render(request,
                                                         "appts.html",
                                                         {"n_form" : n_form,
                                                          "c_form" : c_form,
                                                          "custs"  : custs,
                                                          "provs"  : provs,
                                                          "appts"  : appts_})
        else:
                initial = {"year" : datetime.date.today().year}
                n_form  = appts_make_an_form(acct, initial)
                initial = {"update" : acct.update_0}
                c_form  = bighelp.forms.AppointmentCompForm(initial = initial,
                                                            user    =          \
                                                                 request.user)
                appts_  = appts_get_list(c_form, request.user)
                reply   = django.shortcuts.render(request,
                                                  "appts.html",
                                                  {"n_form" : n_form,
                                                   "c_form" : c_form,
                                                   "custs"  : custs,
                                                   "provs"  : provs,
                                                   "appts"  : appts_})

        return reply

@django.contrib.auth.decorators.login_required
def appts_caini(request):
        appts_ = appts_caini_get_list(request.user)

        return django.shortcuts.render(request,
                                       "appts_caini.html",
                                       {"appts" : appts_})

@django.contrib.auth.decorators.login_required
def appts_edit(request):
        acct   = ACCT.objects.get(user = request.user)
        result = APPT.objects.filter(id      = int(request.GET.get("appt")),
                                     account = acct)
        if not result:
                raise django.http.Http404

        return appts_edit_(request, acct, result[0])

def appts_edit_(request, acct, appt):
        custs   = CUST.objects.filter(account = acct)
        custs   = sorted(list(custs), key = lambda e : e.last)
        custs   = [(f"{e.first} {e.last}", e)
                                           for e in custs if e != appt.customer]
        custs   = [(f"{appt.customer.first} {appt.customer.last}",
                                                         appt.customer)] + custs
        provs   = [e for e in PROV.objects.filter(account = acct)]
        provs   = sorted(provs, key = lambda e : e.user.last_name)
        prov    = appt.provider
        provs   = [(f"{e.user.first_name} {e.user.last_name}", e)
                                                    for e in provs if e != prov]
        provs   = [(f"{prov.user.first_name} {prov.user.last_name}", prov)] +  \
                                                                         provs
        hour    = appt.time.hour % CLOCK
        hour    = hour if hour else 12
        initial = {"customer" : appt.customer,
                   "provider" : appt.provider,
                   "year"     : appt.date.year,
                   "month"    : f"{appt.date.month:02}",
                   "day"      : f"{appt.date.day:02}",
                   "hour"     : f"{hour:02}",
                   "minute"   : f"{appt.time.minute:02}",
                   "am_or_pm" : appt.time.strftime("%p"),
                   "window"   : appt.window,
                   "service"  : appt.service,
                   "price"    : f"{appt.price:.2f}",
                   "remind"   : appt.remind,
                   "rem_text" : appt.rem_text,
                   "account"  : acct.id,
                   "original" : appt.id,
                   "back_to"  : request.GET.get("back_to")}
        if request.method == "POST":
                form = bighelp.forms.AppointmentNewForm(request.POST,
                                                        user = request.user)
                if form.is_valid():
                        cust          = form.cleaned_data["customer"]
                        cust          = CUST.objects.get(id      = cust,
                                                         account = acct)
                        prov          = form.cleaned_data["provider"]
                        prov          = PROV.objects.filter(account = acct,
                                                            id      = prov)
                        prov          = prov[0] if prov else None
                        year          = int(form.cleaned_data["year"])
                        month         = int(form.cleaned_data["month"])
                        day           = int(form.cleaned_data["day"])
                        date          = datetime.date(year, month, day)
                        hour          = int(form.cleaned_data["hour"])
                        min_          = int(form.cleaned_data["minute"])
                        if form.cleaned_data["am_or_pm"] == "PM":
                                hour = 12 + (hour % CLOCK)
                        time          = datetime.time(hour   = hour,
                                                      minute = min_)
                        rem_text      = form.cleaned_data["rem_text"].strip()
                        appts         = [appt]
                        appt_set      = appt.appt_set
                        if   form.cleaned_data["appt_set"] == "2":
                                appts = APPT.objects.filter(appt_set  =        \
                                                                     appt_set,
                                                            date__gte =        \
                                                                    appt.date)
                        elif form.cleaned_data["appt_set"] == "3":
                                appts = APPT.objects.filter(appt_set = appt_set)
                        n_days        = date - appt.date
                        for e in appts:
                                e.customer  = cust
                                e.provider  = prov
                                e.date     += n_days
                                e.time      = time
                                e.window    = form.cleaned_data["window"]
                                e.service   = form.cleaned_data["service"]
                                e.price     = form.cleaned_data["price"]
                                e.remind    = form.cleaned_data["remind"]
                                e.rem_text  = rem_text
                                e.save()
                        back_to       = form.cleaned_data["back_to"]
                        reply         = django.shortcuts.redirect(back_to)
                else:
                        reply = django.shortcuts.render(request,
                                                        "appts_edit.html",
                                                        {"form"  : form,
                                                         "custs" : custs,
                                                         "provs" : provs,
                                                         "appt"  : appt})
        else:
                form  = bighelp.forms.AppointmentNewForm(initial = initial,
                                                         user    = request.user)
                reply = django.shortcuts.render(request,
                                                "appts_edit.html",
                                                {"form"  : form,
                                                 "custs" : custs,
                                                 "provs" : provs,
                                                 "appt"  : appt})

        return reply

@django.contrib.auth.decorators.login_required
def appts_delete(request):
        acct = ACCT.objects.get(user = request.user)
        appt = APPT.objects.get(id      = int(request.GET.get("appt")),
                                account = acct)
        appt.delete()

        return django.shortcuts.redirect(request.GET.get("back_to"))

def add_units(date, n_units, units):
        if   units == "day(s)":
                date_ = date + n_units * datetime.timedelta(days = 1)
        elif units == "week(s)":
                date_ = date + n_units * datetime.timedelta(days = 7)
        elif units == "month(s)":
                date_ = bighelp.add_months.add_months(date, n_units)
        elif units == "year(s)":
                year_ = date.year + n_units
                if (date.month == 2) and (date.day == 29) and                  \
                                     (calendar.monthrange(year_, 2)[1] == 28):
                        date_ = date.replace(year = year_, day = date.day - 1)
                else:
                        date_ = date.replace(year = year_)

        return date_

def add_dates_(dates, end, rec_cus_n, units):
        n_units = rec_cus_n
        date_   = add_units(dates[0], n_units, units)
        while date_ <= end:
                dates.append(date_)
                n_units += rec_cus_n
                date_    = add_units(dates[0], n_units, units)

def add_dates(dates, rec_type, data):
        end = datetime.date(data["rec_end_y"],
                            data["rec_end_m"],
                            data["rec_end_d"])
        if   rec_type in ["daily", "weekly", "monthly", "yearly"]:
                units = rec_type.replace("i", "y").replace("ly", "(s)")
                add_dates_(dates, end, 1, units)
        elif rec_type == "weekdays":
                add_dates_(dates, end, 1, "day(s)")
                for e in dates[:]:
                        if e.weekday() in [SATURDAY, SUNDAY]:
                                dates.remove(e)
        elif rec_type == "custom":
                if data["rec_cus_u"] != "week(s)":
                        add_dates_(dates,
                                   end,
                                   data["rec_cus_n"],
                                   data["rec_cus_u"])
                else:
                        add_dates_(dates, end, 1, "day(s)")
                        week = 0
                        for e in dates[:]:
                                if (str(e.weekday()) not in data["rec_cus_d"]) \
                                                or (week % data["rec_cus_n"]):
                                        dates.remove(e)
                                if e.weekday() == SATURDAY:
                                        week += 1

def appts_proc_an_form(form, user):
        acct     = ACCT.objects.get(user = user)
        cust     = form.cleaned_data["customer"]
        cust     = CUST.objects.get(id = cust, account = acct)
        prov     = form.cleaned_data["provider"]
        prov     = PROV.objects.filter(account = acct, id = prov)
        prov     = prov[0] if prov else acct.user
        year     = int(form.cleaned_data["year"])
        month    = int(form.cleaned_data["month"])
        day      = int(form.cleaned_data["day"])
        date     = datetime.date(year, month, day)
        hour     = int(form.cleaned_data["hour"])
        min_     = int(form.cleaned_data["minute"])
        if form.cleaned_data["am_or_pm"] == "PM":
                hour = 12 + (hour % CLOCK)
        time     = datetime.time(hour = hour, minute = min_)
        dates    = [date]
        rec_type = form.cleaned_data["rec_type"]
        appt_set = None
        if rec_type != "none":
                add_dates(dates, rec_type, form.cleaned_data)
                appt_set = APPTSET.objects.create(account = acct,
                                                  customer = cust)
        for e in dates:
                APPT.objects.create(account   = acct,
                                    customer  = cust,
                                    provider  = prov,
                                    appt_set  = appt_set,
                                    date      = e,
                                    time      = time,
                                    window    = form.cleaned_data["window"],
                                    service   = form.cleaned_data["service"],
                                    price     = form.cleaned_data["price"],
                                    remind    = form.cleaned_data["remind"],
                                    rem_text  = form.cleaned_data["rem_text"],
                                    reminded  = False,
                                    completed = False,
                                    updated   = False)

def appts_proc_ac_form(form, user):
        update   = form.cleaned_data["update"]
        upd_text = form.cleaned_data["upd_text"].strip()
        for e in form.cleaned_data:
                if (e not in ["update", "upd_text"]) and form.cleaned_data[e]:
                        appts_proc_ac_form_(e, update, upd_text, user)

def end(beg, window):
        hour       = beg.hour
        minute     = beg.minute
        datetime_  = datetime.datetime(2000, 1, 1, hour, minute, 0, 0)
        datetime_ += datetime.timedelta(hours = window)

        return datetime_.time()

def appts_proc_ac_form_(appt, update, upd_text, user):
        acct           = ACCT.objects.get(user = user)
        appt           = APPT.objects.get(id = appt, account = acct)
        appt.completed = True
        if update:
                appt.updated = True
                time         = appt.time.strftime("%I:%M %p")
                if appt.window:
                        end_  = end(appt.time, appt.window)
                        time += " - " + end_.strftime("%I:%M %p")
                update       = UPDATE.format(appt.account.business,
                                             appt.service.capitalize(),
                                             appt.date.strftime("%a %b %d, %Y"),
                                             time,
                                             UPDATE_)
                if upd_text:
                        update = upd_text
                subprocess.Popen(["/home/send_text/send_text",
                                  appt.customer.phone,
                                  update,
                                  appt.account.user.email])
                if appt.customer.alt_phone:
                        subprocess.Popen(["/home/send_text/send_text",
                                          appt.customer.alt_phone,
                                          update,
                                          appt.account.user.email])
        appt.save()

def appts_make_an_form(account, initial = {}):
        initial_ = {"account" : account.id, "remind" : account.remind_0}
        initial_.update(initial)

        return bighelp.forms.AppointmentNewForm(initial = initial_,
                                                user    = account.user)

def appts_get_list(form, user):
        acct  = ACCT.objects.get(user = user)
        custs = sorted(list(CUST.objects.filter(account = acct)),
                       key = lambda e : e.last)
        appts = [(f"{e.first} {e.last}",
                  sorted([(a, form[str(a.id)]) for a in APPT.objects.filter(
                                                             customer  = e,
                                                             completed = False,
                                                             account   = acct)],
                         key = lambda e : (e[0].date, e[0].time)))
                 for e in custs]
        appts = [e for e in appts if e[1]]

        return appts

def appts_caini_get_list(user):
        acct  = ACCT.objects.get(user = user)
        custs = sorted(list(CUST.objects.filter(account = acct)),
                       key = lambda e : e.last)
        appts = [(f"{e.first} {e.last}",
                  sorted([a for a in APPT.objects.filter(customer  = e,
                                                         completed = True,
                                                         invoice   = None,
                                                         account   = acct)],
                         key = lambda e : (e.date, e.time)))
                 for e in custs]
        appts = [e for e in appts if e[1]]

        return appts



Warning, long message follows. I am addressing your root question, but it’s going to take me a little time to get there, since you ask some good questions along the way that deserve answers.

(Side note: Not particularly important at the moment, but the specific version of Python could matter. I don’t think it does here, but when someone asks for which version of Python you’re using, you should specify the complete version. You can look at the Python compatibility list to ensure the specific version of Python you’re running is compatible with the version of Django you’re using.)

They are happening in a try block.

Notice the first location you identified:

That’s part of this chunk of code in base.py: (The line flagged is the 5th line after the comment.)

    def _resolve_lookup(self, context):
        """
        Perform resolution of a real variable (i.e. not a literal) against the
        given context.

        As indicated by the method's name, this method is an implementation
        detail and shouldn't be called by external code. Use Variable.resolve()
        instead.
        """
        current = context
        try:  # catch-all for silent variable failures
            for bit in self.lookups:
                try:  # dictionary lookup
                    current = current[bit]
                    # ValueError/IndexError are for numpy.array lookup on
                    # numpy < 1.9 and 1.9+ respectively
                except (TypeError, AttributeError, KeyError, ValueError, IndexError):
                    try:  # attribute lookup
                        # Don't return class attributes if the class is the context:
                        if isinstance(current, BaseContext) and getattr(type(current), bit):
                            raise AttributeError
                        current = getattr(current, bit)
                    except (TypeError, AttributeError):
                        # Reraise if the exception was raised by a @property
                        if not isinstance(current, BaseContext) and bit in dir(current):
                            raise
                        try:  # list-index lookup
                            current = current[int(bit)]
                        except (IndexError,  # list index out of range
                                ValueError,  # invalid literal for int()
                                KeyError,    # current is a dict without `int(bit)` key
                                TypeError):  # unsubscriptable object
                            raise VariableDoesNotExist("Failed lookup for key "
                                                       "[%s] in %r",
                                                       (bit, current))  # missing attribute
                if callable(current):
                    if getattr(current, 'do_not_call_in_templates', False):
                        pass
                    elif getattr(current, 'alters_data', False):
                        current = context.template.engine.string_if_invalid
                    else:
                        try:  # method call (assuming no args required)
                            current = current()
                        except TypeError:
                            signature = inspect.signature(current)
                            try:
                                signature.bind()
                            except TypeError:  # arguments *were* required
                                current = context.template.engine.string_if_invalid  # invalid method call
                            else:
                                raise
        except Exception as e:
            template_name = getattr(context, 'template_name', None) or 'unknown'
            logger.debug(
                "Exception while resolving variable '%s' in template '%s'.",
                bit,
                template_name,
                exc_info=True,
            )

            if getattr(e, 'silent_variable_failure', False):
                current = context.template.engine.string_if_invalid
            else:
                raise

        return current

The template being referenced in that error message (checkbox_select) includes multiple_input.html, which looks like this:

{% with id=widget.attrs.id %}<ul{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}>{% for group, options, index in widget.optgroups %}{% if group %}
  <li>{{ group }}<ul{% if id %} id="{{ id }}_{{ index }}"{% endif %}>{% endif %}{% for option in options %}
    <li>{% include option.template_name with widget=option %}</li>{% endfor %}{% if group %}
  </ul></li>{% endif %}{% endfor %}
</ul>{% endwith %}

Warning What follows here is a bit of a guess. I’m unsure about exactly what’s going on, so I could be wrong about one or more specific details. However, I am comfortable enough to know that even if I’m not precisely correct in those details, the principles are close enough for this discussion and for your understanding of essentially what’s going on here.

So, with that disclaimer in mind, notice this clause in the first line of that template:

{% if widget.attrs.class %}

That’s a directive to the template engine to evaluate the expression widget.attrs.class, and determine whether it’s “truthy” or “falsy” for the purpose of rendering the template between this location and the corresponding {% endif %}.

The issue is that widget.attrs.class could be one of a number of different “things”. (See the “Behind the scenes” section of the Variables section in the template docs.) The template engine then needs to try to evaluate that variable in each of the different ways.

However, it’s completely valid within the Django template engine that class not be defined at all. There is no requirement that widget.attrs actually have any attribute named class. And, from what I’m seeing in your code (acknowledging that I may have overlooked them), there isn’t one in any of the widgets you’re specifying for your fields.

So, you’ve specified a widget to be used, and it appears to me that it may be being built without an attribute named class (of any of the types listed in the docs). And that’s what’s causing the error messages to be logged.

=== whew! End of topic 1 ===

Regarding the logging, that’s actually the easier topic to address.

You’ve got a number of different options - Django’s interfaces to the logging library has a variety of ways you can manage this.

You can mix & match any combination of the following:

  • Create a ‘loggers’ entry to only log entries within the “django” hierarchy at ERROR or above.
  • Create a ‘loggers’ entry to write “django” errors to a different log
  • Create a custom filter (along with a corresponding ‘filters’ entry) to filter out unwanted messages

I’m pointing these out to highlight just how powerful and flexible the Django logging system is. You have just about every facility available that would be needed to fine-tune your logs to make them as valuable to you as desired.

  • Create a ‘loggers’ entry to only log entries within the “django” hierarchy at ERROR or above.

That’s not a bad idea. Right now I have it set at level DEBUG. Is that overkill? I’m just trying to make sure to catch anything I should be paying attention to. I don’t know if DEBUG is overkill for that.

For a production environment, I would definitely consider that to be overkill.

The Loggers section of the Logging docs identify the levels and how they’re intended to be used. In my opinion, WARNING, ERROR and CRITICAL are the only levels to be logged in a production environment. I’ve found DEBUG to only be of value when trying to diagnose an identified problem.

It looks like level WARNING might be good too? Perhaps a good bar would be to always create websites that never have any warnings?

Maybe that would be a better than just trying to avoid ERROR level messages?

Yes, WARNING, ERROR, and CRITICAL would be the most important for tracking in a production environment.
I don’t know how practical it is to require Django to not throw any warnings. I believe that deprecation notices are logged as WARNING - there may be other uses as well.
(We only track ERROR and CRITICAL in our production environments for the ‘django’ package tree. We’ve got a couple systems that are much more granular on the application side.)

Ken

Thanks so much for your help! I so appreciate learning about proper log levels. I was doing overkill
and didn’t know it.

cs