Skip to content

Drop Osztaly.tanev FK; drive class↔tanév via Tanev.osztalyok M2M and gate login on active tanév#5

Merged
PstasDev merged 1 commit into
mainfrom
copilot/remove-tanv-field-and-update-logic
Jul 1, 2026
Merged

Drop Osztaly.tanev FK; drive class↔tanév via Tanev.osztalyok M2M and gate login on active tanév#5
PstasDev merged 1 commit into
mainfrom
copilot/remove-tanv-field-and-update-logic

Conversation

Copilot AI commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Osztaly.tanev duplicated the Tanev.osztalyok M2M and made multi-tanév handling ambiguous. Class names should be computed against the currently active Tanev, and non-teacher/admin users outside the active tanév should not be able to log in.

Model & migration

  • Remove Osztaly.tanev FK; Tanev.osztalyok M2M is now the single source of truth.
  • Osztaly.__str__ delegates to get_current_year_name(), which is anchored on Tanev.get_active() (calendar-based fallback preserved only when no active tanév exists).
  • Read-only Osztaly.tanev property returns the latest linked Tanev via the reverse tanevek relation for compatibility with existing serializer code.
  • New migration 0035_remove_osztaly_tanev_field copies each existing FK link into the M2M via RunPython before dropping the column so no data is lost.

API surface (multi-tanév aware)

  • academic.OsztalySchema / OsztalyWithTeachersSchema expose both tanev (latest) and tanevek (full list) so the frontend can filter/group per tanév.
  • sync.OsztalySchema adds tanev_ids: List[int].
  • Create/update class endpoints still accept tanev_id, but now attach via tanev.add_osztaly(osztaly) instead of writing an FK.
  • All Osztaly.objects.select_related('tanev') / osztaly__tanev swapped for prefetch_related('tanevek') / osztaly__tanevek.
  • Callers updated: academic.py, sync.py, user_import.py, user_import_utils.py, configuration_wizard.py, import_users.py, demonstration.py, admin, resources.

Login access control

backend/api_modules/auth.py adds is_user_allowed_for_current_tanev(user) used by both /login (returns 403 with a Hungarian message) and JWTAuth.authenticate (so pre-existing tokens can't bypass it). A user is denied iff:

  • there is an active Tanev, and
  • they have a Profile, and
  • they are not is_admin, not an osztályfőnök, not Django is_staff/is_superuser, and
  • their osztaly is not in active_tanev.osztalyok.
active_tanev = Tanev.get_active()
if active_tanev and not active_tanev.osztalyok.filter(pk=profile.osztaly_id).exists():
    return False, "A felhasználó osztálya nem tartozik az aktuális tanévhez, ..."

@PstasDev
PstasDev marked this pull request as ready for review July 1, 2026 11:03
Copilot AI review requested due to automatic review settings July 1, 2026 11:03
@PstasDev
PstasDev merged commit 053011c into main Jul 1, 2026
2 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR removes the ambiguous Osztaly.tanev FK and standardizes the class↔school-year relationship on Tanev.osztalyok (M2M), while also enforcing “active tanév” access control during login/JWT authentication.

Changes:

  • Replaces Osztaly.tanev FK usage across APIs/imports/admin with Tanev.osztalyok M2M (+ “latest tanév” compatibility accessors/fields).
  • Adds a data migration to copy existing FK links into the M2M before dropping the FK column.
  • Gates /login and JWT token auth based on whether a student’s class belongs to the active tanév (with teacher/admin exemptions).

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
import_users.py Updates class creation to attach Osztaly to Tanev via M2M.
backend/api_modules/user_import.py Computes tanév label from osztaly.tanevek instead of FK.
backend/api_modules/user_import_utils.py Same M2M-based class attachment; updates docstring.
backend/api_modules/sync.py Adds tanev_ids and switches class/profile query optimization from FK to M2M prefetch.
backend/api_modules/configuration_wizard.py Creates classes without FK; links to current tanév via M2M.
backend/api_modules/auth.py Adds is_user_allowed_for_current_tanev() and enforces it in login + JWT auth.
backend/api_modules/academic.py Exposes tanevek list; updates create/update and fetches to use M2M.
api/resources.py Exports computed tanév display based on latest M2M-linked tanév.
api/models.py Removes FK field; __str__ and compatibility tanev property now use M2M and active tanév.
api/migrations/0035_remove_osztaly_tanev_field.py Copies FK relationship into M2M then drops the FK column.
api/management/commands/demonstration.py Demo data uses M2M linking instead of FK defaults.
api/admin.py Admin listing/filtering updated to use reverse tanevek and computed display.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +16 to +31
migrations.RunPython(
code=lambda apps, schema_editor: _copy_osztaly_tanev_to_m2m(apps),
reverse_code=migrations.RunPython.noop,
),
migrations.RemoveField(
model_name='osztaly',
name='tanev',
),
]


def _copy_osztaly_tanev_to_m2m(apps):
Osztaly = apps.get_model('api', 'Osztaly')
for osztaly in Osztaly.objects.exclude(tanev__isnull=True):
osztaly.tanev.osztalyok.add(osztaly)

Comment on lines +165 to +169
try:
from api.models import Profile, Tanev, Osztaly
except Exception as e: # pragma: no cover - very unlikely
print(f"is_user_allowed_for_current_tanev import error: {e}")
return True, ""
Comment thread api/models.py
Comment on lines 435 to +450
def __str__(self):
# A megjelenített osztálynév mindig az aktuálisan aktív tanév alapján
# kerül kiszámításra (a Tanev.osztalyok M2M az igazságforrás arra, hogy
# egy osztály melyik tanévben aktív).
return self.get_current_year_name()

def get_current_year_name(self, reference_tanev=None):
"""Az osztály neve egy adott tanévhez viszonyítva.

Ha nincs megadva ``reference_tanev``, akkor az aktuálisan aktív tanévet
használjuk. Ha nincs aktív tanév egyáltalán (pl. nyári szünet vagy
friss telepítés), akkor a mai dátum alapján próbálunk visszaesni egy
naptári becslésre, hogy a régi viselkedést megőrizzük.
"""
if reference_tanev is None:
reference_tanev = Tanev.get_active()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants