• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rafalp / Misago / 17496798327

05 Sep 2025 02:59PM UTC coverage: 96.509% (-0.3%) from 96.769%
17496798327

Pull #1995

github

web-flow
Merge 17284d7d7 into 6bb938c90
Pull Request #1995: Move `Post` model from `misago.threads` to `misago.posts`

1884 of 1974 new or added lines in 149 files covered. (95.44%)

305 existing lines in 16 files now uncovered.

66716 of 69129 relevant lines covered (96.51%)

0.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.11
/misago/posts/models/post.py
1
import copy
1✔
2
import hashlib
1✔
3
from typing import TYPE_CHECKING
1✔
4

5
from django.contrib.postgres.indexes import GinIndex
1✔
6
from django.contrib.postgres.search import SearchVector, SearchVectorField
1✔
7
from django.db import models
1✔
8
from django.db.models import Q
1✔
9
from django.urls import reverse
1✔
10

11
from ...conf import settings
1✔
12
from ...core.utils import parse_iso8601_string
1✔
13
from ...plugins.models import PluginDataModel
1✔
14

15
if TYPE_CHECKING:
1✔
NEW
16
    from ...threads.models import Thread
×
17

18

19
class Post(PluginDataModel):
1✔
20
    category = models.ForeignKey("misago_categories.Category", on_delete=models.CASCADE)
1✔
21
    thread = models.ForeignKey("misago_threads.Thread", on_delete=models.CASCADE)
1✔
22
    poster = models.ForeignKey(
1✔
23
        settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.SET_NULL
24
    )
25
    poster_name = models.CharField(max_length=255)
1✔
26

27
    original = models.TextField()
1✔
28
    parsed = models.TextField()
1✔
29
    metadata = models.JSONField(default=dict)
1✔
30

31
    posted_at = models.DateTimeField(db_index=True)
1✔
32
    updated_at = models.DateTimeField(null=True, blank=True)
1✔
33
    hidden_at = models.DateTimeField(null=True, blank=True)
1✔
34

35
    edits = models.PositiveIntegerField(default=0)
1✔
36
    last_editor = models.ForeignKey(
1✔
37
        settings.AUTH_USER_MODEL,
38
        blank=True,
39
        null=True,
40
        on_delete=models.SET_NULL,
41
        related_name="+",
42
    )
43
    last_editor_name = models.CharField(max_length=255, null=True, blank=True)
1✔
44
    last_editor_slug = models.SlugField(max_length=255, null=True, blank=True)
1✔
45

46
    hidden_by = models.ForeignKey(
1✔
47
        settings.AUTH_USER_MODEL,
48
        blank=True,
49
        null=True,
50
        on_delete=models.SET_NULL,
51
        related_name="+",
52
    )
53
    hidden_by_name = models.CharField(max_length=255, null=True, blank=True)
1✔
54
    hidden_by_slug = models.SlugField(max_length=255, null=True, blank=True)
1✔
55

56
    has_reports = models.BooleanField(default=False)
1✔
57
    has_open_reports = models.BooleanField(default=False)
1✔
58

59
    is_unapproved = models.BooleanField(default=False, db_index=True)
1✔
60
    is_hidden = models.BooleanField(default=False)
1✔
61
    is_protected = models.BooleanField(default=False)
1✔
62

63
    search_document = models.TextField(null=True, blank=True)
1✔
64
    search_vector = SearchVectorField()
1✔
65

66
    class Meta:
1✔
67
        indexes = [
1✔
68
            *PluginDataModel.Meta.indexes,
69
            models.Index(
70
                name="misago_post_has_open_repo_part",
71
                fields=["has_open_reports"],
72
                condition=Q(has_open_reports=True),
73
            ),
74
            models.Index(
75
                name="misago_post_is_hidden_part",
76
                fields=["is_hidden"],
77
                condition=Q(is_hidden=False),
78
            ),
79
            GinIndex(fields=["search_vector"]),
80
            # Speed up threadview for team members
81
            models.Index(fields=["thread", "id"]),
82
            models.Index(fields=["poster", "posted_at"]),
83
        ]
84

85
    def __str__(self):
1✔
86
        return "%s..." % self.original[10:].strip()
1✔
87

88
    @property
1✔
89
    def sha256_checksum(self) -> str:
1✔
90
        return hashlib.sha256(
1✔
91
            f"{self.id}:{self.updated_at or "n"}:{self.parsed}".encode()
92
        ).hexdigest()
93

94
    def set_search_document(self, thread: "Thread", search_document: str):
1✔
95
        if self.id == thread.first_post_id:
1✔
96
            self.search_document = f"{thread.title}\n\n{search_document}"
1✔
97
        else:
98
            self.search_document = search_document
1✔
99

100
    def set_search_vector(self):
1✔
101
        self.search_vector = SearchVector(
1✔
102
            "search_document", config=settings.MISAGO_SEARCH_CONFIG
103
        )
104

105
    def get_absolute_url(self):
1✔
106
        return reverse("misago:post", kwargs={"id": self.id})
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc