fnschool 20251021.80056.821__py3-none-any.whl → 20251027.81653.841__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of fnschool might be problematic. Click here for more details.

Files changed (60) hide show
  1. fnschoo1/__init__.py +1 -1
  2. fnschoo1/canteen/forms.py +2 -2
  3. fnschoo1/canteen/migrations/0018_alter_ingredient_updated_at.py +23 -0
  4. fnschoo1/canteen/migrations/0019_category_updated_at_ingredient_created_at_and_more.py +48 -0
  5. fnschoo1/canteen/migrations/0020_alter_ingredient_created_at.py +20 -0
  6. fnschoo1/canteen/models.py +23 -3
  7. fnschoo1/canteen/templates/canteen/category/list.html +1 -1
  8. fnschoo1/canteen/templates/canteen/consumption/create.html +6 -1
  9. fnschoo1/canteen/templates/canteen/ingredient/list.html +11 -2
  10. fnschoo1/canteen/templates/canteen/meal_type/list.html +1 -1
  11. fnschoo1/fnprofile/__init__.py +0 -0
  12. fnschoo1/fnprofile/admin.py +27 -0
  13. fnschoo1/fnprofile/apps.py +12 -0
  14. fnschoo1/fnprofile/forms.py +73 -0
  15. fnschoo1/fnprofile/migrations/0001_initial.py +213 -0
  16. fnschoo1/fnprofile/migrations/0002_auto_20251026_2235.py +42 -0
  17. fnschoo1/fnprofile/migrations/0003_alter_fnuser_options.py +20 -0
  18. fnschoo1/fnprofile/migrations/__init__.py +0 -0
  19. fnschoo1/fnprofile/models.py +90 -0
  20. fnschoo1/fnprofile/signals.py +20 -0
  21. fnschoo1/fnprofile/templates/fnprofile/create.html +18 -0
  22. fnschoo1/fnprofile/templates/fnprofile/detail.html +16 -0
  23. fnschoo1/fnprofile/templates/fnprofile/edit.html +16 -0
  24. fnschoo1/fnprofile/templates/fnprofile/log_in.html +22 -0
  25. fnschoo1/fnprofile/templates/fnprofile/log_out.html +12 -0
  26. fnschoo1/fnprofile/tests.py +3 -0
  27. fnschoo1/fnprofile/urls.py +15 -0
  28. fnschoo1/fnprofile/views.py +69 -0
  29. fnschoo1/fnschool/settings.py +4 -2
  30. fnschoo1/fnschool/settings_fn_profile_migration_0001.py +180 -0
  31. fnschoo1/fnschool/urls.py +7 -2
  32. fnschoo1/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
  33. fnschoo1/profiles/__init__.py +7 -0
  34. fnschoo1/profiles/forms.py +6 -0
  35. fnschoo1/profiles/migrations/0006_profile_created_at_profile_updated_at.py +27 -0
  36. fnschoo1/profiles/migrations/0007_alter_profile_created_at.py +20 -0
  37. fnschoo1/profiles/migrations/0008_alter_profile_groups_alter_profile_user_permissions.py +38 -0
  38. fnschoo1/profiles/models.py +32 -2
  39. fnschoo1/profiles/templates/profiles/create.html +1 -1
  40. fnschoo1/profiles/templates/profiles/detail.html +1 -1
  41. fnschoo1/profiles/templates/profiles/edit.html +4 -2
  42. fnschoo1/profiles/templates/profiles/log_in.html +1 -1
  43. fnschoo1/profiles/templates/profiles/log_out.html +1 -1
  44. fnschoo1/profiles/views.py +7 -1
  45. fnschoo1/static/css/fnschool.css +14 -0
  46. fnschoo1/static/js/fnschool.js +20 -0
  47. fnschoo1/templates/home.html +1 -1
  48. fnschoo1/templates/includes/_header.html +30 -6
  49. {fnschool-20251021.80056.821.dist-info → fnschool-20251027.81653.841.dist-info}/METADATA +1 -1
  50. {fnschool-20251021.80056.821.dist-info → fnschool-20251027.81653.841.dist-info}/RECORD +60 -35
  51. /fnschoo1/templates/base/{header_content_footer.html → document.html} +0 -0
  52. {fnschool-20251021.80056.821.dist-info → fnschool-20251027.81653.841.dist-info}/SOURCES.txt.py +0 -0
  53. {fnschool-20251021.80056.821.dist-info → fnschool-20251027.81653.841.dist-info}/WHEEL +0 -0
  54. {fnschool-20251021.80056.821.dist-info → fnschool-20251027.81653.841.dist-info}/dependency_links.txt.py +0 -0
  55. {fnschool-20251021.80056.821.dist-info → fnschool-20251027.81653.841.dist-info}/entry_points.txt +0 -0
  56. {fnschool-20251021.80056.821.dist-info → fnschool-20251027.81653.841.dist-info}/entry_points.txt.py +0 -0
  57. {fnschool-20251021.80056.821.dist-info → fnschool-20251027.81653.841.dist-info}/licenses/LICENSE +0 -0
  58. {fnschool-20251021.80056.821.dist-info → fnschool-20251027.81653.841.dist-info}/requires.txt.py +0 -0
  59. {fnschool-20251021.80056.821.dist-info → fnschool-20251027.81653.841.dist-info}/top_level.txt +0 -0
  60. {fnschool-20251021.80056.821.dist-info → fnschool-20251027.81653.841.dist-info}/top_level.txt.py +0 -0
@@ -0,0 +1,90 @@
1
+ from django.contrib.auth.models import (
2
+ AbstractBaseUser,
3
+ AbstractUser,
4
+ BaseUserManager,
5
+ PermissionsMixin,
6
+ User,
7
+ )
8
+ from django.db import models
9
+ from django.db.models.signals import post_save
10
+ from django.dispatch import receiver
11
+ from django.utils.translation import gettext as _
12
+ from fnschool import *
13
+
14
+ # Create your models here.
15
+
16
+
17
+ class Gender(models.TextChoices):
18
+ MALE = "M", _("Male")
19
+ FEMALE = "F", _("Female")
20
+ UNKNOWN = "U", "--"
21
+
22
+
23
+ class Fnuser(AbstractUser, PermissionsMixin):
24
+ groups = models.ManyToManyField(
25
+ "auth.Group",
26
+ verbose_name="groups",
27
+ blank=True,
28
+ help_text=_("The groups this user belongs to."),
29
+ related_name="fn_user_groups",
30
+ related_query_name="fn_user",
31
+ )
32
+ user_permissions = models.ManyToManyField(
33
+ "auth.Permission",
34
+ verbose_name="user permissions",
35
+ blank=True,
36
+ help_text=_("Specific permissions for this user."),
37
+ related_name="fn_user_permissions",
38
+ related_query_name="fn_user",
39
+ )
40
+ phone = models.CharField(
41
+ max_length=15, blank=True, null=True, verbose_name=_("Phone Number")
42
+ )
43
+ affiliation = models.CharField(
44
+ max_length=255, blank=True, null=True, verbose_name=_("Affiliation")
45
+ )
46
+
47
+ superior_department = models.CharField(
48
+ max_length=255,
49
+ blank=True,
50
+ null=True,
51
+ verbose_name=_("Superior department"),
52
+ )
53
+
54
+ date_of_birth = models.DateField(
55
+ blank=True, null=True, verbose_name=_("Date of Birth")
56
+ )
57
+ gender = models.CharField(
58
+ max_length=1,
59
+ choices=Gender.choices,
60
+ default=Gender.UNKNOWN,
61
+ verbose_name=_("Gender"),
62
+ )
63
+
64
+ address = models.CharField(
65
+ max_length=255, blank=True, null=True, verbose_name=_("Address")
66
+ )
67
+ avatar = models.ImageField(
68
+ upload_to="avatars/", blank=True, null=True, verbose_name=_("Avatar")
69
+ )
70
+ bio = models.TextField(
71
+ max_length=512, blank=True, verbose_name=_("Biography")
72
+ )
73
+
74
+ created_at = models.DateTimeField(
75
+ null=True, auto_now_add=True, verbose_name=_("Time of creating")
76
+ )
77
+
78
+ updated_at = models.DateTimeField(
79
+ null=True, auto_now=True, verbose_name=_("Time of updating")
80
+ )
81
+
82
+ class Meta:
83
+ verbose_name = _("User Information")
84
+ verbose_name_plural = _("User Information")
85
+
86
+ def __str__(self):
87
+ return _("{0}'s Information").format(self.username)
88
+
89
+
90
+ # The end.
@@ -0,0 +1,20 @@
1
+ from django.contrib.auth.models import User
2
+ from django.db.models.signals import post_save
3
+ from django.dispatch import receiver
4
+
5
+ from .models import Fnuser
6
+
7
+
8
+ @receiver(post_save, sender=User)
9
+ def create_user_fnprofile(sender, instance, created, **kwargs):
10
+ if created:
11
+ Fnuser.objects.create(user=instance)
12
+
13
+
14
+ @receiver(post_save, sender=User)
15
+ def save_user_fnprofile(sender, instance, **kwargs):
16
+ if hasattr(instance, "fnprofile"):
17
+ instance.fnprofile.save()
18
+
19
+
20
+ # The end.
@@ -0,0 +1,18 @@
1
+ {% extends "base/document.html" %}
2
+ {% load crispy_forms_tags %}
3
+ {% block title %}
4
+ {% trans "Create" %}
5
+ {% endblock %}
6
+ {% block content %}
7
+ <h2>{% trans "Create new account." %}</h2>
8
+ <form method="post" action="{% url 'fnprofile:create' %}">
9
+ {% csrf_token %} {{ form|crispy }}
10
+ <button type="submit button" class="btn btn-outline-primary">
11
+ {% trans "Confirm" %}
12
+ </button>
13
+ </form>
14
+ <p>
15
+ {% trans "Already have an account?" %}
16
+ <a href="{% url 'fnprofile:log_in' %}">{% trans "Login here" %}</a>
17
+ </p>
18
+ {% endblock %}
@@ -0,0 +1,16 @@
1
+ {% extends 'base/document.html' %}
2
+ {% block content %}
3
+ <h1>
4
+ {% blocktrans with user_name=fnprofile.user.username %} Hello, {{ user_name }}!
5
+ {% endblocktrans %}
6
+ </h1>
7
+ <p>Phone: {{ fnprofile.phone }}</p>
8
+ <p>Date of Birth: {{ fnprofile.date_of_birth }}</p>
9
+ <p>Affiliation: {{ fnprofile.affiliation }}</p>
10
+ <p>Superior Department: {{ fnprofile.superior_department }}</p>
11
+ <p>Date of Birth: {{ fnprofile.date_of_birth }}</p>
12
+ <p>Address: {{ fnprofile.address }}</p>
13
+ {% if fnprofile.avatar %}
14
+ <img src="{{ fnprofile.avatar.url }}" alt="Avatar" />
15
+ {% endif %}
16
+ {% endblock %}
@@ -0,0 +1,16 @@
1
+ {% extends "base/document.html" %}
2
+ {% load crispy_forms_tags %}
3
+ {% block title %}
4
+ {% trans "Update Profile" %}
5
+ {% endblock %}
6
+ {% block content %}
7
+ <h2>{% trans "Update Profile." %}</h2>
8
+ <form method="post"
9
+ action="{% url 'fnprofile:update' %}"
10
+ enctype="multipart/form-data">
11
+ {% csrf_token %} {{ form|crispy }}
12
+ <button type="submit button" class="btn btn-outline-primary">
13
+ {% trans "Update" %}
14
+ </button>
15
+ </form>
16
+ {% endblock %}
@@ -0,0 +1,22 @@
1
+ {% extends "base/document.html" %}
2
+ {% load crispy_forms_tags %}
3
+ {% block title %}
4
+ {% trans "Log in" %}
5
+ {% endblock %}
6
+ {% block content %}
7
+ <h2>{% trans "Log in" %}</h2>
8
+ {% if form.errors %}
9
+ <p>{% trans "Your username and password didn't match. Please try again." %}</p>
10
+ {% endif %}
11
+ <form method="post" action="{% url 'fnprofile:log_in' %}">
12
+ {% csrf_token %} {{ form|crispy }}
13
+ <input type="hidden" name="next" value="{{ request.GET.next }}" />
14
+ <button type="submit button" class="btn btn-outline-primary">
15
+ {% trans "Log in" %}
16
+ </button>
17
+ </form>
18
+ <p>
19
+ {% trans "Don't have an account?" %}
20
+ <a href="{% url 'fnprofile:create' %}">{% trans "Register here" %}</a>
21
+ </p>
22
+ {% endblock %}
@@ -0,0 +1,12 @@
1
+ {% extends "base/document.html" %}
2
+ {% block title %}
3
+ {% trans
4
+ "Logged Out" %}
5
+ {% endblock %}
6
+ {% block content %}
7
+ <h2>{% trans "Logged Out" %}</h2>
8
+ <p>{% trans "You have been successfully logged out." %}</p>
9
+ <p>
10
+ <a href="{% url 'fnprofile:log_in' %}">{% trans "Log in again" %}</a>
11
+ </p>
12
+ {% endblock %}
@@ -0,0 +1,3 @@
1
+ from django.test import TestCase
2
+
3
+ # Create your tests here.
@@ -0,0 +1,15 @@
1
+ from django.contrib import admin
2
+ from django.urls import include, path
3
+
4
+ from . import views
5
+
6
+ app_name = "fnprofile"
7
+
8
+ urlpatterns = [
9
+ path("detail", views.fnprofile_edit, name="detail"),
10
+ path("log_out", views.fnprofile_log_out, name="log_out"),
11
+ path("create", views.fnprofile_new, name="create"),
12
+ path("log_in", views.fnprofile_log_in, name="log_in"),
13
+ path("update", views.fnprofile_edit, name="update"),
14
+ ]
15
+ # The end.
@@ -0,0 +1,69 @@
1
+ from django.contrib import messages
2
+ from django.contrib.auth import authenticate, login, logout
3
+ from django.contrib.auth.decorators import login_required
4
+ from django.shortcuts import redirect, render
5
+ from django.urls import reverse_lazy
6
+ from django.views.generic import CreateView
7
+ from fnschool import _, count_chinese_characters
8
+ from fnschool.settings import LOGIN_URL
9
+
10
+ from .forms import FnuserForm, FnuserLoginForm
11
+
12
+ # Create your views here.
13
+
14
+
15
+ def fnprofile_new(request):
16
+ form = None
17
+ if request.method == "POST":
18
+ form = FnuserForm(request.POST)
19
+ if form.is_valid():
20
+ user = form.save(commit=False)
21
+ user.set_password(form.cleaned_data["password"])
22
+ user.username = form.cleaned_data["username"]
23
+ user.save()
24
+ login(request, user)
25
+ return redirect("home")
26
+ else:
27
+ form = FnuserForm()
28
+
29
+ return render(request, "fnprofile/create.html", {"form": form})
30
+
31
+
32
+ def fnprofile_log_in(request):
33
+ if request.method == "POST":
34
+ form = FnuserLoginForm(request, data=request.POST)
35
+ if form.is_valid():
36
+ username = form.cleaned_data.get("username")
37
+ password = form.cleaned_data.get("password")
38
+ user = authenticate(request, username=username, password=password)
39
+ if user is not None:
40
+ login(request, user)
41
+ next_url = request.POST.get("next") or reverse_lazy("home")
42
+ return redirect(next_url)
43
+ else:
44
+ form = FnuserLoginForm()
45
+ return render(request, "fnprofile/log_in.html", {"form": form})
46
+
47
+
48
+ def fnprofile_log_out(request):
49
+ logout(request)
50
+ return redirect("home")
51
+
52
+
53
+ @login_required
54
+ def fnprofile_edit(request):
55
+ if request.method == "POST":
56
+ form = FnuserForm(request.POST, request.FILES, instance=request.user)
57
+ print(request.FILES)
58
+ if form.is_valid():
59
+ form.save()
60
+ messages.success(
61
+ request, _("Fnuser has been updated successfully!")
62
+ )
63
+ return redirect("home")
64
+ else:
65
+ form = FnuserForm(instance=request.user)
66
+ return render(request, "fnprofile/edit.html", {"form": form})
67
+
68
+
69
+ # The end.
@@ -48,6 +48,7 @@ INSTALLED_APPS = [
48
48
  "crispy_bootstrap5", # For Bootstrap 5
49
49
  # fnschool apps.
50
50
  "fnschool",
51
+ "fnprofile",
51
52
  "profiles",
52
53
  "canteen",
53
54
  ]
@@ -106,7 +107,8 @@ DATABASES = {
106
107
 
107
108
  # Password validation
108
109
  # https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
109
- AUTH_USER_MODEL = "profiles.Profile"
110
+ AUTH_USER_MODEL = "fnprofile.Fnuser"
111
+ ## AUTH_USER_MODEL = "profiles.Profile"
110
112
  AUTH_PASSWORD_VALIDATORS = [
111
113
  {
112
114
  "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
@@ -164,7 +166,7 @@ DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
164
166
  MEDIA_URL = "/media/"
165
167
  MEDIA_ROOT = os.path.join(BASE_DIR, "media")
166
168
 
167
- LOGIN_URL = reverse_lazy("profiles:log_in")
169
+ LOGIN_URL = reverse_lazy("fnprofile:log_in")
168
170
 
169
171
  _settings_path = Path(__file__).parent / "_settings.py"
170
172
  if _settings_path.exists():
@@ -0,0 +1,180 @@
1
+ """
2
+ Django settings for fnschool project.
3
+
4
+ Generated by 'django-admin startproject' using Django 4.2.24.
5
+
6
+ For more information on this file, see
7
+ https://docs.djangoproject.com/en/4.2/topics/settings/
8
+
9
+ For the full list of settings and their values, see
10
+ https://docs.djangoproject.com/en/4.2/ref/settings/
11
+ """
12
+
13
+ import os
14
+ from pathlib import Path
15
+
16
+ from django.urls import reverse_lazy
17
+ from django.utils.translation import gettext_lazy as _
18
+
19
+ # Build paths inside the project like this: BASE_DIR / 'subdir'.
20
+ BASE_DIR = Path(__file__).resolve().parent.parent
21
+
22
+
23
+ # Quick-start development settings - unsuitable for production
24
+ # See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
25
+
26
+ # SECURITY WARNING: keep the secret key used in production secret!
27
+ SECRET_KEY = (
28
+ "django-insecure-vt(#owf#cbx)yo$9m^=%&-heu&txuc23&a5b=a=u@=274)2!9w"
29
+ )
30
+
31
+ # SECURITY WARNING: don't run with debug turned on in production!
32
+ DEBUG = True
33
+
34
+ ALLOWED_HOSTS = []
35
+
36
+
37
+ # Application definition
38
+
39
+ INSTALLED_APPS = [
40
+ "django.contrib.admin",
41
+ "django.contrib.auth",
42
+ "django.contrib.contenttypes",
43
+ "django.contrib.sessions",
44
+ "django.contrib.messages",
45
+ "django.contrib.staticfiles",
46
+ # site apps.
47
+ "crispy_forms",
48
+ "crispy_bootstrap5", # For Bootstrap 5
49
+ # fnschool apps.
50
+ "fnschool",
51
+ "fnprofile",
52
+ "profiles",
53
+ "canteen",
54
+ ]
55
+
56
+ CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
57
+ CRISPY_TEMPLATE_PACK = "bootstrap5"
58
+
59
+ MIDDLEWARE = [
60
+ "django.middleware.security.SecurityMiddleware",
61
+ "django.contrib.sessions.middleware.SessionMiddleware",
62
+ "django.middleware.locale.LocaleMiddleware",
63
+ "django.middleware.common.CommonMiddleware",
64
+ "django.middleware.csrf.CsrfViewMiddleware",
65
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
66
+ "django.contrib.messages.middleware.MessageMiddleware",
67
+ "django.middleware.clickjacking.XFrameOptionsMiddleware",
68
+ ]
69
+
70
+ ROOT_URLCONF = "fnschool.urls"
71
+
72
+ TEMPLATES = [
73
+ {
74
+ "BACKEND": "django.template.backends.django.DjangoTemplates",
75
+ "DIRS": [
76
+ os.path.join(BASE_DIR, "templates"),
77
+ ],
78
+ "APP_DIRS": True,
79
+ "OPTIONS": {
80
+ "context_processors": [
81
+ "django.template.context_processors.debug",
82
+ "django.template.context_processors.request",
83
+ "django.contrib.auth.context_processors.auth",
84
+ "django.contrib.messages.context_processors.messages",
85
+ "django.template.context_processors.i18n",
86
+ ],
87
+ "builtins": [
88
+ "django.templatetags.i18n",
89
+ ],
90
+ },
91
+ },
92
+ ]
93
+
94
+ WSGI_APPLICATION = "fnschool.wsgi.application"
95
+
96
+
97
+ # Database
98
+ # https://docs.djangoproject.com/en/4.2/ref/settings/#databases
99
+
100
+ DATABASES = {
101
+ "default": {
102
+ "ENGINE": "django.db.backends.sqlite3",
103
+ "NAME": BASE_DIR / "db.sqlite3",
104
+ }
105
+ }
106
+
107
+
108
+ # Password validation
109
+ # https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
110
+ ## AUTH_USER_MODEL = "fn_profile.FnUser"
111
+ AUTH_USER_MODEL = "profiles.Profile"
112
+ AUTH_PASSWORD_VALIDATORS = [
113
+ {
114
+ "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
115
+ },
116
+ {
117
+ "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
118
+ },
119
+ {
120
+ "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
121
+ },
122
+ {
123
+ "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
124
+ },
125
+ ]
126
+
127
+
128
+ # Internationalization
129
+ # https://docs.djangoproject.com/en/4.2/topics/i18n/
130
+
131
+ USE_TZ = True
132
+ TIME_ZONE = "Asia/Shanghai"
133
+
134
+ USE_I18N = True
135
+ USE_L10N = True
136
+
137
+ LANGUAGE_CODE = "zh-hans"
138
+
139
+ LANGUAGES = [
140
+ ("en", _("English")),
141
+ ("es", _("Spanish")),
142
+ ("fr", _("French")),
143
+ ("zh-hans", _("Simplified Chinese")),
144
+ ]
145
+ LOCALE_PATHS = [
146
+ os.path.join(BASE_DIR, "locale"),
147
+ ]
148
+
149
+ USE_TZ = True
150
+
151
+
152
+ # Static files (CSS, JavaScript, Images)
153
+ # https://docs.djangoproject.com/en/4.2/howto/static-files/
154
+
155
+ STATIC_URL = "/static/"
156
+ STATICFILES_DIRS = [
157
+ os.path.join(BASE_DIR, "static")
158
+ ] # Optional: for additional static directories
159
+ STATIC_ROOT = os.path.join(BASE_DIR, "static_collected")
160
+
161
+ # Default primary key field type
162
+ # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
163
+
164
+ DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
165
+
166
+ MEDIA_URL = "/media/"
167
+ MEDIA_ROOT = os.path.join(BASE_DIR, "media")
168
+
169
+ LOGIN_URL = reverse_lazy("profile:log_in")
170
+
171
+ _settings_path = Path(__file__).parent / "_settings.py"
172
+ if _settings_path.exists():
173
+ print(
174
+ ('Custom configuration "{_settings_path}" has been used.').format(
175
+ _settings_path=_settings_path.as_posix()
176
+ )
177
+ )
178
+ from ._settings import *
179
+
180
+ # The end.
fnschoo1/fnschool/urls.py CHANGED
@@ -15,9 +15,12 @@ Including another URLconf
15
15
  2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
16
16
  """
17
17
 
18
+ from django.conf import settings
18
19
  from django.conf.urls.i18n import i18n_patterns
20
+ from django.conf.urls.static import static
19
21
  from django.contrib import admin
20
- from django.urls import include, path
22
+ from django.urls import include, path, re_path
23
+ from django.views.static import serve
21
24
 
22
25
  from . import views
23
26
 
@@ -25,6 +28,8 @@ urlpatterns = [
25
28
  path("", views.home_view, name="home"),
26
29
  path("admin/", admin.site.urls),
27
30
  path("i18n/", include("django.conf.urls.i18n")),
28
- path("profiles/", include("profiles.urls")),
31
+ path("fnprofile/", include("fnprofile.urls")),
29
32
  path("canteen/", include("canteen.urls")),
30
33
  ]
34
+ if settings.DEBUG:
35
+ urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
@@ -0,0 +1,7 @@
1
+ import warnings
2
+
3
+ warnings.warn(
4
+ "The 'profiles' app is deprecated and will be removed in the future. Use 'profile' app instead.",
5
+ DeprecationWarning,
6
+ stacklevel=2,
7
+ )
@@ -36,6 +36,12 @@ class ProfileForm(ModelForm):
36
36
  ),
37
37
  )
38
38
 
39
+ def __init__(self, *args, **kwargs):
40
+ super().__init__(*args, **kwargs)
41
+ self.fields["avatar"].widget.attrs.update(
42
+ {"class": "form-control-file"}
43
+ )
44
+
39
45
  class Meta:
40
46
  current_year = date.today().year
41
47
  year_range = list(range(current_year - 100, current_year + 1))
@@ -0,0 +1,27 @@
1
+ # Generated by Django 4.2.25 on 2025-10-24 01:44
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("profiles", "0005_alter_profile_gender"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name="profile",
15
+ name="created_at",
16
+ field=models.DateTimeField(
17
+ auto_now_add=True, null=True, verbose_name="Time of creating"
18
+ ),
19
+ ),
20
+ migrations.AddField(
21
+ model_name="profile",
22
+ name="updated_at",
23
+ field=models.DateTimeField(
24
+ auto_now=True, null=True, verbose_name="更新时间"
25
+ ),
26
+ ),
27
+ ]
@@ -0,0 +1,20 @@
1
+ # Generated by Django 4.2.25 on 2025-10-26 14:16
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("profiles", "0006_profile_created_at_profile_updated_at"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name="profile",
15
+ name="created_at",
16
+ field=models.DateTimeField(
17
+ auto_now_add=True, null=True, verbose_name="创建时间"
18
+ ),
19
+ ),
20
+ ]
@@ -0,0 +1,38 @@
1
+ # Generated by Django 4.2.25 on 2025-10-26 14:35
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("auth", "0012_alter_user_first_name_max_length"),
10
+ ("profiles", "0007_alter_profile_created_at"),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AlterField(
15
+ model_name="profile",
16
+ name="groups",
17
+ field=models.ManyToManyField(
18
+ blank=True,
19
+ help_text="The groups this user belongs to.",
20
+ related_name="profile_groups",
21
+ related_query_name="profile",
22
+ to="auth.group",
23
+ verbose_name="groups",
24
+ ),
25
+ ),
26
+ migrations.AlterField(
27
+ model_name="profile",
28
+ name="user_permissions",
29
+ field=models.ManyToManyField(
30
+ blank=True,
31
+ help_text="这个用户的特定权限。",
32
+ related_name="profile_permissions",
33
+ related_query_name="profile",
34
+ to="auth.permission",
35
+ verbose_name="user permissions",
36
+ ),
37
+ ),
38
+ ]