Merge branch '3-user-subject-relations' into 'master'
Added base for user-subject relations See merge request JustScreaMy/facturio!4
This commit is contained in:
commit
a6bac23b9f
13 changed files with 185 additions and 39 deletions
|
@ -1,6 +1,13 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.contrib.auth.admin import UserAdmin
|
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from .models import User
|
from . import models
|
||||||
|
|
||||||
admin.site.register(User, UserAdmin)
|
|
||||||
|
@admin.register(models.User)
|
||||||
|
class UserAdmin(BaseUserAdmin):
|
||||||
|
fieldsets = (
|
||||||
|
*BaseUserAdmin.fieldsets,
|
||||||
|
(_("Subjects"), {"fields": ("subjects",)})
|
||||||
|
)
|
||||||
|
|
18
accounts/migrations/0003_user_subjects.py
Normal file
18
accounts/migrations/0003_user_subjects.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 5.0.1 on 2024-01-29 21:06
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("accounts", "0002_alter_user_email_alter_user_first_name_and_more"),
|
||||||
|
("subjects", "0003_alter_subject_options"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="user",
|
||||||
|
name="subjects",
|
||||||
|
field=models.ManyToManyField(to="subjects.subject"),
|
||||||
|
),
|
||||||
|
]
|
18
accounts/migrations/0004_alter_user_subjects.py
Normal file
18
accounts/migrations/0004_alter_user_subjects.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 5.0.1 on 2024-01-29 21:19
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("accounts", "0003_user_subjects"),
|
||||||
|
("subjects", "0003_alter_subject_options"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="user",
|
||||||
|
name="subjects",
|
||||||
|
field=models.ManyToManyField(blank=True, to="subjects.subject"),
|
||||||
|
),
|
||||||
|
]
|
|
@ -2,6 +2,8 @@ from django.contrib.auth.models import AbstractUser
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from subjects.models import Subject
|
||||||
|
|
||||||
|
|
||||||
class User(AbstractUser):
|
class User(AbstractUser):
|
||||||
REQUIRED_FIELDS = ["email", "first_name", "last_name"]
|
REQUIRED_FIELDS = ["email", "first_name", "last_name"]
|
||||||
|
@ -10,5 +12,7 @@ class User(AbstractUser):
|
||||||
last_name = models.CharField(_("last name"), max_length=150)
|
last_name = models.CharField(_("last name"), max_length=150)
|
||||||
email = models.EmailField(_("email address"))
|
email = models.EmailField(_("email address"))
|
||||||
|
|
||||||
|
subjects = models.ManyToManyField(Subject, blank=True)
|
||||||
|
|
||||||
class Meta(AbstractUser.Meta):
|
class Meta(AbstractUser.Meta):
|
||||||
pass
|
...
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
{% block title %}{% trans "About Me" %}{% endblock %}
|
{% block title %}{% trans "About Me" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2 class="mb-4">{% trans "About Me" %}</h2>
|
<h2 class="mb-2">{% trans "About Me" %}</h2>
|
||||||
<div class="card">
|
<div class="card mb-2">
|
||||||
<h5 class="card-header">
|
<h5 class="card-header">
|
||||||
{% blocktrans trimmed with firstname=request.user.first_name lastname=request.user.last_name %}Welcome,
|
{% blocktrans trimmed with firstname=request.user.first_name lastname=request.user.last_name %}Welcome,
|
||||||
{{ firstname }} {{ lastname }}!{% endblocktrans %}
|
{{ firstname }} {{ lastname }}!{% endblocktrans %}
|
||||||
|
@ -19,4 +19,14 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
{% if request.user.subjects.exists %}
|
||||||
|
<div class="card mb-2">
|
||||||
|
<h5 class="card-header">{% trans "Linked subjects" %}</h5>
|
||||||
|
<ul class="list-group list-group-flush">
|
||||||
|
{% for subject in request.user.subjects.all %}
|
||||||
|
<li class="list-group-item">{{ subject.name }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2024-01-29 20:46+0000\n"
|
"POT-Creation-Date: 2024-02-01 20:43+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -18,25 +18,31 @@ msgstr ""
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n "
|
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n "
|
||||||
"<= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
|
"<= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
|
||||||
|
|
||||||
|
#: accounts/admin.py:12 subjects/models.py:9
|
||||||
|
#: subjects/templates/subjects/index.html:4 templates/facturio/base.html:20
|
||||||
|
msgid "Subjects"
|
||||||
|
msgstr "Subjekty"
|
||||||
|
|
||||||
#: accounts/forms.py:17 accounts/templates/account/login.html:6
|
#: accounts/forms.py:17 accounts/templates/account/login.html:6
|
||||||
#: templates/facturio/base.html:39
|
#: templates/facturio/base.html:42
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
msgstr "Přihlásit se"
|
msgstr "Přihlásit se"
|
||||||
|
|
||||||
#: accounts/forms.py:30 accounts/templates/account/register.html:6
|
#: accounts/forms.py:30 accounts/templates/account/register.html:6
|
||||||
#: templates/facturio/base.html:42
|
#: templates/facturio/base.html:45
|
||||||
msgid "Register"
|
msgid "Register"
|
||||||
msgstr "Registrovat se"
|
msgstr "Registrovat se"
|
||||||
|
|
||||||
#: accounts/models.py:9
|
#: accounts/models.py:11
|
||||||
msgid "first name"
|
msgid "first name"
|
||||||
msgstr "křestní jméno"
|
msgstr "křestní jméno"
|
||||||
|
|
||||||
#: accounts/models.py:10
|
#: accounts/models.py:12
|
||||||
msgid "last name"
|
msgid "last name"
|
||||||
msgstr "přijmení"
|
msgstr "přijmení"
|
||||||
|
|
||||||
#: accounts/models.py:11
|
#: accounts/models.py:13
|
||||||
msgid "email address"
|
msgid "email address"
|
||||||
msgstr "emailová adresa"
|
msgstr "emailová adresa"
|
||||||
|
|
||||||
|
@ -59,6 +65,10 @@ msgstr "Uživatelské jméno: %(username)s"
|
||||||
msgid "Email: %(email)s"
|
msgid "Email: %(email)s"
|
||||||
msgstr "Email: %(email)s"
|
msgstr "Email: %(email)s"
|
||||||
|
|
||||||
|
#: accounts/templates/account/me.html:24
|
||||||
|
msgid "Linked subjects"
|
||||||
|
msgstr "Propojené Subjekty"
|
||||||
|
|
||||||
#: facturio/settings.py:109
|
#: facturio/settings.py:109
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr "Angličtina"
|
msgstr "Angličtina"
|
||||||
|
@ -92,11 +102,6 @@ msgstr "Vytvořit"
|
||||||
msgid "Subject"
|
msgid "Subject"
|
||||||
msgstr "Subjekt"
|
msgstr "Subjekt"
|
||||||
|
|
||||||
#: subjects/models.py:9 subjects/templates/subjects/index.html:4
|
|
||||||
#: templates/facturio/base.html:19
|
|
||||||
msgid "Subjects"
|
|
||||||
msgstr "Subjekty"
|
|
||||||
|
|
||||||
#: subjects/models.py:18 subjects/templates/subjects/index.html:12
|
#: subjects/models.py:18 subjects/templates/subjects/index.html:12
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Jméno"
|
msgstr "Jméno"
|
||||||
|
@ -129,14 +134,26 @@ msgstr "Vytvořit Subjekty"
|
||||||
msgid "Add new"
|
msgid "Add new"
|
||||||
msgstr "Přidat nový"
|
msgstr "Přidat nový"
|
||||||
|
|
||||||
|
#: subjects/templates/subjects/index.html:18
|
||||||
|
msgid "Connect"
|
||||||
|
msgstr "Propojit"
|
||||||
|
|
||||||
|
#: subjects/templates/subjects/index.html:35
|
||||||
|
msgid "Link"
|
||||||
|
msgstr "Propojit"
|
||||||
|
|
||||||
|
#: subjects/templates/subjects/index.html:38
|
||||||
|
msgid "Unlink"
|
||||||
|
msgstr "Odpojit"
|
||||||
|
|
||||||
#: templates/facturio/base.html:8 templates/facturio/index.html:4
|
#: templates/facturio/base.html:8 templates/facturio/index.html:4
|
||||||
msgid "App"
|
msgid "App"
|
||||||
msgstr "Aplikace"
|
msgstr "Aplikace"
|
||||||
|
|
||||||
#: templates/facturio/base.html:31
|
#: templates/facturio/base.html:34
|
||||||
msgid "Profile"
|
msgid "Profile"
|
||||||
msgstr "Profil"
|
msgstr "Profil"
|
||||||
|
|
||||||
#: templates/facturio/base.html:34
|
#: templates/facturio/base.html:37
|
||||||
msgid "Logout"
|
msgid "Logout"
|
||||||
msgstr "Odhlásit se"
|
msgstr "Odhlásit se"
|
||||||
|
|
16
subjects/migrations/0003_alter_subject_options.py
Normal file
16
subjects/migrations/0003_alter_subject_options.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# Generated by Django 5.0.1 on 2024-01-29 21:06
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("subjects", "0002_alter_subject_city_alter_subject_city_part_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name="subject",
|
||||||
|
options={"verbose_name": "Subject", "verbose_name_plural": "Subjects"},
|
||||||
|
),
|
||||||
|
]
|
|
@ -50,3 +50,6 @@ class Subject(models.Model):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
def get_linked_users(self) -> list[int]:
|
||||||
|
return list(self.user_set.values_list("id", flat=True))
|
||||||
|
|
|
@ -15,10 +15,13 @@
|
||||||
<th>{% trans "Zip Code" %}</th>
|
<th>{% trans "Zip Code" %}</th>
|
||||||
<th>{% trans "City" %}</th>
|
<th>{% trans "City" %}</th>
|
||||||
<th>{% trans "City part" %}</th>
|
<th>{% trans "City part" %}</th>
|
||||||
|
<th>{% trans "Connect" %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for subject in subjects %}
|
{% for subject in subjects %}
|
||||||
|
{# FIXME: in the future, or when this create problems, find a better way#}
|
||||||
|
{% with linked_users=subject.get_linked_users %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ subject.id }}</td>
|
<td>{{ subject.id }}</td>
|
||||||
<td>{{ subject.name }}</td>
|
<td>{{ subject.name }}</td>
|
||||||
|
@ -27,7 +30,17 @@
|
||||||
<td>{{ subject.zip_code }}</td>
|
<td>{{ subject.zip_code }}</td>
|
||||||
<td>{{ subject.city }}</td>
|
<td>{{ subject.city }}</td>
|
||||||
<td>{{ subject.city_part }}</td>
|
<td>{{ subject.city_part }}</td>
|
||||||
|
<td>
|
||||||
|
{% if request.user.id not in linked_users %}
|
||||||
|
<a href="{% url "subjects:link" subject_id=subject.id %}" class="btn btn-primary"
|
||||||
|
role="button">{% trans "Link" %}</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="{% url "subjects:unlink" subject_id=subject.id %}" class="btn btn-danger"
|
||||||
|
role="button">{% trans "Unlink" %}</a>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{% endwith %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -6,5 +6,7 @@ app_name = "subjects"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", views.main_page, name="list"),
|
path("", views.main_page, name="list"),
|
||||||
path("new/", views.create_subject, name="create")
|
path("new/", views.create_subject, name="create"),
|
||||||
|
path("link/<int:subject_id>", views.link_subject, name="link"),
|
||||||
|
path("unlink/<int:subject_id>", views.unlink_subject, name="unlink")
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from ares_util.ares import call_ares
|
from ares_util.ares import call_ares
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
@ -10,11 +11,13 @@ def build_address(street: str, zip_code: int | str, city: str, city_part: str) -
|
||||||
return f"{street}, {zip_code}, {city} - {city_part}"
|
return f"{street}, {zip_code}, {city} - {city_part}"
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
def main_page(req: HttpRequest) -> HttpResponse:
|
def main_page(req: HttpRequest) -> HttpResponse:
|
||||||
subjects = models.Subject.objects.all()
|
subjects = models.Subject.objects.all()
|
||||||
return render(req, "subjects/index.html", dict(subjects=subjects))
|
return render(req, "subjects/index.html", dict(subjects=subjects))
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
def create_subject(req: HttpRequest) -> HttpResponse:
|
def create_subject(req: HttpRequest) -> HttpResponse:
|
||||||
if req.method == "POST":
|
if req.method == "POST":
|
||||||
form = forms.CreateSubjectForm(data=req.POST)
|
form = forms.CreateSubjectForm(data=req.POST)
|
||||||
|
@ -45,3 +48,17 @@ def create_subject(req: HttpRequest) -> HttpResponse:
|
||||||
return render(req, "subjects/create.html", dict(form=form))
|
return render(req, "subjects/create.html", dict(form=form))
|
||||||
|
|
||||||
return HttpResponse(status=405)
|
return HttpResponse(status=405)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def link_subject(request: HttpRequest, subject_id: int) -> HttpResponse:
|
||||||
|
subject = models.Subject.objects.get(pk=subject_id)
|
||||||
|
subject.user_set.add(request.user)
|
||||||
|
return redirect(reverse("subjects:list"))
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def unlink_subject(request: HttpRequest, subject_id: int) -> HttpResponse:
|
||||||
|
subject = models.Subject.objects.get(pk=subject_id)
|
||||||
|
subject.user_set.remove(request.user)
|
||||||
|
return redirect(reverse("subjects:list"))
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<a class="navbar-brand" href="{% url "main-page" %}">Facturio</a>
|
<a class="navbar-brand" href="{% url "main-page" %}">Facturio</a>
|
||||||
|
{% if request.user.is_authenticated %}
|
||||||
<div class="collapse navbar-collapse">
|
<div class="collapse navbar-collapse">
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
|
@ -23,6 +24,8 @@
|
||||||
{#</li>#}
|
{#</li>#}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
<div class="collapse navbar-collapse justify-content-end">
|
<div class="collapse navbar-collapse justify-content-end">
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
|
|
18
utils/start.sh
Executable file
18
utils/start.sh
Executable file
|
@ -0,0 +1,18 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
#set -x
|
||||||
|
|
||||||
|
if [[ ! -f "manage.py" ]]; then
|
||||||
|
echo 'Control `manage.py` is not in current directory!'
|
||||||
|
echo 'Please, launch this script from the root directory'
|
||||||
|
echo 'Example: `./utils/start.sh`'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Compiling messages!"
|
||||||
|
./manage.py compilemessages >/dev/null
|
||||||
|
|
||||||
|
echo "Migrating"
|
||||||
|
./manage.py migrate >/dev/null
|
Reference in a new issue