Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
bb10b9c
Add `bio` field to `accounts.Profile` model
ertgl Oct 19, 2025
2c29158
Apply black to migrations
ertgl Oct 19, 2025
093ed87
Add a nullable one-to-one `user` field linking `IndividualMember` to …
ertgl Oct 19, 2025
8b918c0
Display `IndividualMember` names as links to their profiles when asso…
ertgl Oct 20, 2025
6e90cd5
Fix vertical split of profile name caused by floated image
ertgl Oct 20, 2025
6a2322d
Make the `user` field on `IndividualMemberAdmin` auto-completable
ertgl Oct 20, 2025
a55a0f5
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 20, 2025
9a180f1
Update `UserProfileTests.test_username_is_page_title` to reflect temp…
ertgl Oct 20, 2025
a352eda
Add profile link tests for current and former individual members
ertgl Oct 20, 2025
36b3f90
Add admin action to send account invite mail to individual members
ertgl Oct 21, 2025
b7ad21f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 21, 2025
da706a4
Fix Flake8 E501 (line too long) errors
ertgl Oct 21, 2025
d4d8873
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 21, 2025
b918122
Introduce several improvements (please see the commit message for det…
ertgl Oct 22, 2025
e46a504
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 22, 2025
f1a4781
Fix `ngettext` usages to correctly handle singular forms for all lang…
ertgl Oct 22, 2025
20117b6
Add management command `send_individual_member_account_invite_mails`
ertgl Oct 22, 2025
84c0826
Add management command `link_individual_members_to_users_by_email`
ertgl Oct 22, 2025
bc0467e
Add migration that links individual members to users by email
ertgl Oct 22, 2025
7365e8b
Add tests for displaying user bio in profile
ertgl Oct 22, 2025
46cb8cd
Convert `contrib.django.forms` module into an app for testing
ertgl Oct 22, 2025
a69e9e1
Add note to describe why we need value normalization in the `BoundFie…
ertgl Oct 22, 2025
85c211d
Add tests for `BoundFieldWithCharacterCounter` class
ertgl Oct 22, 2025
65950f7
Add tests for `edit_profile` view
ertgl Oct 22, 2025
9fc7fd1
Add test for `IndividualMember.match_and_set_users_by_email` classmethod
ertgl Oct 23, 2025
bb963f6
Add test for `IndividualMember.send_account_invite_mails` classmethod
ertgl Oct 23, 2025
2588d94
Add test for `IndividualMember.send_account_invite_mail` method
ertgl Oct 23, 2025
94ba067
Fix Flake8 F541 (f-string is missing placeholders) error
ertgl Oct 23, 2025
81d7137
Add test to verify `IndividualMember.send_account_invite_mails` preve…
ertgl Oct 23, 2025
db9a7dd
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 23, 2025
aa9d1cb
Reduce resource usage of some tests
ertgl Oct 23, 2025
f37e3f8
Add note to account-invite mail about matching GitHub username for Tr…
ertgl Oct 23, 2025
ef54d5b
Update subject of Individual Membership account-invite mail
ertgl Oct 23, 2025
b956bfb
Introduce `get_trac_username` function (please see the commit message…
ertgl Oct 23, 2025
f922710
Rename `get_trac_username` to `get_user_trac_username`
ertgl Oct 23, 2025
a0f2d47
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 23, 2025
b91a2a0
Fix Flake8 F401 (imported but unused) error
ertgl Oct 23, 2025
26eed00
Reduce resource usage of `IndividualMemberTransactionTests.test_send_…
ertgl Oct 23, 2025
c9cc83e
Add tests to ensure overriding user's Trac username works for every t…
ertgl Oct 23, 2025
a8984d5
Remove the 'noreply' sender address from the individual member accoun…
ertgl Oct 23, 2025
4725cfa
Format code
ertgl Oct 23, 2025
5b67032
Add tests for management command `send_individual_member_account_invi…
ertgl Oct 23, 2025
a42b481
Remove unnecessary test `test_trac_username_overrides_user_username`
ertgl Oct 24, 2025
58ed3bc
Fix test `BoundFieldWithCharacterCounterTests.test_characters_remaini…
ertgl Oct 24, 2025
29c77ed
Prevent rendering Trac stats for a user when the username is used by …
ertgl Oct 24, 2025
e13bd18
Fix Flake8 F401 (imported but unused) error
ertgl Oct 24, 2025
f314007
Rename `check_if_trac_username_is_overridden_for_another_user` to `ch…
ertgl Oct 24, 2025
86d44c4
Move code block inside `if` to prevent unnecessary execution
ertgl Oct 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.PHONY: all ci clean collectstatics compile-scss compile-scss-debug install run test watch-scss

APP_LIST ?= accounts aggregator blog contact dashboard djangoproject docs foundation fundraising legacy members releases svntogit tracdb
APP_LIST ?= accounts aggregator blog contact contrib.django.forms dashboard djangoproject docs foundation fundraising legacy members releases svntogit tracdb
SCSS = djangoproject/scss
STATIC = djangoproject/static

Expand Down
31 changes: 31 additions & 0 deletions accounts/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from django import forms
from django.contrib import admin

from .forms import ProfileForm
from .models import Profile


class ProfileAdminForm(forms.ModelForm):
class Meta:
model = Profile
fields = "__all__"

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["bio"].widget.attrs["maxlength"] = ProfileForm.base_fields[
"bio"
].max_length
self.fields["bio"].help_text = ProfileForm.base_fields["bio"].help_text


@admin.register(Profile)
class ProfileAdmin(admin.ModelAdmin):
list_display = [
"user__username",
"name",
"trac_username",
]
list_select_related = ["user"]
search_fields = ["user__username", "name", "trac_username"]
form = ProfileAdminForm
autocomplete_fields = ["user"]
13 changes: 12 additions & 1 deletion accounts/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from django.db.models import ProtectedError
from django.utils.translation import gettext_lazy as _

from contrib.django.forms.boundfields import BoundFieldWithCharacterCounter

from .models import Profile


Expand All @@ -20,10 +22,19 @@ class ProfileForm(forms.ModelForm):
email = forms.EmailField(
required=False, widget=forms.TextInput(attrs={"placeholder": _("Email")})
)
bio = forms.CharField(
bound_field_class=BoundFieldWithCharacterCounter,
required=False,
max_length=3_000,
widget=forms.Textarea(attrs={"placeholder": _("Bio")}),
help_text=_(
"URLs and email addresses are automatically converted into clickable links.",
),
)

class Meta:
model = Profile
fields = ["name"]
fields = ["name", "bio"]

def __init__(self, *args, **kwargs):
instance = kwargs.get("instance", None)
Expand Down
18 changes: 18 additions & 0 deletions accounts/migrations/0003_profile_bio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.2.7 on 2025-10-19 01:39

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("accounts", "0002_migrate_sha1_passwords"),
]

operations = [
migrations.AddField(
model_name="profile",
name="bio",
field=models.TextField(blank=True),
),
]
20 changes: 20 additions & 0 deletions accounts/migrations/0004_profile_trac_username.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 5.2.7 on 2025-10-22 23:07

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("accounts", "0003_profile_bio"),
]

operations = [
migrations.AddField(
model_name="profile",
name="trac_username",
field=models.CharField(
blank=True, db_index=True, default="", max_length=150
),
),
]
8 changes: 8 additions & 0 deletions accounts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=200, blank=True)
bio = models.TextField(blank=True)
trac_username = models.CharField(
max_length=150,
blank=True,
null=False,
default="",
db_index=True,
)

def __str__(self):
return self.name or str(self.user)
Loading