1274 lines
37 KiB
Markdown
1274 lines
37 KiB
Markdown
# ABSOLUTE DON'TS:
|
|
- Verwendung von npm anstelle der Tailwind CDN
|
|
- Implementierung von Content Security Policy (CSP) - UNTER KEINEN UMSTÄNDEN!
|
|
- Implementierung von Cross-Site Request Forgery (CSRF) Schutz
|
|
- Implementierung von Security Headers
|
|
- Verwendung von TypeScript anstelle von JavaScript
|
|
- Implementierung von OAuth oder anderen externen Authentifizierungsmethoden
|
|
|
|
# HÄUFIGE FEHLER:
|
|
- Verwendung der falschen Datenbank (die korrekte ist: database/systades.db)
|
|
- Falsche Pfadangaben bei statischen Dateien
|
|
- Vergessen der deutschen Spracheinstellungen in Templates
|
|
- Nicht beachten der vorhandenen Projektstruktur
|
|
- Falsche Einbindung der Neural Network Background Animation
|
|
- Verwendung von englischen Variablennamen in deutschen Funktionen
|
|
- Vergessen der Mindmap-Datenstruktur gemäß der Roadmap
|
|
- NameError bei Verwendung von 'follows' anstatt 'user_follows' in API Endpunkten
|
|
- 404 Fehler bei /auth/login weil Route als /login definiert ist
|
|
|
|
# Häufige Fehler und Lösungen
|
|
|
|
## SQLAlchemy Beziehungsfehler
|
|
|
|
### Fehler: "AmbiguousForeignKeysError - Could not determine join condition"
|
|
|
|
**Fehlerbeschreibung:**
|
|
```
|
|
sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join condition between parent/child tables on relationship User.notifications - there are multiple foreign key paths linking the tables. Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table.
|
|
```
|
|
|
|
**Ursache:**
|
|
SQLAlchemy kann nicht bestimmen, welcher Foreign Key für eine Beziehung verwendet werden soll, wenn mehrere Foreign Keys zwischen zwei Tabellen existieren. In diesem Fall hat die `Notification` Tabelle sowohl `user_id` (Empfänger) als auch `related_user_id` (Auslöser) als Foreign Keys zur `User` Tabelle.
|
|
|
|
**Lösung:**
|
|
|
|
1. **Foreign Keys explizit angeben in der User-Klasse:**
|
|
```python
|
|
# In models.py - User Klasse
|
|
notifications = db.relationship('Notification', foreign_keys='Notification.user_id', backref='user', lazy=True, cascade="all, delete-orphan")
|
|
```
|
|
|
|
2. **Foreign Keys in der Notification-Klasse spezifizieren:**
|
|
```python
|
|
# In models.py - Notification Klasse
|
|
related_user = db.relationship('User', foreign_keys=[related_user_id])
|
|
related_post = db.relationship('SocialPost', foreign_keys=[related_post_id])
|
|
related_comment = db.relationship('SocialComment', foreign_keys=[related_comment_id])
|
|
related_thought = db.relationship('Thought', foreign_keys=[related_thought_id])
|
|
```
|
|
|
|
3. **Datenbank nach Änderungen aktualisieren:**
|
|
```bash
|
|
python3.11 -c "from app import app, db; from models import *; app.app_context().push(); db.create_all(); print('Datenbank erfolgreich aktualisiert')"
|
|
```
|
|
|
|
**Vorbeugende Maßnahmen:**
|
|
- Immer `foreign_keys` Parameter verwenden, wenn mehrere Foreign Keys zwischen Tabellen existieren
|
|
- Eindeutige Beziehungsnamen verwenden
|
|
- Dokumentation der Beziehungen in den Modellen
|
|
|
|
### Fehler: "Eager loading cannot be applied - dynamic relationship"
|
|
|
|
**Fehlerbeschreibung:**
|
|
```
|
|
'MindMapNode.children' does not support object population - eager loading cannot be applied.
|
|
```
|
|
|
|
**Ursache:**
|
|
SQLAlchemy kann kein Eager Loading (`joinedload`) auf `dynamic` Beziehungen anwenden. Dynamic Beziehungen sind Query-Objekte, die erst bei Zugriff ausgeführt werden.
|
|
|
|
**Lösung:**
|
|
|
|
1. **Dynamic Beziehungen ohne Eager Loading verwenden:**
|
|
```python
|
|
# Falsch - versucht joinedload auf dynamic relationship
|
|
node = MindMapNode.query.options(
|
|
joinedload(MindMapNode.children) # Fehler!
|
|
).get_or_404(node_id)
|
|
|
|
# Richtig - dynamic relationship direkt verwenden
|
|
node = MindMapNode.query.get_or_404(node_id)
|
|
children = node.children.all() # Explizit alle Kinder laden
|
|
has_children = node.children.count() > 0 # Anzahl prüfen
|
|
```
|
|
|
|
2. **Beziehung von dynamic auf lazy ändern (falls Eager Loading benötigt):**
|
|
```python
|
|
# In models.py - falls Eager Loading wirklich benötigt wird
|
|
children = db.relationship(
|
|
'MindMapNode',
|
|
secondary=node_relationship,
|
|
primaryjoin=(node_relationship.c.parent_id == id),
|
|
secondaryjoin=(node_relationship.c.child_id == id),
|
|
backref=db.backref('parents', lazy='select'), # Nicht dynamic
|
|
lazy='select' # Ermöglicht joinedload
|
|
)
|
|
```
|
|
|
|
3. **Separate Queries für bessere Performance:**
|
|
```python
|
|
# Hauptknoten laden
|
|
node = MindMapNode.query.get_or_404(node_id)
|
|
|
|
# Kinder in separater Query laden
|
|
children = MindMapNode.query.join(node_relationship).filter(
|
|
node_relationship.c.parent_id == node.id
|
|
).all()
|
|
```
|
|
|
|
**Vorbeugende Maßnahmen:**
|
|
- Dokumentiere, welche Beziehungen `dynamic` sind
|
|
- Verwende `lazy='select'` oder `lazy='joined'` wenn Eager Loading benötigt wird
|
|
- Teste API-Endpunkte nach Modelländerungen
|
|
|
|
### Fehler: "404 Not Found - Admin API Endpoint"
|
|
|
|
**Fehlerbeschreibung:**
|
|
```
|
|
404 Not Found: The requested URL was not found on the server.
|
|
Endpoint: /admin/api/dashboard-data, Method: GET
|
|
```
|
|
|
|
**Ursache:**
|
|
Der Admin-API-Endpunkt `/admin/api/dashboard-data` existiert nicht in der Anwendung.
|
|
|
|
**Lösung:**
|
|
|
|
1. **Admin-Dashboard-API-Route hinzufügen:**
|
|
```python
|
|
@app.route('/admin/api/dashboard-data', methods=['GET'])
|
|
@admin_required
|
|
def admin_dashboard_data():
|
|
"""Liefert Dashboard-Daten für den Admin-Bereich"""
|
|
try:
|
|
# Benutzerstatistiken
|
|
total_users = User.query.count()
|
|
active_users = User.query.filter_by(is_active=True).count()
|
|
admin_users = User.query.filter_by(role='admin').count()
|
|
|
|
# Weitere Statistiken...
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'data': {
|
|
'users': {
|
|
'total': total_users,
|
|
'active': active_users,
|
|
'admins': admin_users
|
|
}
|
|
# Weitere Daten...
|
|
}
|
|
})
|
|
except Exception as e:
|
|
ErrorHandler.log_exception(e, "admin/api/dashboard-data")
|
|
return jsonify({
|
|
'success': False,
|
|
'error': 'Fehler beim Laden der Dashboard-Daten',
|
|
'details': str(e)
|
|
}), 500
|
|
```
|
|
|
|
2. **Benötigte Imports hinzufügen:**
|
|
```python
|
|
from models import (
|
|
# ... bestehende Imports ...
|
|
SocialPost, SocialComment, Notification
|
|
)
|
|
```
|
|
|
|
**Vorbeugende Maßnahmen:**
|
|
- Alle API-Endpunkte dokumentieren
|
|
- Regelmäßige Tests der Admin-Funktionalität
|
|
- Fehlerbehandlung für alle API-Routen implementieren
|
|
|
|
### Fehler: "NameError: name 'follows' is not defined"
|
|
|
|
**Fehlerbeschreibung:**
|
|
```python
|
|
NameError: name 'follows' is not defined
|
|
Endpoint: /api/discover/users, Method: GET
|
|
```
|
|
|
|
**Ursache:**
|
|
Der Code versucht auf eine Variable namens `follows` zuzugreifen, aber die korrekte Tabelle heißt `user_follows` (definiert in models.py).
|
|
|
|
**Lösung:**
|
|
|
|
1. **Korrekten Tabellennamen verwenden:**
|
|
```python
|
|
# Falsch
|
|
not_following_subquery = db.session.query(follows.c.followed_id).filter(
|
|
follows.c.follower_id == current_user.id
|
|
).subquery()
|
|
|
|
# Richtig
|
|
not_following_subquery = db.session.query(user_follows.c.followed_id).filter(
|
|
user_follows.c.follower_id == current_user.id
|
|
).subquery()
|
|
```
|
|
|
|
2. **Import prüfen:**
|
|
```python
|
|
# In app.py - sicherstellen dass user_follows importiert ist
|
|
from models import (
|
|
db, User, Thought, Comment, MindMapNode, ThoughtRelation, ThoughtRating,
|
|
RelationType, Category, UserMindmap, UserMindmapNode, MindmapNote,
|
|
node_thought_association, user_thought_bookmark, node_relationship,
|
|
MindmapShare, PermissionType, SocialPost, SocialComment, Notification,
|
|
user_follows # Wichtig: user_follows importieren
|
|
)
|
|
```
|
|
|
|
**Vorbeugende Maßnahmen:**
|
|
- Tabellennamen in models.py dokumentieren
|
|
- Konsistente Benennung verwenden
|
|
- Import-Listen regelmäßig prüfen
|
|
|
|
### Fehler: "404 Not Found: /auth/login"
|
|
|
|
**Fehlerbeschreibung:**
|
|
```
|
|
404 Not Found: The requested URL was not found on the server.
|
|
Endpoint: /auth/login, Method: GET
|
|
```
|
|
|
|
**Ursache:**
|
|
Die Login-Route ist als `/login` definiert, aber das Frontend oder Links versuchen auf `/auth/login` zuzugreifen.
|
|
|
|
**Lösung:**
|
|
|
|
1. **Kompatibilitäts-Route hinzufügen:**
|
|
```python
|
|
# Haupt-Login-Route
|
|
@app.route('/login', methods=['GET', 'POST'])
|
|
@log_execution_time(component='AUTH')
|
|
def login():
|
|
# ... Login-Logik ...
|
|
return render_template('login.html')
|
|
|
|
# Kompatibilitäts-Route für /auth/login
|
|
@app.route('/auth/login', methods=['GET', 'POST'])
|
|
@log_execution_time(component='AUTH')
|
|
def auth_login():
|
|
"""Redirect /auth/login to /login for compatibility"""
|
|
return redirect(url_for('login'))
|
|
```
|
|
|
|
2. **Weitere Auth-Routen für Konsistenz:**
|
|
```python
|
|
@app.route('/auth/register', methods=['GET', 'POST'])
|
|
def auth_register():
|
|
return redirect(url_for('register'))
|
|
|
|
@app.route('/auth/logout')
|
|
def auth_logout():
|
|
return redirect(url_for('logout'))
|
|
```
|
|
|
|
**Vorbeugende Maßnahmen:**
|
|
- URL-Struktur konsistent halten
|
|
- Alle verwendeten Routes dokumentieren
|
|
- Redirects für Legacy-URLs implementieren
|
|
|
|
## Flask Application Context Fehler
|
|
|
|
### Fehler: "Working outside of application context"
|
|
|
|
**Fehlerbeschreibung:**
|
|
```
|
|
RuntimeError: Working outside of application context.
|
|
|
|
This typically means that you attempted to use functionality that needed
|
|
the current application. To solve this, set up an application context
|
|
with app.app_context(). See the documentation for more information.
|
|
```
|
|
|
|
**Ursache:**
|
|
Der Logger versucht auf Flask's `g` (global context) oder `request` zuzugreifen, bevor ein Flask Application Context existiert. Das passiert oft während des Imports oder bei der Initialisierung.
|
|
|
|
**Lösung:**
|
|
|
|
1. **Logger Context-Safe machen:**
|
|
```python
|
|
# In utils/logger.py JSONFormatter
|
|
try:
|
|
from flask import has_app_context, has_request_context
|
|
if has_app_context():
|
|
if hasattr(g, 'user_id'):
|
|
log_entry['user_id'] = g.user_id
|
|
except (ImportError, RuntimeError):
|
|
# Flask ist nicht verfügbar oder kein App-Context
|
|
pass
|
|
```
|
|
|
|
2. **Zirkuläre Imports vermeiden:**
|
|
```python
|
|
# Anstatt direkter Import von app:
|
|
# from app import app
|
|
|
|
# Verwende lazy loading:
|
|
def get_app():
|
|
try:
|
|
from flask import current_app
|
|
return current_app
|
|
except RuntimeError:
|
|
from app import app
|
|
return app
|
|
```
|
|
|
|
3. **Fehlende Imports hinzufügen:**
|
|
```python
|
|
# Sicherstellen, dass alle benötigten Module importiert sind
|
|
import time # Für g.start_time
|
|
from utils.logger import performance_monitor, log_user_activity
|
|
```
|
|
|
|
**Vorbeugende Maßnahmen:**
|
|
- Immer `has_app_context()` und `has_request_context()` prüfen
|
|
- Lazy Loading für Flask-App-Imports verwenden
|
|
- Try-Catch für Flask Context-abhängige Operationen
|
|
|
|
## Datenbankfehler
|
|
|
|
### Fehler: "no such column: user.password"
|
|
|
|
**Fehlerbeschreibung:**
|
|
```
|
|
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such column: user.password
|
|
[SQL: SELECT user.id AS user_id, user.username AS user_username, user.email AS user_email, user.password AS user_password, user.created_at AS user_created_at, user.is_active AS user_is_active, user.role AS user_role
|
|
FROM user
|
|
WHERE user.id = ?]
|
|
```
|
|
|
|
**Ursache:**
|
|
Die Spalte `password` fehlt in der Tabelle `user` der SQLite-Datenbank. Dies kann durch eine unvollständige Datenbankinitialisierung oder ein fehlerhaftes Schema-Update verursacht worden sein.
|
|
|
|
**Lösung:**
|
|
|
|
1. **Datenbank reparieren mit dem Fix-Skript**
|
|
|
|
```bash
|
|
python fix_user_table.py
|
|
```
|
|
|
|
Dieses Skript:
|
|
- Prüft, ob die Tabelle `user` existiert und erstellt sie, falls nicht
|
|
- Prüft, ob die Spalte `password` existiert und fügt sie hinzu, falls nicht
|
|
|
|
2. **Standardbenutzer erstellen**
|
|
|
|
```bash
|
|
python create_default_users.py
|
|
```
|
|
|
|
Dieses Skript:
|
|
- Erstellt Standardbenutzer (admin, user), falls keine vorhanden sind
|
|
- Setzt Passwörter mit korrektem Hashing
|
|
|
|
3. **Datenbank testen**
|
|
|
|
```bash
|
|
python test_app.py
|
|
```
|
|
|
|
Dieses Skript überprüft:
|
|
- Ob die Datenbank existiert
|
|
- Ob die Tabelle `user` korrekt konfiguriert ist
|
|
- Ob Benutzer vorhanden sind
|
|
|
|
## Häufige Ursachen für Datenbankfehler
|
|
|
|
1. **Inkonsistente Datenbankschemas**
|
|
- Unterschiede zwischen dem SQLAlchemy-Modell und der tatsächlichen Datenbankstruktur
|
|
- Fehlende Spalten, die in den Modellen definiert sind
|
|
|
|
2. **Falsche Datenbankinitialisierung**
|
|
- Die Datenbank wurde nicht korrekt initialisiert
|
|
- Fehler bei der Migration oder dem Schema-Update
|
|
|
|
3. **Datenbankdatei-Korrumpierung**
|
|
- Die SQLite-Datenbankdatei wurde beschädigt
|
|
- Lösung: Sicherung wiederherstellen oder Datenbank neu erstellen
|
|
|
|
## Vorbeugende Maßnahmen
|
|
|
|
1. **Regelmäßige Backups**
|
|
- Tägliche Sicherung der Datenbankdatei
|
|
|
|
2. **Schema-Validierung**
|
|
- Regelmäßige Überprüfung der Datenbankstruktur
|
|
- Automatisierte Tests für Datenbankschema
|
|
|
|
3. **Datenbankmigration**
|
|
- Verwenden Sie Flask-Migrate für strukturierte Datenbank-Updates
|
|
- Dokumentieren Sie alle Schemaänderungen
|
|
|
|
## Content Security Policy (CSP)
|
|
|
|
### Problem: Externe Ressourcen werden nicht geladen
|
|
**Fehler:** Externe Ressourcen wie CDNs werden nicht korrekt geladen.
|
|
|
|
**Lösung:**
|
|
1. Stellen Sie sicher, dass die URLs in den Templates korrekt sind:
|
|
```html
|
|
<link href="https://cdn.tailwindcss.com" rel="stylesheet">
|
|
```
|
|
|
|
2. Überprüfen Sie die Netzwerkverbindung und ob die CDN-Domains erreichbar sind.
|
|
|
|
3. Verwenden Sie lokale Ressourcen als Alternative:
|
|
```html
|
|
<link href="{{ url_for('static', filename='css/tailwind.min.css') }}" rel="stylesheet">
|
|
```
|
|
|
|
### Problem: CSP-Fehler (script-src-elem 'none', font-src 'none', etc.)
|
|
**Fehler:** Skripte werden aufgrund von CSP-Einschränkungen blockiert.
|
|
|
|
**Lösung:**
|
|
1. WICHTIG: CSP-Einschränkungen NICHT implementieren! Diese sind im Projekt unerwünscht.
|
|
|
|
2. Falls CSP-Header bereits vorhanden sind, entfernen Sie diese aus der Anwendung:
|
|
```python
|
|
# Entfernen Sie diesen Code, falls vorhanden:
|
|
# @app.after_request
|
|
# def add_security_headers(response):
|
|
# response.headers['Content-Security-Policy'] = '...'
|
|
# return response
|
|
```
|
|
|
|
3. Stellen Sie sicher, dass externe Ressourcen direkt geladen werden:
|
|
```html
|
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
<script src="{{ url_for('static', filename='js/alpine.min.js') }}" defer></script>
|
|
```
|
|
|
|
4. Für Inline-Skripte keine Nonce verwenden:
|
|
```html
|
|
<script>
|
|
// Inline-JavaScript-Code ohne Einschränkungen
|
|
</script>
|
|
```
|
|
|
|
### Problem: Tailwind CSS CDN wird blockiert
|
|
**Fehler:** Tailwind CSS kann nicht von CDN geladen werden.
|
|
|
|
**Lösung:**
|
|
1. Verwenden Sie die lokale Version von Tailwind CSS:
|
|
```html
|
|
<link href="{{ url_for('static', filename='css/tailwind.min.css') }}" rel="stylesheet">
|
|
```
|
|
|
|
2. Alternativ können Sie die CDN-Version direkt im Template einbinden:
|
|
```html
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
```
|
|
|
|
3. Stellen Sie sicher, dass die Datei `static/css/tailwind.min.css` existiert und aktuell ist.
|
|
|
|
## Authentifizierung
|
|
|
|
### Problem: Login funktioniert nicht
|
|
**Fehler:** Benutzer kann sich nicht einloggen.
|
|
|
|
**Lösung:**
|
|
1. Standard-Admin-Benutzer erstellen: `python TOOLS.py user:admin`
|
|
2. Passwort zurücksetzen: `python TOOLS.py user:reset-pw -u USERNAME -p NEWPASSWORD`
|
|
|
|
## Neural Network Background
|
|
|
|
### Problem: Hintergrund-Animation wird nicht angezeigt
|
|
**Fehler:** Die Neural Network Animation im Hintergrund erscheint nicht.
|
|
|
|
**Lösung:**
|
|
1. Überprüfen Sie, ob die Datei `static/neural-network-background.js` korrekt eingebunden ist:
|
|
```html
|
|
<script src="{{ url_for('static', filename='neural-network-background.js') }}"></script>
|
|
```
|
|
|
|
2. Initialisieren Sie die Animation im Template:
|
|
```html
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const background = new NeuralNetworkBackground();
|
|
background.initialize();
|
|
background.animate();
|
|
});
|
|
</script>
|
|
```
|
|
|
|
3. Stellen Sie sicher, dass keine CSS-Regeln die Animation überdecken:
|
|
```css
|
|
#neural-network-background {
|
|
z-index: -10;
|
|
opacity: 1;
|
|
}
|
|
```
|
|
|
|
## Mindmap-Funktionalität
|
|
|
|
### Problem: Mindmap-Daten werden nicht geladen
|
|
**Fehler:** Die dynamische Mindmap zeigt keine Daten an.
|
|
|
|
**Lösung:**
|
|
1. Überprüfen Sie die API-Endpunkte für die Mindmap-Daten:
|
|
```python
|
|
@app.route('/api/mindmap/nodes', methods=['GET'])
|
|
def get_mindmap_nodes():
|
|
# Implementierung...
|
|
```
|
|
|
|
2. Stellen Sie sicher, dass die AJAX-Anfragen korrekt implementiert sind:
|
|
```javascript
|
|
fetch('/api/mindmap/nodes')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
// Verarbeitung der Mindmap-Daten
|
|
});
|
|
```
|
|
|
|
3. Überprüfen Sie die Datenbankeinträge für Mindmap-Knoten und -Verbindungen.
|
|
|
|
## ChatGPT-Assistent
|
|
|
|
### Problem: Assistent reagiert nicht auf Eingaben
|
|
**Fehler:** Der ChatGPT-Assistent verarbeitet keine Benutzereingaben.
|
|
|
|
**Lösung:**
|
|
1. Überprüfen Sie die Einbindung der JavaScript-Datei:
|
|
```html
|
|
<script src="{{ url_for('static', filename='js/modules/chatgpt-assistant.js') }}"></script>
|
|
```
|
|
|
|
2. Stellen Sie sicher, dass der Assistent korrekt initialisiert wird:
|
|
```javascript
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const assistant = new ChatGPTAssistant();
|
|
assistant.initialize();
|
|
});
|
|
```
|
|
|
|
3. Überprüfen Sie die API-Endpunkte für die Kommunikation mit dem Assistenten.
|
|
|
|
# 🔧 SysTades Social Network - Häufige Fehler und Lösungen
|
|
|
|
## 📋 Überblick
|
|
Diese Datei enthält eine Sammlung häufig auftretender Fehler und deren bewährte Lösungen für die SysTades Social Network Anwendung.
|
|
|
|
---
|
|
|
|
## 🗄️ Datenbankfehler
|
|
|
|
### 1. Tabellen existieren nicht
|
|
**Fehler**: `sqlite3.OperationalError: no such table: users`
|
|
**Ursache**: Datenbank wurde nicht initialisiert
|
|
**Lösung**:
|
|
```bash
|
|
python3.11 init_db.py
|
|
```
|
|
|
|
### 2. Foreign Key Constraints
|
|
**Fehler**: `FOREIGN KEY constraint failed`
|
|
**Ursache**: Versuch, einen Datensatz zu löschen, der von anderen referenziert wird
|
|
**Lösung**:
|
|
```python
|
|
# Erst abhängige Datensätze löschen
|
|
user_follows.query.filter(user_follows.c.follower_id == user_id).delete()
|
|
user_follows.query.filter(user_follows.c.followed_id == user_id).delete()
|
|
# Dann Hauptdatensatz
|
|
user = User.query.get(user_id)
|
|
db.session.delete(user)
|
|
db.session.commit()
|
|
```
|
|
|
|
### 3. Migration Fehler
|
|
**Fehler**: `alembic.util.exc.CommandError: Can't locate revision identified by`
|
|
**Ursache**: Inkonsistente Migration History
|
|
**Lösung**:
|
|
```bash
|
|
rm -rf migrations/
|
|
flask db init
|
|
flask db migrate -m "Initial migration"
|
|
flask db upgrade
|
|
```
|
|
|
|
---
|
|
|
|
## 🔐 Authentifizierung & Session Fehler
|
|
|
|
### 4. Login erforderlich Fehler
|
|
**Fehler**: `401 Unauthorized`
|
|
**Ursache**: Benutzer nicht angemeldet für geschützte Route
|
|
**Lösung**:
|
|
```python
|
|
@login_required
|
|
def protected_route():
|
|
# Prüfe explizit auf current_user
|
|
if not current_user.is_authenticated:
|
|
return jsonify({'error': 'Login erforderlich'}), 401
|
|
```
|
|
|
|
### 5. Session Timeout
|
|
**Fehler**: Session läuft ab, Benutzer wird ausgeloggt
|
|
**Ursache**: Kurze Session-Lebensdauer
|
|
**Lösung**:
|
|
```python
|
|
# In app.py
|
|
app.permanent_session_lifetime = timedelta(days=7)
|
|
```
|
|
|
|
### 6. CSRF Token Fehler
|
|
**Fehler**: `400 Bad Request: The CSRF token is missing`
|
|
**Ursache**: Fehlender oder ungültiger CSRF Token
|
|
**Lösung**:
|
|
```html
|
|
<!-- In Templates -->
|
|
<meta name="csrf-token" content="{{ csrf_token() }}">
|
|
```
|
|
|
|
---
|
|
|
|
## 🌐 API & AJAX Fehler
|
|
|
|
### 7. JSON Parsing Fehler
|
|
**Fehler**: `JSONDecodeError: Expecting value`
|
|
**Ursache**: Leerer oder ungültiger JSON Body
|
|
**Lösung**:
|
|
```python
|
|
try:
|
|
data = request.get_json()
|
|
if not data:
|
|
return jsonify({'error': 'Leerer JSON Body'}), 400
|
|
except Exception as e:
|
|
return jsonify({'error': 'Ungültiges JSON Format'}), 400
|
|
```
|
|
|
|
### 8. CORS Fehler
|
|
**Fehler**: `Access-Control-Allow-Origin` header fehlt
|
|
**Ursache**: Frontend und Backend auf verschiedenen Ports
|
|
**Lösung**:
|
|
```python
|
|
# CORS headers explizit setzen
|
|
@app.after_request
|
|
def after_request(response):
|
|
response.headers.add('Access-Control-Allow-Origin', '*')
|
|
response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization')
|
|
response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE')
|
|
return response
|
|
```
|
|
|
|
### 9. 413 Request Entity Too Large
|
|
**Fehler**: File upload zu groß
|
|
**Ursache**: Datei überschreitet MAX_CONTENT_LENGTH
|
|
**Lösung**:
|
|
```python
|
|
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB
|
|
```
|
|
|
|
---
|
|
|
|
## 🎨 Frontend & UI Fehler
|
|
|
|
### 10. JavaScript Fehler
|
|
**Fehler**: `TypeError: Cannot read property of undefined`
|
|
**Ursache**: DOM Element nicht gefunden
|
|
**Lösung**:
|
|
```javascript
|
|
const element = document.getElementById('myElement');
|
|
if (element) {
|
|
// Nur ausführen wenn Element existiert
|
|
element.addEventListener('click', handleClick);
|
|
}
|
|
```
|
|
|
|
### 11. CSS nicht geladen
|
|
**Fehler**: Styling fehlt komplett
|
|
**Ursache**: Falsche CSS-Pfade oder Cache-Problem
|
|
**Lösung**:
|
|
```html
|
|
<!-- Versionierung für Cache-Busting -->
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/social.css') }}?v=2.0">
|
|
```
|
|
|
|
### 12. Mobile Responsiveness
|
|
**Fehler**: UI bricht auf mobilen Geräten
|
|
**Ursache**: Fehlende Viewport Meta-Tag oder falsche CSS
|
|
**Lösung**:
|
|
```html
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
```
|
|
|
|
---
|
|
|
|
## 📁 Datei & Upload Fehler
|
|
|
|
### 13. Datei Upload Fehler
|
|
**Fehler**: `OSError: [Errno 2] No such file or directory`
|
|
**Ursache**: Upload-Verzeichnis existiert nicht
|
|
**Lösung**:
|
|
```python
|
|
import os
|
|
upload_folder = 'static/uploads'
|
|
os.makedirs(upload_folder, exist_ok=True)
|
|
```
|
|
|
|
### 14. Dateiberechtigungen
|
|
**Fehler**: `PermissionError: [Errno 13] Permission denied`
|
|
**Ursache**: Falsche Dateiberechtigungen
|
|
**Lösung**:
|
|
```bash
|
|
chmod 755 static/
|
|
chmod -R 644 static/uploads/
|
|
```
|
|
|
|
---
|
|
|
|
## 🚀 Performance Probleme
|
|
|
|
### 15. Langsame Datenbankabfragen
|
|
**Problem**: Seitenladezeiten > 3 Sekunden
|
|
**Ursache**: Fehlende Indizes, N+1 Queries
|
|
**Lösung**:
|
|
```python
|
|
# Indizes hinzufügen
|
|
class User(db.Model):
|
|
username = db.Column(db.String(80), unique=True, nullable=False, index=True)
|
|
|
|
# Eager Loading verwenden
|
|
posts = SocialPost.query.options(joinedload(SocialPost.author)).all()
|
|
```
|
|
|
|
### 16. Memory Leaks
|
|
**Problem**: Speicherverbrauch steigt kontinuierlich
|
|
**Ursache**: Sessions nicht geschlossen, große Objekte im Memory
|
|
**Lösung**:
|
|
```python
|
|
try:
|
|
# Database operations
|
|
db.session.commit()
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
raise
|
|
finally:
|
|
db.session.close()
|
|
```
|
|
|
|
### 17. JavaScript Performance
|
|
**Problem**: UI friert ein bei großen Datenmengen
|
|
**Ursache**: Blocking Operations im Main Thread
|
|
**Lösung**:
|
|
```javascript
|
|
// Pagination verwenden
|
|
const POSTS_PER_PAGE = 10;
|
|
|
|
// Virtual Scrolling für große Listen
|
|
function renderVisiblePosts(startIndex, endIndex) {
|
|
// Nur sichtbare Posts rendern
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🔄 Real-time & WebSocket Fehler
|
|
|
|
### 18. WebSocket Connection Failed
|
|
**Fehler**: `WebSocket connection to 'ws://localhost:5000/' failed`
|
|
**Ursache**: WebSocket Server nicht konfiguriert
|
|
**Lösung**:
|
|
```python
|
|
# Für Real-time Features
|
|
from flask_socketio import SocketIO
|
|
socketio = SocketIO(app, cors_allowed_origins="*")
|
|
```
|
|
|
|
### 19. Notification Polling Performance
|
|
**Problem**: Zu viele API Calls für Notifications
|
|
**Ursache**: Zu kurze Polling-Intervalle
|
|
**Lösung**:
|
|
```javascript
|
|
// Längere Intervalle verwenden
|
|
setInterval(pollNotifications, 30000); // 30 Sekunden statt 5
|
|
```
|
|
|
|
---
|
|
|
|
## 🧪 Testing & Debugging
|
|
|
|
### 20. Unit Test Fehler
|
|
**Fehler**: Tests schlagen fehl wegen Datenbankzustand
|
|
**Ursache**: Tests teilen sich Datenbankzustand
|
|
**Lösung**:
|
|
```python
|
|
@pytest.fixture
|
|
def client():
|
|
app.config['TESTING'] = True
|
|
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
|
|
with app.test_client() as client:
|
|
with app.app_context():
|
|
db.create_all()
|
|
yield client
|
|
db.drop_all()
|
|
```
|
|
|
|
### 21. Debug Mode Probleme
|
|
**Problem**: Änderungen werden nicht übernommen
|
|
**Ursache**: Debug Mode nicht aktiviert
|
|
**Lösung**:
|
|
```python
|
|
if __name__ == '__main__':
|
|
app.run(debug=True, host='0.0.0.0', port=5000)
|
|
```
|
|
|
|
---
|
|
|
|
## 🔧 Deployment Fehler
|
|
|
|
### 22. Gunicorn Worker Timeout
|
|
**Fehler**: `[CRITICAL] WORKER TIMEOUT`
|
|
**Ursache**: Lange laufende Requests
|
|
**Lösung**:
|
|
```bash
|
|
gunicorn --timeout 120 --workers 4 app:app
|
|
```
|
|
|
|
### 23. Static Files nicht gefunden
|
|
**Fehler**: 404 für CSS/JS Files in Production
|
|
**Ursache**: Nginx nicht konfiguriert für static files
|
|
**Lösung**:
|
|
```nginx
|
|
location /static {
|
|
alias /path/to/website/static;
|
|
expires 1y;
|
|
add_header Cache-Control "public, immutable";
|
|
}
|
|
```
|
|
|
|
### 24. Environment Variables
|
|
**Fehler**: `KeyError: 'SECRET_KEY'`
|
|
**Ursache**: .env Datei nicht geladen in Production
|
|
**Lösung**:
|
|
```bash
|
|
# In start.sh
|
|
export FLASK_ENV=production
|
|
export SECRET_KEY="your-secret-key"
|
|
python3.11 app.py
|
|
```
|
|
|
|
---
|
|
|
|
## 🐛 Logging & Monitoring
|
|
|
|
### 25. Logs nicht geschrieben
|
|
**Problem**: Log-Dateien bleiben leer
|
|
**Ursache**: Falsche Berechtigungen oder Pfad
|
|
**Lösung**:
|
|
```python
|
|
import os
|
|
log_dir = 'logs'
|
|
os.makedirs(log_dir, exist_ok=True)
|
|
```
|
|
|
|
### 26. Log Rotation
|
|
**Problem**: Log-Dateien werden zu groß
|
|
**Ursache**: Keine Log-Rotation konfiguriert
|
|
**Lösung**:
|
|
```python
|
|
from logging.handlers import RotatingFileHandler
|
|
|
|
handler = RotatingFileHandler(
|
|
'logs/app.log',
|
|
maxBytes=10*1024*1024, # 10MB
|
|
backupCount=5
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 Social Network Spezifische Fehler
|
|
|
|
### 27. Like/Unlike Race Conditions
|
|
**Problem**: Doppelte Likes bei schnellen Klicks
|
|
**Ursache**: Race Condition zwischen Requests
|
|
**Lösung**:
|
|
```python
|
|
from sqlalchemy import func
|
|
|
|
# Atomic operation
|
|
existing_like = PostLike.query.filter_by(
|
|
user_id=user_id,
|
|
post_id=post_id
|
|
).first()
|
|
|
|
if existing_like:
|
|
db.session.delete(existing_like)
|
|
post.like_count = func.greatest(post.like_count - 1, 0)
|
|
else:
|
|
new_like = PostLike(user_id=user_id, post_id=post_id)
|
|
db.session.add(new_like)
|
|
post.like_count += 1
|
|
|
|
db.session.commit()
|
|
```
|
|
|
|
### 28. Infinite Scroll Duplikate
|
|
**Problem**: Gleiche Posts werden mehrfach geladen
|
|
**Ursache**: Pagination offset/page confusion
|
|
**Lösung**:
|
|
```python
|
|
# Cursor-based pagination verwenden
|
|
last_id = request.args.get('last_id', type=int)
|
|
query = SocialPost.query.order_by(SocialPost.id.desc())
|
|
|
|
if last_id:
|
|
query = query.filter(SocialPost.id < last_id)
|
|
|
|
posts = query.limit(10).all()
|
|
```
|
|
|
|
### 29. Notification Spam
|
|
**Problem**: Zu viele Benachrichtigungen für gleiche Aktion
|
|
**Ursache**: Keine Deduplizierung
|
|
**Lösung**:
|
|
```python
|
|
# Prüfe auf existierende Notification
|
|
existing = Notification.query.filter_by(
|
|
user_id=target_user_id,
|
|
type='like',
|
|
related_post_id=post_id,
|
|
created_at=func.date(func.now())
|
|
).first()
|
|
|
|
if not existing:
|
|
# Nur neue Notification erstellen
|
|
notification = Notification(...)
|
|
db.session.add(notification)
|
|
```
|
|
|
|
---
|
|
|
|
## 🔍 Quick Debugging Commands
|
|
|
|
```bash
|
|
# Datenbankzustand prüfen
|
|
sqlite3 instance/site.db ".tables"
|
|
sqlite3 instance/site.db "SELECT * FROM users LIMIT 5;"
|
|
|
|
# Logs live verfolgen
|
|
tail -f logs/app.log
|
|
tail -f logs/errors.log
|
|
|
|
# Port prüfen
|
|
netstat -tulpn | grep :5000
|
|
|
|
# Python Dependencies prüfen
|
|
pip3.11 list | grep -i flask
|
|
|
|
# Datenbankmigrationen
|
|
flask db current
|
|
flask db history
|
|
|
|
# Performance monitoring
|
|
python3.11 -m cProfile -o profile.stats app.py
|
|
```
|
|
|
|
---
|
|
|
|
## 📋 Präventive Maßnahmen
|
|
|
|
### 1. Code Quality
|
|
- Immer Validierung für User Input
|
|
- Try-Catch Blöcke für externe API Calls
|
|
- Logging für alle kritischen Operationen
|
|
|
|
### 2. Testing
|
|
- Unit Tests für alle API Endpoints
|
|
- Integration Tests für User Flows
|
|
- Load Testing für Performance
|
|
|
|
### 3. Monitoring
|
|
- Error Rate Tracking
|
|
- Response Time Monitoring
|
|
- Database Query Performance
|
|
|
|
### 4. Security
|
|
- Input Sanitization
|
|
- SQL Injection Prevention
|
|
- XSS Protection
|
|
|
|
---
|
|
|
|
## 🔄 Update Checklist
|
|
|
|
Vor jedem Update prüfen:
|
|
- [ ] Backup der Datenbank erstellen
|
|
- [ ] Tests ausführen
|
|
- [ ] Dependencies aktualisieren
|
|
- [ ] Logs auf Fehler prüfen
|
|
- [ ] Performance Metrics vergleichen
|
|
|
|
---
|
|
|
|
**Letzte Aktualisierung**: Aktuelle Version
|
|
**Version**: 2.0.0 - Social Network Release
|
|
**Wartung**: Kontinuierlich aktualisiert
|
|
|
|
> **💡 Tipp**: Bei neuen Fehlern immer diese Datei aktualisieren und die Lösung dokumentieren!
|
|
|
|
## Logging-System
|
|
|
|
### Verbessertes Logging mit Emojis und Farben
|
|
|
|
Das System verwendet jetzt ein erweiterte Logging-System mit visuellen Verbesserungen:
|
|
|
|
#### Features:
|
|
- 🎨 **Farbige Konsolen-Ausgabe** mit ANSI-Codes
|
|
- 📝 **Emoji-basierte Kategorisierung** für bessere Übersicht
|
|
- 🔍 **Komponenten-spezifisches Logging** (AUTH, API, DB, SOCIAL, ERROR, etc.)
|
|
- ⏱️ **Performance-Monitoring** mit Zeitdauer-Tracking
|
|
- 📊 **Strukturierte JSON-Logs** für externe Analyse
|
|
- 🚀 **Decorator-basierte Instrumentierung** für automatisches Logging
|
|
|
|
#### Verwendung:
|
|
|
|
```python
|
|
from utils.logger import get_logger, log_execution_time, log_api_call, performance_monitor
|
|
|
|
# Logger-Instanz abrufen
|
|
logger = get_logger('SysTades')
|
|
|
|
# Einfache Logs mit Komponenten-Kategorisierung
|
|
logger.info("Benutzer angemeldet", component='AUTH', user='username')
|
|
logger.error("API-Fehler aufgetreten", component='API')
|
|
logger.warning("Datenbank-Verbindung langsam", component='DB')
|
|
|
|
# Spezielle Logging-Methoden
|
|
logger.auth_success('username', ip='192.168.1.1')
|
|
logger.user_action('username', 'mindmap_created', 'Neue Mindmap erstellt')
|
|
logger.performance_metric('response_time', 250.5, 'ms')
|
|
|
|
# Decorator für automatisches API-Logging
|
|
@log_api_call
|
|
def my_api_endpoint():
|
|
return jsonify({'success': True})
|
|
|
|
# Decorator für Performance-Monitoring
|
|
@performance_monitor('database_operation')
|
|
def complex_database_query():
|
|
# Komplexe Datenbankabfrage
|
|
pass
|
|
|
|
# Decorator für Ausführungszeit-Tracking
|
|
@log_execution_time(component='AUTH')
|
|
def login_process():
|
|
# Anmeldeprozess
|
|
pass
|
|
```
|
|
|
|
#### Log-Kategorien und Emojis:
|
|
|
|
| Komponente | Emoji | Beschreibung |
|
|
|------------|-------|--------------|
|
|
| AUTH | 🔐 | Authentifizierung und Autorisierung |
|
|
| API | 🌐 | API-Endpunkte und REST-Calls |
|
|
| DB | 🗄️ | Datenbankoperationen |
|
|
| SOCIAL | 👥 | Social-Media-Funktionen |
|
|
| SYSTEM | ⚙️ | System-Events |
|
|
| ERROR | 💥 | Fehlerbehandlung |
|
|
| SECURITY | 🛡️ | Sicherheitsereignisse |
|
|
| PERFORMANCE | ⚡ | Performance-Metriken |
|
|
|
|
#### Log-Level mit Emojis:
|
|
|
|
| Level | Emoji | Verwendung |
|
|
|-------|-------|------------|
|
|
| DEBUG | 🔍 | Entwicklung und Debugging |
|
|
| INFO | ✅ | Normale Informationen |
|
|
| WARNING | ⚠️ | Warnungen |
|
|
| ERROR | ❌ | Fehler |
|
|
| CRITICAL | 🚨 | Kritische Systemfehler |
|
|
|
|
#### Konfiguration:
|
|
|
|
Die Logger-Konfiguration erfolgt über die `setup_logging()` Funktion:
|
|
|
|
```python
|
|
# Automatische Konfiguration bei App-Start
|
|
setup_logging(app, log_level='INFO')
|
|
|
|
# Manuelle Konfiguration
|
|
from utils.logger import LoggerConfig
|
|
LoggerConfig.LOG_DIR = 'custom_logs'
|
|
LoggerConfig.MAX_LOG_SIZE = 20 * 1024 * 1024 # 20MB
|
|
```
|
|
|
|
#### Ausgabe-Beispiel:
|
|
|
|
```
|
|
⏰ 14:32:15.123 │ ✅ INFO │ 🔐 [AUTH ] │ 🚪 Benutzer admin angemeldet 👤 admin 🌍 192.168.1.1
|
|
⏰ 14:32:16.456 │ ❌ ERROR │ 💥 [ERROR ] │ 💥 API-Fehler: Verbindung zur Datenbank fehlgeschlagen ⚡ 1250.00ms
|
|
⏰ 14:32:17.789 │ ⚠️ WARNING │ 🗄️ [DB ] │ ⏱️ Langsame Datenbankabfrage detected 👤 admin ⚡ 2500.00ms
|
|
```
|
|
|
|
### Integration in die App
|
|
|
|
Die Hauptanwendung wurde vollständig auf das neue Logging-System umgestellt:
|
|
|
|
- Alle `print()` Statements wurden durch strukturierte Logs ersetzt
|
|
- Authentication-Events werden automatisch protokolliert
|
|
- API-Calls werden mit Performance-Metriken geloggt
|
|
- Datenbankoperationen haben detaillierte Logging-Ausgaben
|
|
- Fehlerbehandlung nutzt das zentrale Logging-System
|
|
|
|
### Troubleshooting
|
|
|
|
**Problem**: Logs erscheinen nicht in der Konsole
|
|
**Lösung**: Überprüfen Sie das LOG_LEVEL in der .env-Datei:
|
|
```bash
|
|
LOG_LEVEL=DEBUG # Für detaillierte Logs
|
|
LOG_LEVEL=INFO # Für normale Logs
|
|
```
|
|
|
|
**Problem**: Farben werden nicht angezeigt
|
|
**Lösung**: Stellen Sie sicher, dass Ihr Terminal ANSI-Codes unterstützt
|
|
|
|
**Problem**: Log-Dateien werden zu groß
|
|
**Lösung**: Konfigurieren Sie die Log-Rotation:
|
|
```python
|
|
LoggerConfig.MAX_LOG_SIZE = 5 * 1024 * 1024 # 5MB
|
|
LoggerConfig.BACKUP_COUNT = 10 # 10 Backup-Dateien
|
|
```
|
|
|
|
---
|
|
|
|
## 🚨 HÄUFIGE FEHLER UND LÖSUNGEN
|
|
|
|
## SQLAlchemy Relationship Fehler
|
|
|
|
### ❌ AttributeError: followed_id beim Social Feed
|
|
**Problem:** `AttributeError: followed_id` - Der Fehler tritt auf, wenn versucht wird, auf eine Spalte in einer Subquery zuzugreifen, die nicht existiert.
|
|
|
|
**Fehlerhafter Code:**
|
|
```python
|
|
def get_feed_posts(self, limit=20):
|
|
followed_users = self.following.subquery()
|
|
return SocialPost.query.join(
|
|
followed_users, SocialPost.user_id == followed_users.c.followed_id
|
|
).order_by(SocialPost.created_at.desc()).limit(limit)
|
|
```
|
|
|
|
**Lösung:**
|
|
```python
|
|
def get_feed_posts(self, limit=20):
|
|
# Hole alle User-IDs von Benutzern, denen ich folge
|
|
followed_user_ids = [user.id for user in self.following]
|
|
|
|
# Hole Posts von diesen Benutzern und meinen eigenen Posts
|
|
return SocialPost.query.filter(
|
|
SocialPost.user_id.in_(followed_user_ids + [self.id])
|
|
).order_by(SocialPost.created_at.desc()).limit(limit)
|
|
```
|
|
|
|
**Ursache:** Die `self.following` Beziehung gibt User-Objekte zurück, nicht die Zwischentabelle `user_follows`. Bei der Subquery-Erstellung wird versucht, auf `followed_id` zuzugreifen, aber die Subquery enthält User-Felder, nicht die Spalten der Zwischentabelle.
|
|
|
|
---
|
|
|
|
## SQL UNION Syntax Fehler
|
|
|
|
### Fehler: "sqlite3.OperationalError: near 'UNION': syntax error"
|
|
|
|
**Fehlerbeschreibung:**
|
|
```
|
|
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) near "UNION": syntax error
|
|
[SQL: SELECT anon_1.social_post_id AS anon_1_social_post_id ...
|
|
FROM ((SELECT social_post.id AS social_post_id ... LIMIT ? OFFSET ?) UNION SELECT social_post.id AS social_post_id ... WHERE social_post.user_id = ?) AS anon_1 ORDER BY anon_1.social_post_created_at DESC
|
|
LIMIT ? OFFSET ?]
|
|
```
|
|
|
|
**Ursache:**
|
|
SQLite hat spezielle Regeln für UNION-Abfragen. Das Problem tritt auf, wenn man versucht, eine Query mit bereits angewendetem LIMIT/OFFSET (Paginierung) in einer UNION zu verwenden. SQLAlchemy's `paginate()` fügt automatisch LIMIT und OFFSET hinzu, was zu ungültigem SQL führt.
|
|
|
|
**Problematischer Code:**
|
|
```python
|
|
# FEHLERHAFT - Union von paginierten Queries
|
|
followed_posts = current_user.get_feed_posts(limit=100) # Hat bereits LIMIT
|
|
own_posts = SocialPost.query.filter_by(user_id=current_user.id)
|
|
all_posts = followed_posts.union(own_posts).order_by(SocialPost.created_at.desc())
|
|
posts = all_posts.paginate(page=page, per_page=posts_per_page) # Zusätzliches LIMIT/OFFSET
|
|
```
|
|
|
|
**Lösung:**
|
|
|
|
1. **Eine einzige Query mit IN-Operator verwenden:**
|
|
```python
|
|
# RICHTIG - Eine einzelne Query ohne Union
|
|
@app.route('/feed')
|
|
@login_required
|
|
def social_feed():
|
|
page = request.args.get('page', 1, type=int)
|
|
posts_per_page = 10
|
|
|
|
# Alle User-IDs sammeln (gefolgte + eigene)
|
|
followed_user_ids = [user.id for user in current_user.following]
|
|
all_user_ids = followed_user_ids + [current_user.id]
|
|
|
|
# Eine einzige Query für alle Posts
|
|
all_posts = SocialPost.query.filter(
|
|
SocialPost.user_id.in_(all_user_ids)
|
|
).order_by(SocialPost.created_at.desc())
|
|
|
|
posts = all_posts.paginate(
|
|
page=page, per_page=posts_per_page, error_out=False
|
|
)
|
|
|
|
return render_template('social/feed.html', posts=posts)
|
|
```
|
|
|
|
2. **Model-Methode anpassen:**
|
|
```python
|
|
# In models.py - User Klasse
|
|
def get_feed_posts(self, limit=20):
|
|
"""Holt Posts für den Feed (von gefolgten Benutzern)"""
|
|
# Alle User-IDs sammeln
|
|
followed_user_ids = [user.id for user in self.following]
|
|
all_user_ids = followed_user_ids + [self.id]
|
|
|
|
# Eine einzige Query
|
|
return SocialPost.query.filter(
|
|
SocialPost.user_id.in_(all_user_ids)
|
|
).order_by(SocialPost.created_at.desc()).limit(limit)
|
|
```
|
|
|
|
3. **API-Endpunkt entsprechend anpassen:**
|
|
```python
|
|
@app.route('/api/feed')
|
|
@login_required
|
|
def get_feed_posts():
|
|
page = request.args.get('page', 1, type=int)
|
|
per_page = request.args.get('per_page', 10, type=int)
|
|
|
|
# Gleiche Logik wie im Web-Endpunkt
|
|
followed_user_ids = [user.id for user in current_user.following]
|
|
all_user_ids = followed_user_ids + [current_user.id]
|
|
|
|
all_posts = SocialPost.query.filter(
|
|
SocialPost.user_id.in_(all_user_ids)
|
|
).order_by(SocialPost.created_at.desc())
|
|
|
|
posts = all_posts.paginate(
|
|
page=page, per_page=per_page, error_out=False
|
|
)
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'posts': [post.to_dict() for post in posts.items],
|
|
'has_next': posts.has_next,
|
|
'has_prev': posts.has_prev,
|
|
'page': posts.page,
|
|
'pages': posts.pages,
|
|
'total': posts.total
|
|
})
|
|
```
|
|
|
|
**Vorbeugende Maßnahmen:**
|
|
- Vermeide UNION mit bereits paginierten Queries
|
|
- Verwende `IN`-Operator für einfache Filter-Kombinationen
|
|
- Teste SQL-Queries vor Produktionsfreigabe
|
|
- Dokumentiere komplexe Query-Logik ausführlich
|
|
|
|
**Weitere UNION-Regeln für SQLite:**
|
|
- UNION-Queries müssen die gleiche Anzahl von Spalten haben
|
|
- Spaltentypen müssen kompatibel sein
|
|
- ORDER BY nur am Ende der kompletten UNION-Query
|
|
- LIMIT/OFFSET nur am Ende, nicht in Subqueries
|
|
|
|
## SQLAlchemy Beziehungsfehler |