# qualifiers-model Erstellt und erweitert Django Models für das qualifiers-Modul (UEFA Qualifikationsturniere). ## Trigger - Neue QNode, QMatch, QGame, QGrouping Models - Model-Erweiterungen für Qualifiers - "qualifiers model", "qnode", "qmatch", "qgame" ## Kontext Das qualifiers-Modul verwaltet UEFA-Qualifikationsturniere mit folgender Hierarchie: - **QPath** → QTier → QNode (Turnier-Struktur) - **QNode** → QGrouping → QPosition (Gruppenbildung) - **QNode** → QMatch → QGame (Spielpaarungen) ## Regeln 1. **Immer db_index für ForeignKeys setzen** wenn häufig gefiltert wird: ```python scenario = models.ForeignKey('scheduler.Scenario', on_delete=models.CASCADE, db_index=True) ``` 2. **Meta-Klasse mit indexes und constraints**: ```python class Meta: ordering = ['-tier__ypos', '-stage__xpos'] constraints = [ models.UniqueConstraint(fields=['name', 'scenario'], name='unique_qnode_name_scenario') ] indexes = [ models.Index(fields=['scenario', 'tier', 'stage'], name='qnode_scenario_tier_stage_idx'), ] ``` 3. **related_name konsistent benennen**: - Plural für ForeignKey: `related_name='groupings'` - Model-Name für M2M: `related_name='qnodes_seeded'` 4. **State-Machine Pattern für QNode**: ```python NODESTATE_CHOICES = ( (0, "none"), # Warten auf Vorgänger (1, "seeded"), # Teams zugeordnet (2, "grouping"), # Gruppen gebildet (3, "draw"), # Spiele ausgelost (4, "finalized"), # Alle Ergebnisse da ) ``` 5. **GameMixin für QGame verwenden**: ```python from scheduler.models import GameMixin class QGame(GameMixin): # Erbt: homeGoals, awayGoals, resultEntered, season qnode = models.ForeignKey(QNode, ...) ``` 6. **Signals mit transaction.atomic()**: ```python @receiver(post_save, sender=QGame) def post_match_signal(sender, instance, created, **kwargs): with transaction.atomic(): # Signal-Logik ``` ## Beispiel: Neues Feld zu QNode ```python # qualifiers/models.py class QNode(models.Model): # Existierende Felder... # Neues Feld auto_advance = models.BooleanField( default=False, help_text="Automatisch zum nächsten State wechseln" ) ``` ## Beispiel: Neues Model ```python # qualifiers/models.py class QNodeViolation(models.Model): """Speichert Constraint-Verletzungen für einen Node.""" node = models.ForeignKey( QNode, on_delete=models.CASCADE, related_name='violations', db_index=True ) scenario = models.ForeignKey( 'scheduler.Scenario', on_delete=models.CASCADE, db_index=True ) type = models.CharField(max_length=50) # 'country_clash', 'distance', etc. message = models.TextField() severity = models.IntegerField(default=1) # 1=Warning, 2=Error created_at = models.DateTimeField(auto_now_add=True) class Meta: ordering = ['-created_at'] indexes = [ models.Index(fields=['node', 'scenario'], name='qnodeviolation_node_scen_idx'), ] ``` ## Migration erstellen ```bash conda activate planner python manage.py makemigrations qualifiers python manage.py migrate qualifiers ``` ## Referenz-Dateien - [qualifiers/models.py](qualifiers/models.py) - Alle Models - [docs/qualifiers/models/](docs/qualifiers/models/) - Dokumentation