Upgrade django to 5.2, python to 3.13 (#1915)

This commit is contained in:
daveoconnor
2025-10-22 13:24:26 -07:00
committed by GitHub
parent 1ec3cba5e3
commit 5f022aca0a
26 changed files with 207 additions and 130 deletions

View File

@@ -25,7 +25,7 @@ jobs:
services:
postgres:
image: postgres:12
image: postgres:16
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
@@ -44,10 +44,10 @@ jobs:
with:
fetch-depth: 0
- name: Set up Python 3.11
- name: Set up Python 3.13
uses: actions/setup-python@v5
with:
python-version: 3.11
python-version: 3.13
- uses: actions/cache@v4
with:
@@ -133,10 +133,10 @@ jobs:
run: |
git fetch --depth=1 origin +refs/tags/*:refs/tags/* || true
- name: Set up Python 3.11
- name: Set up Python 3.13
uses: actions/setup-python@v5
with:
python-version: 3.11
python-version: 3.13
- name: Install Python dependencies
run: |

View File

@@ -15,7 +15,7 @@ jobs:
services:
postgres:
image: postgres:12
image: postgres:16
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
@@ -34,10 +34,10 @@ jobs:
with:
fetch-depth: 0
- name: Set up Python 3.11
- name: Set up Python 3.13
uses: actions/setup-python@v5
with:
python-version: 3.11
python-version: 3.13
- uses: actions/cache@v4
with:
@@ -85,10 +85,10 @@ jobs:
run: |
git fetch --depth=1 origin +refs/tags/*:refs/tags/* || true
- name: Set up Python 3.11
- name: Set up Python 3.13
uses: actions/setup-python@v5
with:
python-version: 3.11
python-version: 3.13
- name: Install Python dependencies
run: |

View File

@@ -1,9 +1,14 @@
default_language_version:
python: python3.11
python: python3.13
exclude: .*migrations\/.*|static\/img\/.*|static\/animations\/.*|static\/js\/boost-gecko\/.*|kube\/boost\/templates\/.*\.yaml
repos:
- repo: https://github.com/adamchainz/django-upgrade
rev: "1.27.0"
hooks:
- id: django-upgrade
args: [--target-version, "5.2"] # Replace with Django version
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:

View File

@@ -13,7 +13,7 @@ Links:
## Local Development Setup
This project will use Python 3.11, Docker, and Docker Compose.
This project will use Python 3.13, Docker, and Docker Compose.
Instructions to install those packages are included in [development_setup_notes.md](docs/development_setup_notes.md).

View File

@@ -20,10 +20,8 @@ def get_calendar(min_time=None, single_events=True, order_by="startTime"):
https://developers.google.com/calendar/api/v3/reference/events/list
"""
if not min_time:
min_time = (
datetime.datetime.utcnow().isoformat() + "Z"
) # 'Z' indicates UTC time
# 'Z' indicates UTC time
min_time = datetime.datetime.now(datetime.timezone.utc).isoformat() + "Z"
url = f"https://www.googleapis.com/calendar/v3/calendars/{settings.BOOST_CALENDAR}/events?key={settings.CALENDAR_API_KEY}&timeMin={min_time}&singleEvents={single_events}&orderBy={order_by}"
headers = {"Accept": "application/json"}

View File

@@ -28,7 +28,7 @@ class Migration(migrations.Migration):
migrations.AddConstraint(
model_name="sitesettings",
constraint=models.CheckConstraint(
check=models.Q(("id", 1)), name="core_sitesettings_single_instance"
condition=models.Q(("id", 1)), name="core_sitesettings_single_instance"
),
),
migrations.AlterModelOptions(

View File

@@ -72,7 +72,7 @@ class SiteSettings(models.Model):
# check constraint to only allow id=1 to exist
models.CheckConstraint(
name="%(app_label)s_%(class)s_single_instance",
check=models.Q(id=1),
condition=models.Q(id=1),
),
]
verbose_name_plural = "Site Settings"

View File

@@ -1,5 +1,6 @@
import os
import requests
from django.utils import timezone
from urllib.parse import urljoin
@@ -16,11 +17,14 @@ from django.http import (
HttpResponse,
HttpResponseNotFound,
HttpResponseRedirect,
HttpRequest,
)
from django.shortcuts import redirect
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.cache import never_cache
from django.views.generic import TemplateView
from config.settings import ENABLE_DB_CACHE
@@ -942,3 +946,57 @@ class RedirectToLibrariesView(BaseRedirectView):
if requested_version == "release":
new_path = "/libraries/"
return HttpResponseRedirect(new_path)
@method_decorator(never_cache, name="dispatch")
class QRCodeView(View):
"""Handles QR code urls, sending them to Plausible, then redirecting to the desired url.
QR code urls are formatted /qrc/<campaign_identifier>/desired/path/to/content/, and will
result in a redirect to /desired/path/to/content/.
E.g. https://www.boost.org/qrc/pv-01/library/latest/beast/ will send this full url to Plausible,
then redirect to https://www.boost.org/library/latest/beast/
"""
def get(self, request: HttpRequest, campaign_identifier: str, main_path: str = ""):
absolute_url = request.build_absolute_uri(request.path)
referrer = request.headers.get("referer", "")
user_agent = request.headers.get("user-agent", "")
plausible_payload = {
"name": "pageview",
"domain": "qrc.boost.org",
"url": absolute_url,
"referrer": referrer,
}
headers = {"Content-Type": "application/json", "User-Agent": user_agent}
client_ip = request.headers.get("x-forwarded-for", "").split(",")[0].strip()
client_ip = client_ip or request.META.get("REMOTE_ADDR")
if client_ip:
headers["X-Forwarded-For"] = client_ip
try:
requests.post(
"https://plausible.io/api/event",
json=plausible_payload,
headers=headers,
timeout=2.0,
)
except Exception as e:
# Dont interrupt the redirect - just log it
logger.error(f"Plausible event post failed: {e}")
# Now that we've sent the request url to plausible, we can redirect to the main_path
# Preserve the original querystring, if any.
# Example: /qrc/3/library/latest/algorithm/?x=1 -> /library/latest/algorithm/?x=1
# `main_path` is everything after qrc/<campaign>/ thanks to <path:main_path>.
redirect_path = "/" + main_path if main_path else "/"
qs = request.META.get("QUERY_STRING")
if qs:
redirect_path = f"{redirect_path}?{qs}"
return HttpResponseRedirect(redirect_path)

View File

@@ -1,6 +1,6 @@
# syntax = docker/dockerfile:experimental
FROM python:3.11-slim AS builder-py
FROM python:3.13-slim AS builder-py
ARG LOCAL_DEVELOPMENT
@@ -42,7 +42,7 @@ RUN yarn build
# Final image.
FROM python:3.11-slim AS release
FROM python:3.13-slim AS release
RUN apt update && apt install -y git libpq-dev ruby ruby-dev && rm -rf /var/lib/apt/lists/*

View File

@@ -7,3 +7,14 @@
1. Run `just pip-compile`, which will add the dependency to `requirements.txt`
1. Run `just rebuild` to rebuild your Docker image to include the new dependencies
2. Run `just up` and continue with development
## Upgrading dependencies
To upgrade all dependencies to their latest versions, run:
1. `just pip-compile-upgrade`.
2. Get the django version from requirements.txt and set the `DJANGO_VERSION` value in /justfile
3. Update the `--target-version` args value for django-upgrade in .pre-commit-config.yaml to match
3. In a venv with installed packages run `just run-django-upgrade` to upgrade python code.
4. `just build` to create new docker images.
5. Tear down docker containers and restart with the newly built images, then test.

View File

@@ -1,6 +1,7 @@
set dotenv-load := false
COMPOSE_FILE := "docker-compose.yml"
ENV_FILE := ".env"
DJANGO_VERSION := "5.2"
@_default:
just --list
@@ -122,6 +123,10 @@ alias shell := console
fi
@cd development-tofu; direnv allow && tofu destroy
@run-django-upgrade:
[ -n "${VIRTUAL_ENV-}" ] || { echo "❌ Activate your venv first."; exit 1; }
-git ls-files -z -- '*.py' | xargs -0r django-upgrade --target {{DJANGO_VERSION}}
# Dependency management
@pip-compile ARGS='': ## rebuilds our pip requirements
docker compose run --rm web uv pip compile {{ ARGS }} ./requirements.in --no-strip-extras --output-file ./requirements.txt

View File

@@ -28,8 +28,8 @@ class PlausibleRedirectView(View):
def get(self, request: HttpRequest, campaign_identifier: str, main_path: str = ""):
absolute_url = request.build_absolute_uri(request.path)
referrer = request.META.get("HTTP_REFERER", "")
user_agent = request.META.get("HTTP_USER_AGENT", "")
referrer = request.headers.get("referer", "")
user_agent = request.headers.get("user-agent", "")
plausible_payload = {
"name": "pageview",
@@ -40,7 +40,7 @@ class PlausibleRedirectView(View):
headers = {"Content-Type": "application/json", "User-Agent": user_agent}
client_ip = request.META.get("HTTP_X_FORWARDED_FOR", "").split(",")[0].strip()
client_ip = request.headers.get("x-forwarded-for", "").split(",")[0].strip()
client_ip = client_ip or request.META.get("REMOTE_ADDR")
if client_ip:
@@ -85,7 +85,7 @@ class WhitePaperView(SuccessMessageMixin, CreateView):
if original_referrer := self.request.session.get("original_referrer", ""):
self.referrer = original_referrer
else:
self.referrer = self.request.META.get("HTTP_REFERER", "")
self.referrer = self.request.headers.get("referer", "")
return super().dispatch(request, *args, **kwargs)
def get_template_names(self):

View File

@@ -1,7 +1,7 @@
from datetime import datetime
from datetime import datetime, timezone
from django.contrib.syndication.views import Feed
from django.utils.feedgenerator import Atom1Feed
from django.utils.timezone import make_aware, utc
from django.utils.timezone import make_aware
from django.utils.html import urlize, linebreaks
from .models import Entry
@@ -22,7 +22,7 @@ class RSSNewsFeed(Feed):
publish_date = item.publish_at
if publish_date:
datetime_obj = datetime.combine(publish_date, datetime.min.time())
aware_datetime_obj = make_aware(datetime_obj, timezone=utc)
aware_datetime_obj = make_aware(datetime_obj, timezone=timezone.utc)
return aware_datetime_obj
def item_description(self, item):

View File

@@ -60,7 +60,7 @@ def generate_magic_approval_link(entry_slug: str, moderator_id: int):
def send_email_news_needs_moderation(request, entry):
recipient_list = [
u
for u in moderators().select_related("preferences").only("email")
for u in moderators().select_related("preferences").only("email", "preferences")
if entry.tag in u.preferences.allow_notification_others_news_needs_moderation
]
if not recipient_list:

View File

@@ -1,5 +1,5 @@
from datetime import datetime, timedelta
from django.utils.timezone import make_aware, now, utc
from datetime import datetime, timedelta, timezone
from django.utils.timezone import make_aware, now
from model_bakery import baker
from ..feeds import RSSNewsFeed, AtomNewsFeed
@@ -22,7 +22,8 @@ def test_item_pubdate(make_entry):
feed = RSSNewsFeed()
published_entry = make_entry(moderator=baker.make("users.User"), approved_at=now())
expected_datetime = make_aware(
datetime.combine(published_entry.publish_at, datetime.min.time()), timezone=utc
datetime.combine(published_entry.publish_at, datetime.min.time()),
timezone=timezone.utc,
)
assert feed.item_pubdate(published_entry) == expected_datetime
@@ -51,6 +52,7 @@ def test_item_pubdate_atom(make_entry):
feed = AtomNewsFeed()
published_entry = make_entry(moderator=baker.make("users.User"), approved_at=now())
expected_datetime = make_aware(
datetime.combine(published_entry.publish_at, datetime.min.time()), timezone=utc
datetime.combine(published_entry.publish_at, datetime.min.time()),
timezone=timezone.utc,
)
assert feed.item_pubdate(published_entry) == expected_datetime

View File

@@ -6,7 +6,7 @@ whitelist-regex = ["test_.*"]
[tool.ruff]
line-length = 88
target-version = "py311"
target-version = "py313"
[tool.black]
line-length = 88

View File

@@ -1,3 +1,3 @@
-c requirements.txt
django-debug-toolbar
pydevd-pycharm==243.26053.29 # pinned to appropriate version for current pycharm
pydevd-pycharm==252.26830.99 # pinned to appropriate version for current pycharm

View File

@@ -1,16 +1,16 @@
# This file was autogenerated by uv via the following command:
# uv pip compile ./requirements-dev.in --no-strip-extras --output-file ./requirements-dev.txt
asgiref==3.9.1
asgiref==3.10.0
# via
# -c ./requirements.txt
# django
django==4.2.24
django==5.2.7
# via
# -c ./requirements.txt
# django-debug-toolbar
django-debug-toolbar==6.0.0
# via -r ./requirements-dev.in
pydevd-pycharm==243.26053.29
pydevd-pycharm==252.26830.99
# via -r ./requirements-dev.in
sqlparse==0.5.3
# via

View File

@@ -1,4 +1,4 @@
Django>=4.0, <5.0
Django>=5.0, <6.0
bumpversion
django-admin-env-notice
django-allauth
@@ -12,7 +12,7 @@ django-health-check
django-imagekit
django-oauth-toolkit
django-redis
django-rest-auth
django-upgrade
django-widget-tweaks
djangorestframework
environs[django]

View File

@@ -2,17 +2,17 @@
# uv pip compile ./requirements.in --no-strip-extras --output-file ./requirements.txt
aiohappyeyeballs==2.6.1
# via aiohttp
aiohttp==3.12.15
aiohttp==3.13.1
# via algoliasearch
aiosignal==1.4.0
# via aiohttp
algoliasearch==4.27.0
algoliasearch==4.30.0
# via -r ./requirements.in
amqp==5.3.1
# via kombu
annotated-types==0.7.0
# via pydantic
anyio==4.10.0
anyio==4.11.0
# via
# httpx
# openai
@@ -22,7 +22,7 @@ argon2-cffi==25.1.0
# via minio
argon2-cffi-bindings==25.1.0
# via argon2-cffi
asgiref==3.9.1
asgiref==3.10.0
# via
# django
# django-allauth
@@ -32,21 +32,21 @@ asttokens==3.0.0
# via stack-data
async-timeout==5.0.1
# via algoliasearch
attrs==25.3.0
attrs==25.4.0
# via
# aiohttp
# interrogate
beautifulsoup4==4.13.5
beautifulsoup4==4.14.2
# via -r ./requirements.in
billiard==4.2.1
billiard==4.2.2
# via celery
black==25.1.0
black==25.9.0
# via -r ./requirements.in
boto3==1.40.24
boto3==1.40.56
# via
# -r ./requirements.in
# django-bakery
botocore==1.40.24
botocore==1.40.56
# via
# boto3
# s3transfer
@@ -56,14 +56,14 @@ bumpversion==0.6.0
# via -r ./requirements.in
celery==5.5.3
# via -r ./requirements.in
certifi==2025.8.3
certifi==2025.10.5
# via
# elasticsearch
# httpcore
# httpx
# minio
# requests
cffi==1.17.1
cffi==2.0.0
# via
# argon2-cffi-bindings
# cryptography
@@ -71,9 +71,9 @@ cfgv==3.4.0
# via pre-commit
chardet==5.2.0
# via -r ./requirements.in
charset-normalizer==3.4.3
charset-normalizer==3.4.4
# via requests
click==8.2.1
click==8.3.0
# via
# black
# celery
@@ -92,9 +92,9 @@ colorama==0.4.6
# via interrogate
contourpy==1.3.3
# via matplotlib
coverage[toml]==7.10.6
coverage[toml]==7.11.0
# via pytest-cov
cryptography==45.0.7
cryptography==46.0.3
# via
# -r ./requirements.in
# jwcrypto
@@ -107,11 +107,11 @@ distlib==0.4.0
# via virtualenv
distro==1.9.0
# via openai
dj-database-url==2.2.0
dj-database-url==3.0.1
# via environs
dj-email-url==1.0.6
# via environs
django==4.2.24
django==5.2.7
# via
# -r ./requirements.in
# dj-database-url
@@ -126,13 +126,12 @@ django==4.2.24
# django-js-asset
# django-oauth-toolkit
# django-redis
# django-rest-auth
# django-storages
# djangorestframework
# model-bakery
django-admin-env-notice==1.0.1
# via -r ./requirements.in
django-allauth[socialaccount]==65.11.1
django-allauth[socialaccount]==65.12.1
# via -r ./requirements.in
django-anymail[mailgun]==13.1
# via -r ./requirements.in
@@ -144,7 +143,7 @@ django-cache-url==3.4.5
# via environs
django-click==2.4.1
# via -r ./requirements.in
django-cors-headers==4.7.0
django-cors-headers==4.9.0
# via -r ./requirements.in
django-countries==7.6.1
# via -r ./requirements.in
@@ -156,53 +155,51 @@ django-haystack==3.3.0
# via -r ./requirements.in
django-health-check==3.20.0
# via -r ./requirements.in
django-imagekit==5.0.0
django-imagekit==6.0.0
# via -r ./requirements.in
django-js-asset==3.1.2
# via django-mptt
django-mptt==0.14.0
# via -r ./requirements.in
django-oauth-toolkit==3.0.1
django-oauth-toolkit==3.1.0
# via -r ./requirements.in
django-redis==6.0.0
# via -r ./requirements.in
django-rest-auth==0.9.5
# via -r ./requirements.in
django-storages==1.14.6
# via -r ./requirements.in
django-test-plus==2.3.0
# via -r ./requirements.in
django-tracer==0.9.3
# via -r ./requirements.in
django-upgrade==1.29.0
# via -r ./requirements.in
django-widget-tweaks==1.5.0
# via -r ./requirements.in
djangorestframework==3.16.1
# via
# -r ./requirements.in
# django-rest-auth
elasticsearch==7.17.12
# via -r ./requirements.in
elasticsearch==7.9.1
# via -r ./requirements.in
environs[django]==14.3.0
# via -r ./requirements.in
executing==2.2.1
# via stack-data
faker==37.6.0
faker==37.11.0
# via -r ./requirements.in
fastcore==1.8.8
fastcore==1.8.13
# via ghapi
filelock==3.19.1
filelock==3.20.0
# via virtualenv
fonttools==4.59.2
fonttools==4.60.1
# via matplotlib
frozenlist==1.7.0
frozenlist==1.8.0
# via
# aiohttp
# aiosignal
fs==2.4.16
# via django-bakery
gevent==25.8.2
gevent==25.9.1
# via -r ./requirements.in
ghapi==1.0.6
ghapi==1.0.8
# via -r ./requirements.in
greenlet==3.2.4
# via
@@ -216,19 +213,19 @@ httpcore==1.0.9
# via httpx
httpx==0.28.1
# via openai
identify==2.6.1
identify==2.6.15
# via pre-commit
idna==3.10
idna==3.11
# via
# anyio
# httpx
# requests
# yarl
iniconfig==2.1.0
iniconfig==2.3.0
# via pytest
interrogate==1.7.0
# via -r ./requirements.in
ipython==9.5.0
ipython==9.6.0
# via -r ./requirements.in
ipython-pygments-lexers==1.1.1
# via ipython
@@ -236,7 +233,7 @@ itsdangerous==2.2.0
# via -r ./requirements.in
jedi==0.19.2
# via ipython
jiter==0.10.0
jiter==0.11.1
# via openai
jmespath==1.0.1
# via
@@ -252,21 +249,21 @@ kiwisolver==1.4.9
# via matplotlib
kombu==5.5.4
# via celery
lxml==6.0.1
lxml==6.0.2
# via -r ./requirements.in
marshmallow==4.0.1
# via environs
matplotlib==3.10.6
matplotlib==3.10.7
# via wordcloud
matplotlib-inline==0.1.7
# via ipython
minio==7.2.16
minio==7.2.18
# via -r ./requirements.in
mistletoe==1.4.0
mistletoe==1.5.0
# via -r ./requirements.in
model-bakery==1.20.5
# via -r ./requirements.in
multidict==6.6.4
multidict==6.7.0
# via
# aiohttp
# yarl
@@ -274,7 +271,7 @@ mypy-extensions==1.1.0
# via black
nodeenv==1.9.1
# via pre-commit
numpy==2.3.2
numpy==2.3.4
# via
# contourpy
# matplotlib
@@ -283,9 +280,9 @@ oauthlib==3.3.1
# via
# django-allauth
# django-oauth-toolkit
openai==1.102.0
openai==2.6.0
# via -r ./requirements.in
packaging==24.1
packaging==25.0
# via
# black
# django-haystack
@@ -303,13 +300,13 @@ pexpect==4.9.0
# via ipython
pilkit==3.0
# via django-imagekit
pillow==11.3.0
pillow==12.0.0
# via
# -r ./requirements.in
# matplotlib
# pilkit
# wordcloud
platformdirs==4.4.0
platformdirs==4.5.0
# via
# black
# virtualenv
@@ -323,13 +320,13 @@ prompt-toolkit==3.0.52
# via
# click-repl
# ipython
propcache==0.3.2
propcache==0.4.1
# via
# aiohttp
# yarl
psycogreen==1.0.2
# via -r ./requirements.in
psycopg2-binary==2.9.10
psycopg2-binary==2.9.11
# via -r ./requirements.in
ptyprocess==0.7.0
# via pexpect
@@ -337,33 +334,33 @@ pure-eval==0.2.3
# via stack-data
py==1.11.0
# via interrogate
pycparser==2.22
pycparser==2.23
# via cffi
pycryptodome==3.23.0
# via minio
pydantic==2.11.9
pydantic==2.12.3
# via
# algoliasearch
# openai
pydantic-core==2.33.2
pydantic-core==2.41.4
# via pydantic
pygments==2.19.2
# via
# ipython
# ipython-pygments-lexers
# pytest
pyjwt[crypto]==2.9.0
pyjwt[crypto]==2.10.1
# via
# django-allauth
# redis
pyparsing==3.2.0
pyparsing==3.2.5
# via matplotlib
pytest==8.4.2
# via
# -r ./requirements.in
# pytest-cov
# pytest-django
pytest-cov==6.2.1
pytest-cov==7.0.0
# via -r ./requirements.in
pytest-django==4.11.1
# via -r ./requirements.in
@@ -378,9 +375,11 @@ python-dotenv==1.1.1
# via environs
python-frontmatter==1.1.0
# via -r ./requirements.in
python-json-logger==3.3.0
python-json-logger==4.0.0
# via -r ./requirements.in
pyyaml==6.0.2
pytokens==0.2.0
# via black
pyyaml==6.0.3
# via
# pre-commit
# python-frontmatter
@@ -399,26 +398,24 @@ requests==2.32.5
# responses
responses==0.25.8
# via -r ./requirements.in
s3transfer==0.13.1
s3transfer==0.14.0
# via boto3
setuptools==80.9.0
# via
# fs
# zope-event
# zope-interface
six==1.17.0
# via
# django-bakery
# django-rest-auth
# fs
# python-dateutil
slack-sdk==3.36.0
slack-sdk==3.37.0
# via -r ./requirements.in
sniffio==1.3.1
# via
# anyio
# openai
soupsieve==2.6
soupsieve==2.8
# via beautifulsoup4
sqlparse==0.5.3
# via django
@@ -428,6 +425,8 @@ structlog==25.4.0
# via -r ./requirements.in
tabulate==0.9.0
# via interrogate
tokenize-rt==6.2.0
# via django-upgrade
tqdm==4.67.1
# via openai
traitlets==5.14.3
@@ -439,7 +438,6 @@ typing-extensions==4.15.0
# aiosignal
# anyio
# beautifulsoup4
# dj-database-url
# django-countries
# ipython
# jwcrypto
@@ -448,7 +446,7 @@ typing-extensions==4.15.0
# pydantic
# pydantic-core
# typing-inspection
typing-inspection==0.4.1
typing-inspection==0.4.2
# via pydantic
tzdata==2025.2
# via
@@ -456,7 +454,7 @@ tzdata==2025.2
# kombu
unidecode==1.4.0
# via -r ./requirements.in
urllib3==1.26.20
urllib3==2.5.0
# via
# algoliasearch
# botocore
@@ -465,26 +463,26 @@ urllib3==1.26.20
# minio
# requests
# responses
uv==0.8.15
uv==0.9.5
# via -r ./requirements.in
vine==5.1.0
# via
# amqp
# celery
# kombu
virtualenv==20.34.0
virtualenv==20.35.3
# via pre-commit
wcwidth==0.2.13
wcwidth==0.2.14
# via prompt-toolkit
wheel==0.45.1
# via -r ./requirements.in
whitenoise==6.9.0
whitenoise==6.11.0
# via -r ./requirements.in
wordcloud==1.9.4
# via -r ./requirements.in
yarl==1.20.1
yarl==1.22.0
# via aiohttp
zope-event==5.1.1
zope-event==6.0
# via gevent
zope-interface==7.2
zope-interface==8.0.1
# via gevent

View File

@@ -146,7 +146,7 @@ class Migration(migrations.Migration):
migrations.AddConstraint(
model_name="thread",
constraint=models.CheckConstraint(
check=django.db.models.lookups.GreaterThanOrEqual(
condition=django.db.models.lookups.GreaterThanOrEqual(
django.db.models.functions.comparison.Cast(
"last_update_ts", output_field=models.FloatField()
),
@@ -172,7 +172,7 @@ class Migration(migrations.Migration):
migrations.AddConstraint(
model_name="channelupdategap",
constraint=models.CheckConstraint(
check=django.db.models.lookups.GreaterThan(
condition=django.db.models.lookups.GreaterThan(
django.db.models.functions.comparison.Cast(
"newest_message_ts", output_field=models.FloatField()
),

View File

@@ -61,7 +61,7 @@ class ChannelUpdateGap(models.Model):
class Meta:
constraints = [
models.CheckConstraint(
check=models.lookups.GreaterThan(
condition=models.lookups.GreaterThan(
models.functions.Cast(
"newest_message_ts", output_field=models.FloatField()
),
@@ -110,7 +110,7 @@ class Thread(models.Model):
unique_together = [("channel", "thread_ts")]
constraints = [
models.CheckConstraint(
check=models.lookups.GreaterThanOrEqual(
condition=models.lookups.GreaterThanOrEqual(
models.functions.Cast(
"last_update_ts", output_field=models.FloatField()
),

View File

@@ -5,6 +5,7 @@ from django.utils.translation import gettext_lazy as _
from .models import User
@admin.register(User)
class EmailUserAdmin(UserAdmin):
fieldsets = (
(None, {"fields": ("email", "password")}),
@@ -59,6 +60,3 @@ class EmailUserAdmin(UserAdmin):
"claimed",
)
search_fields = ("email", "display_name__unaccent")
admin.site.register(User, EmailUserAdmin)

View File

@@ -330,7 +330,7 @@ class UserAvatar(TemplateView):
):
# check if user is on pages that require CSRF but don't require login
# (auth pages where anonymous users submit forms)
referer = self.request.META.get("HTTP_REFERER", "")
referer = self.request.headers.get("referer", "")
current_path = self.request.path
# paths that anonymous users can access and have forms

View File

@@ -1,7 +1,7 @@
from datetime import datetime
from datetime import datetime, timezone
from django.contrib.syndication.views import Feed
from django.utils.feedgenerator import Atom1Feed
from django.utils.timezone import make_aware, utc
from django.utils.timezone import make_aware
from core.models import RenderedContent
from .models import Version
@@ -24,7 +24,7 @@ class RSSVersionFeed(Feed):
release_date = item.release_date
if release_date:
datetime_obj = datetime.combine(release_date, datetime.min.time())
aware_datetime_obj = make_aware(datetime_obj, timezone=utc)
aware_datetime_obj = make_aware(datetime_obj, timezone=timezone.utc)
return aware_datetime_obj
def item_description(self, item):

View File

@@ -1,5 +1,5 @@
from datetime import datetime
from django.utils.timezone import make_aware, utc
from datetime import datetime, timezone
from django.utils.timezone import make_aware
from ..feeds import RSSVersionFeed, AtomVersionFeed
@@ -16,7 +16,8 @@ def test_items(version, old_version):
def test_item_pubdate(version):
feed = RSSVersionFeed()
expected_datetime = make_aware(
datetime.combine(version.release_date, datetime.min.time()), timezone=utc
datetime.combine(version.release_date, datetime.min.time()),
timezone=timezone.utc,
)
assert feed.item_pubdate(version) == expected_datetime
@@ -51,6 +52,7 @@ def test_items_atom(version, old_version):
def test_item_pubdate_atom(version):
feed = AtomVersionFeed()
expected_datetime = make_aware(
datetime.combine(version.release_date, datetime.min.time()), timezone=utc
datetime.combine(version.release_date, datetime.min.time()),
timezone=timezone.utc,
)
assert feed.item_pubdate(version) == expected_datetime