Compare commits
3 Commits
898e9b8163
...
improvemen
| Author | SHA1 | Date | |
|---|---|---|---|
| 2350cca32c | |||
| 671d259c44 | |||
| 28a1bb4b62 |
@@ -5,7 +5,6 @@ from stichworte.models import Stichwort
|
|||||||
from referenzen.models import Referenz
|
from referenzen.models import Referenz
|
||||||
from rollen.models import Rolle
|
from rollen.models import Rolle
|
||||||
import datetime
|
import datetime
|
||||||
from django.db.models import Q
|
|
||||||
|
|
||||||
class Dokumententyp(models.Model):
|
class Dokumententyp(models.Model):
|
||||||
name = models.CharField(max_length=100, primary_key=True)
|
name = models.CharField(max_length=100, primary_key=True)
|
||||||
@@ -129,7 +128,7 @@ class Vorgabe(models.Model):
|
|||||||
'vorgabe2': vorgabe2,
|
'vorgabe2': vorgabe2,
|
||||||
'conflict_type': 'date_range_intersection',
|
'conflict_type': 'date_range_intersection',
|
||||||
'message': f"Vorgaben {vorgabe1.Vorgabennummer()} and {vorgabe2.Vorgabennummer()} "
|
'message': f"Vorgaben {vorgabe1.Vorgabennummer()} and {vorgabe2.Vorgabennummer()} "
|
||||||
f"have intersecting validity periods"
|
f"überschneiden sich in der Geltungsdauer"
|
||||||
})
|
})
|
||||||
|
|
||||||
return conflicts
|
return conflicts
|
||||||
|
|||||||
@@ -2,6 +2,12 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<h1 class="mb-4">Suche</h1>
|
<h1 class="mb-4">Suche</h1>
|
||||||
|
|
||||||
|
{% if error_message %}
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<strong>Fehler:</strong> {{ error_message }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<!-- Search form -->
|
<!-- Search form -->
|
||||||
<form action="." method="post">
|
<form action="." method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
@@ -13,7 +19,9 @@
|
|||||||
id="query"
|
id="query"
|
||||||
name="q"
|
name="q"
|
||||||
placeholder="Suchbegriff eingeben …"
|
placeholder="Suchbegriff eingeben …"
|
||||||
required>
|
value="{{ search_term|default:'' }}"
|
||||||
|
required
|
||||||
|
maxlength="200">
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary">Suchen</button>
|
<button type="submit" class="btn btn-primary">Suchen</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
312
pages/tests.py
Normal file
312
pages/tests.py
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
from django.test import TestCase, Client
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.utils import timezone
|
||||||
|
from datetime import date, timedelta
|
||||||
|
from dokumente.models import Dokument, Vorgabe, VorgabeKurztext, VorgabeLangtext, Geltungsbereich, Dokumententyp, Thema
|
||||||
|
from stichworte.models import Stichwort
|
||||||
|
from unittest.mock import patch
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
class SearchViewTest(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
# Create test data
|
||||||
|
self.dokumententyp = Dokumententyp.objects.create(
|
||||||
|
name="Test Typ",
|
||||||
|
verantwortliche_ve="Test VE"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.thema = Thema.objects.create(
|
||||||
|
name="Test Thema",
|
||||||
|
erklaerung="Test Erklärung"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.dokument = Dokument.objects.create(
|
||||||
|
nummer="TEST-001",
|
||||||
|
dokumententyp=self.dokumententyp,
|
||||||
|
name="Test Dokument",
|
||||||
|
gueltigkeit_von=date.today(),
|
||||||
|
aktiv=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.vorgabe = Vorgabe.objects.create(
|
||||||
|
order=1,
|
||||||
|
nummer=1,
|
||||||
|
dokument=self.dokument,
|
||||||
|
thema=self.thema,
|
||||||
|
titel="Test Vorgabe Titel",
|
||||||
|
gueltigkeit_von=date.today()
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create text content
|
||||||
|
self.kurztext = VorgabeKurztext.objects.create(
|
||||||
|
abschnitt=self.vorgabe,
|
||||||
|
inhalt="Dies ist ein Test Kurztext mit Suchbegriff"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.langtext = VorgabeLangtext.objects.create(
|
||||||
|
abschnitt=self.vorgabe,
|
||||||
|
inhalt="Dies ist ein Test Langtext mit anderem Suchbegriff"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.geltungsbereich = Geltungsbereich.objects.create(
|
||||||
|
geltungsbereich=self.dokument,
|
||||||
|
inhalt="Test Geltungsbereich mit Suchbegriff"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_search_get_request(self):
|
||||||
|
"""Test GET request returns search form"""
|
||||||
|
response = self.client.get('/search/')
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, 'Suche')
|
||||||
|
self.assertContains(response, 'Suchbegriff')
|
||||||
|
|
||||||
|
def test_search_post_valid_term(self):
|
||||||
|
"""Test POST request with valid search term"""
|
||||||
|
response = self.client.post('/search/', {'q': 'Test'})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, 'Suchresultate für Test')
|
||||||
|
|
||||||
|
def test_search_case_insensitive(self):
|
||||||
|
"""Test that search is case insensitive"""
|
||||||
|
# Search for lowercase
|
||||||
|
response = self.client.post('/search/', {'q': 'test'})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, 'Suchresultate für test')
|
||||||
|
|
||||||
|
# Search for uppercase
|
||||||
|
response = self.client.post('/search/', {'q': 'TEST'})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, 'Suchresultate für TEST')
|
||||||
|
|
||||||
|
# Search for mixed case
|
||||||
|
response = self.client.post('/search/', {'q': 'TeSt'})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, 'Suchresultate für TeSt')
|
||||||
|
|
||||||
|
def test_search_in_kurztext(self):
|
||||||
|
"""Test search in Kurztext content"""
|
||||||
|
response = self.client.post('/search/', {'q': 'Suchbegriff'})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, 'TEST-001')
|
||||||
|
|
||||||
|
def test_search_in_langtext(self):
|
||||||
|
"""Test search in Langtext content"""
|
||||||
|
response = self.client.post('/search/', {'q': 'anderem'})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, 'TEST-001')
|
||||||
|
|
||||||
|
def test_search_in_titel(self):
|
||||||
|
"""Test search in Vorgabe title"""
|
||||||
|
response = self.client.post('/search/', {'q': 'Titel'})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, 'TEST-001')
|
||||||
|
|
||||||
|
def test_search_in_geltungsbereich(self):
|
||||||
|
"""Test search in Geltungsbereich content"""
|
||||||
|
response = self.client.post('/search/', {'q': 'Geltungsbereich'})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, 'Standards mit')
|
||||||
|
|
||||||
|
def test_search_no_results(self):
|
||||||
|
"""Test search with no results"""
|
||||||
|
response = self.client.post('/search/', {'q': 'NichtVorhanden'})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, 'Keine Resultate für "NichtVorhanden"')
|
||||||
|
|
||||||
|
def test_search_expired_vorgabe_not_included(self):
|
||||||
|
"""Test that expired Vorgaben are not included in results"""
|
||||||
|
# Create expired Vorgabe
|
||||||
|
expired_vorgabe = Vorgabe.objects.create(
|
||||||
|
order=2,
|
||||||
|
nummer=2,
|
||||||
|
dokument=self.dokument,
|
||||||
|
thema=self.thema,
|
||||||
|
titel="Abgelaufene Vorgabe",
|
||||||
|
gueltigkeit_von=date.today() - timedelta(days=10),
|
||||||
|
gueltigkeit_bis=date.today() - timedelta(days=1)
|
||||||
|
)
|
||||||
|
|
||||||
|
VorgabeKurztext.objects.create(
|
||||||
|
abschnitt=expired_vorgabe,
|
||||||
|
inhalt="Abgelaufener Inhalt mit Test"
|
||||||
|
)
|
||||||
|
|
||||||
|
response = self.client.post('/search/', {'q': 'Test'})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
# Should only find the active Vorgabe, not the expired one
|
||||||
|
self.assertContains(response, 'Test Vorgabe Titel')
|
||||||
|
# The expired vorgabe should not appear in results
|
||||||
|
self.assertNotContains(response, 'Abgelaufene Vorgabe')
|
||||||
|
|
||||||
|
def test_search_empty_term_validation(self):
|
||||||
|
"""Test validation for empty search term"""
|
||||||
|
response = self.client.post('/search/', {'q': ''})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, 'Fehler:')
|
||||||
|
self.assertContains(response, 'Suchbegriff darf nicht leer sein')
|
||||||
|
|
||||||
|
def test_search_no_term_validation(self):
|
||||||
|
"""Test validation when no search term is provided"""
|
||||||
|
response = self.client.post('/search/', {})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, 'Fehler:')
|
||||||
|
self.assertContains(response, 'Suchbegriff darf nicht leer sein')
|
||||||
|
|
||||||
|
def test_search_html_tags_stripped(self):
|
||||||
|
"""Test that HTML tags are stripped from search input"""
|
||||||
|
response = self.client.post('/search/', {'q': '<script>alert("xss")</script>Test'})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
# Should search for "alert('xss')Test" after HTML tag removal
|
||||||
|
self.assertContains(response, 'Suchresultate für alert("xss")Test')
|
||||||
|
|
||||||
|
def test_search_invalid_characters_validation(self):
|
||||||
|
"""Test validation for invalid characters"""
|
||||||
|
response = self.client.post('/search/', {'q': 'Test| DROP TABLE users'})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, 'Fehler:')
|
||||||
|
self.assertContains(response, 'Ungültige Zeichen im Suchbegriff')
|
||||||
|
|
||||||
|
def test_search_too_long_validation(self):
|
||||||
|
"""Test validation for overly long search terms"""
|
||||||
|
long_term = 'a' * 201 # 201 characters, exceeds limit of 200
|
||||||
|
response = self.client.post('/search/', {'q': long_term})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, 'Fehler:')
|
||||||
|
self.assertContains(response, 'Suchbegriff ist zu lang')
|
||||||
|
|
||||||
|
def test_search_max_length_allowed(self):
|
||||||
|
"""Test that exactly 200 characters are allowed"""
|
||||||
|
max_term = 'a' * 200 # Exactly 200 characters
|
||||||
|
response = self.client.post('/search/', {'q': max_term})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
# Should not show validation error
|
||||||
|
self.assertNotContains(response, 'Fehler:')
|
||||||
|
|
||||||
|
def test_search_german_umlauts_allowed(self):
|
||||||
|
"""Test that German umlauts are allowed in search"""
|
||||||
|
response = self.client.post('/search/', {'q': 'Test Müller äöü ÄÖÜ ß'})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
# Should not show validation error
|
||||||
|
self.assertNotContains(response, 'Fehler:')
|
||||||
|
|
||||||
|
def test_search_special_characters_allowed(self):
|
||||||
|
"""Test that allowed special characters work"""
|
||||||
|
response = self.client.post('/search/', {'q': 'Test-Test, Test: Test; Test! Test? (Test) [Test] {Test} "Test" \'Test\''})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
# Should not show validation error
|
||||||
|
self.assertNotContains(response, 'Fehler:')
|
||||||
|
|
||||||
|
def test_search_input_preserved_on_error(self):
|
||||||
|
"""Test that search input is preserved on validation errors"""
|
||||||
|
response = self.client.post('/search/', {'q': '<script>Test</script>'})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
# The input should be preserved (escaped) in the form
|
||||||
|
# Since HTML tags are stripped, we expect "Test" to be searched
|
||||||
|
self.assertContains(response, 'Suchresultate für Test')
|
||||||
|
|
||||||
|
def test_search_xss_prevention_in_results(self):
|
||||||
|
"""Test that search terms are escaped in results to prevent XSS"""
|
||||||
|
# Create content with potential XSS
|
||||||
|
self.kurztext.inhalt = "Content with <script>alert('xss')</script> term"
|
||||||
|
self.kurztext.save()
|
||||||
|
|
||||||
|
response = self.client.post('/search/', {'q': 'term'})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
# The script tag should be escaped in the output
|
||||||
|
# Note: This depends on how the template renders the content
|
||||||
|
self.assertContains(response, 'Suchresultate für term')
|
||||||
|
|
||||||
|
@patch('pages.views.pprint.pp')
|
||||||
|
def test_search_result_logging(self, mock_pprint):
|
||||||
|
"""Test that search results are logged for debugging"""
|
||||||
|
response = self.client.post('/search/', {'q': 'Test'})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
# Verify that pprint.pp was called with the result
|
||||||
|
mock_pprint.assert_called_once()
|
||||||
|
|
||||||
|
def test_search_multiple_documents(self):
|
||||||
|
"""Test search across multiple documents"""
|
||||||
|
# Create second document
|
||||||
|
dokument2 = Dokument.objects.create(
|
||||||
|
nummer="TEST-002",
|
||||||
|
dokumententyp=self.dokumententyp,
|
||||||
|
name="Zweites Test Dokument",
|
||||||
|
gueltigkeit_von=date.today(),
|
||||||
|
aktiv=True
|
||||||
|
)
|
||||||
|
|
||||||
|
vorgabe2 = Vorgabe.objects.create(
|
||||||
|
order=1,
|
||||||
|
nummer=1,
|
||||||
|
dokument=dokument2,
|
||||||
|
thema=self.thema,
|
||||||
|
titel="Zweite Test Vorgabe",
|
||||||
|
gueltigkeit_von=date.today()
|
||||||
|
)
|
||||||
|
|
||||||
|
VorgabeKurztext.objects.create(
|
||||||
|
abschnitt=vorgabe2,
|
||||||
|
inhalt="Zweiter Test Inhalt"
|
||||||
|
)
|
||||||
|
|
||||||
|
response = self.client.post('/search/', {'q': 'Test'})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
# Should find results from both documents
|
||||||
|
self.assertContains(response, 'TEST-001')
|
||||||
|
self.assertContains(response, 'TEST-002')
|
||||||
|
|
||||||
|
|
||||||
|
class SearchValidationTest(TestCase):
|
||||||
|
"""Test the validate_search_input function directly"""
|
||||||
|
|
||||||
|
def test_validate_search_input_valid(self):
|
||||||
|
"""Test valid search input"""
|
||||||
|
from pages.views import validate_search_input
|
||||||
|
|
||||||
|
result = validate_search_input("Test Suchbegriff")
|
||||||
|
self.assertEqual(result, "Test Suchbegriff")
|
||||||
|
|
||||||
|
def test_validate_search_input_empty(self):
|
||||||
|
"""Test empty search input"""
|
||||||
|
from pages.views import validate_search_input
|
||||||
|
|
||||||
|
with self.assertRaises(ValidationError) as context:
|
||||||
|
validate_search_input("")
|
||||||
|
|
||||||
|
self.assertIn("Suchbegriff darf nicht leer sein", str(context.exception))
|
||||||
|
|
||||||
|
def test_validate_search_input_html_stripped(self):
|
||||||
|
"""Test that HTML tags are stripped"""
|
||||||
|
from pages.views import validate_search_input
|
||||||
|
|
||||||
|
result = validate_search_input("<script>alert('xss')</script>Test")
|
||||||
|
self.assertEqual(result, "alert('xss')Test")
|
||||||
|
|
||||||
|
def test_validate_search_input_invalid_chars(self):
|
||||||
|
"""Test validation of invalid characters"""
|
||||||
|
from pages.views import validate_search_input
|
||||||
|
|
||||||
|
with self.assertRaises(ValidationError) as context:
|
||||||
|
validate_search_input("Test| DROP TABLE users")
|
||||||
|
|
||||||
|
self.assertIn("Ungültige Zeichen im Suchbegriff", str(context.exception))
|
||||||
|
|
||||||
|
def test_validate_search_input_too_long(self):
|
||||||
|
"""Test length validation"""
|
||||||
|
from pages.views import validate_search_input
|
||||||
|
|
||||||
|
with self.assertRaises(ValidationError) as context:
|
||||||
|
validate_search_input("a" * 201)
|
||||||
|
|
||||||
|
self.assertIn("Suchbegriff ist zu lang", str(context.exception))
|
||||||
|
|
||||||
|
def test_validate_search_input_whitespace_stripped(self):
|
||||||
|
"""Test that whitespace is stripped"""
|
||||||
|
from pages.views import validate_search_input
|
||||||
|
|
||||||
|
result = validate_search_input(" Test Suchbegriff ")
|
||||||
|
self.assertEqual(result, "Test Suchbegriff")
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.utils.html import escape
|
||||||
|
import re
|
||||||
from abschnitte.utils import render_textabschnitte
|
from abschnitte.utils import render_textabschnitte
|
||||||
from dokumente.models import Dokument, VorgabeLangtext, VorgabeKurztext, Geltungsbereich
|
from dokumente.models import Dokument, VorgabeLangtext, VorgabeKurztext, Geltungsbereich, Vorgabe
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
import datetime
|
import datetime
|
||||||
import pprint
|
import pprint
|
||||||
@@ -9,23 +12,60 @@ def startseite(request):
|
|||||||
standards=list(Dokument.objects.filter(aktiv=True))
|
standards=list(Dokument.objects.filter(aktiv=True))
|
||||||
return render(request, 'startseite.html', {"dokumente":standards,})
|
return render(request, 'startseite.html', {"dokumente":standards,})
|
||||||
|
|
||||||
|
def validate_search_input(search_term):
|
||||||
|
"""
|
||||||
|
Validate search input to prevent SQL injection and XSS
|
||||||
|
"""
|
||||||
|
if not search_term:
|
||||||
|
raise ValidationError("Suchbegriff darf nicht leer sein")
|
||||||
|
|
||||||
|
# Remove any HTML tags to prevent XSS
|
||||||
|
search_term = re.sub(r'<[^>]*>', '', search_term)
|
||||||
|
|
||||||
|
# Allow only alphanumeric characters, spaces, and basic punctuation
|
||||||
|
# This prevents SQL injection and other malicious input while allowing useful characters
|
||||||
|
if not re.match(r'^[a-zA-Z0-9äöüÄÖÜß\s\-\.\,\:\;\!\?\(\)\[\]\{\}\"\']+$', search_term):
|
||||||
|
raise ValidationError("Ungültige Zeichen im Suchbegriff")
|
||||||
|
|
||||||
|
# Limit length to prevent DoS attacks
|
||||||
|
if len(search_term) > 200:
|
||||||
|
raise ValidationError("Suchbegriff ist zu lang")
|
||||||
|
|
||||||
|
return search_term.strip()
|
||||||
|
|
||||||
def search(request):
|
def search(request):
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
return render(request, 'search.html')
|
return render(request, 'search.html')
|
||||||
elif request.method == "POST":
|
elif request.method == "POST":
|
||||||
suchbegriff=request.POST.get("q")
|
raw_search_term = request.POST.get("q", "")
|
||||||
|
|
||||||
|
try:
|
||||||
|
suchbegriff = validate_search_input(raw_search_term)
|
||||||
|
except ValidationError as e:
|
||||||
|
return render(request, 'search.html', {
|
||||||
|
'error_message': str(e),
|
||||||
|
'search_term': escape(raw_search_term)
|
||||||
|
})
|
||||||
|
|
||||||
|
# Escape the search term for display in templates
|
||||||
|
safe_search_term = escape(suchbegriff)
|
||||||
result= {"all": {}}
|
result= {"all": {}}
|
||||||
qs = VorgabeKurztext.objects.filter(inhalt__contains=suchbegriff).exclude(abschnitt__gueltigkeit_bis__lt=datetime.date.today())
|
qs = VorgabeKurztext.objects.filter(inhalt__icontains=suchbegriff).exclude(abschnitt__gueltigkeit_bis__lt=datetime.date.today())
|
||||||
result["kurztext"] = {k: [o.abschnitt for o in g] for k, g in groupby(qs, key=lambda o: o.abschnitt.dokument)}
|
result["kurztext"] = {k: [o.abschnitt for o in g] for k, g in groupby(qs, key=lambda o: o.abschnitt.dokument)}
|
||||||
qs = VorgabeLangtext.objects.filter(inhalt__contains=suchbegriff).exclude(abschnitt__gueltigkeit_bis__lt=datetime.date.today())
|
qs = VorgabeLangtext.objects.filter(inhalt__icontains=suchbegriff).exclude(abschnitt__gueltigkeit_bis__lt=datetime.date.today())
|
||||||
result['langtext']= {k: [o.abschnitt for o in g] for k, g in groupby(qs, key=lambda o: o.abschnitt.dokument)}
|
result['langtext']= {k: [o.abschnitt for o in g] for k, g in groupby(qs, key=lambda o: o.abschnitt.dokument)}
|
||||||
|
qs = Vorgabe.objects.filter(titel__icontains=suchbegriff).exclude(gueltigkeit_bis__lt=datetime.date.today())
|
||||||
|
result['titel']= {k: list(g) for k, g in groupby(qs, key=lambda o: o.dokument)}
|
||||||
for r in result.keys():
|
for r in result.keys():
|
||||||
for s in result[r].keys():
|
for s in result[r].keys():
|
||||||
result["all"][s] = set(result[r][s])
|
if r == 'titel':
|
||||||
|
result["all"][s] = set(result["all"].get(s, set()) | set(result[r][s]))
|
||||||
|
else:
|
||||||
|
result["all"][s] = set(result["all"].get(s, set()) | set(result[r][s]))
|
||||||
result["geltungsbereich"]={}
|
result["geltungsbereich"]={}
|
||||||
geltungsbereich=set(list([x.geltungsbereich for x in Geltungsbereich.objects.filter(inhalt__contains=suchbegriff)]))
|
geltungsbereich=set(list([x.geltungsbereich for x in Geltungsbereich.objects.filter(inhalt__icontains=suchbegriff)]))
|
||||||
for s in geltungsbereich:
|
for s in geltungsbereich:
|
||||||
result["geltungsbereich"][s]=render_textabschnitte(s.geltungsbereich_set.order_by("order"))
|
result["geltungsbereich"][s]=render_textabschnitte(s.geltungsbereich_set.order_by("order"))
|
||||||
pprint.pp (result)
|
pprint.pp (result)
|
||||||
return render(request,"results.html",{"suchbegriff":suchbegriff,"resultat":result})
|
return render(request,"results.html",{"suchbegriff":safe_search_term,"resultat":result})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user