2026-02-20 22:08:04 +01:00

3.4 KiB

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:

    scenario = models.ForeignKey('scheduler.Scenario', on_delete=models.CASCADE, db_index=True)
    
  2. Meta-Klasse mit indexes und constraints:

    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:

    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:

    from scheduler.models import GameMixin
    
    class QGame(GameMixin):
        # Erbt: homeGoals, awayGoals, resultEntered, season
        qnode = models.ForeignKey(QNode, ...)
    
  6. Signals mit transaction.atomic():

    @receiver(post_save, sender=QGame)
    def post_match_signal(sender, instance, created, **kwargs):
        with transaction.atomic():
            # Signal-Logik
    

Beispiel: Neues Feld zu QNode

# 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

# 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

conda activate planner
python manage.py makemigrations qualifiers
python manage.py migrate qualifiers

Referenz-Dateien