Implement reviews and voting system

- Added Review model with fields for user, content type, title, content, rating, visit metadata, helpful votes, moderation status, and timestamps.
- Created ReviewHelpfulVote model to track user votes on reviews.
- Implemented moderation workflow for reviews with approve and reject methods.
- Developed admin interface for managing reviews and helpful votes, including custom display methods and actions for bulk approval/rejection.
- Added migrations for the new models and their relationships.
- Ensured unique constraints and indexes for efficient querying.
This commit is contained in:
pacnpal
2025-11-08 15:50:43 -05:00
parent d6ff4cc3a3
commit 00985eac8d
17 changed files with 1726 additions and 1 deletions

View File

@@ -0,0 +1,265 @@
# Generated by Django 4.2.8 on 2025-11-08 20:46
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import django_lifecycle.mixins
import model_utils.fields
import uuid
class Migration(migrations.Migration):
dependencies = [
("contenttypes", "0002_remove_content_type_name"),
("entities", "0003_add_search_vector_gin_indexes"),
("users", "0001_initial"),
]
operations = [
migrations.CreateModel(
name="UserTopList",
fields=[
(
"created",
model_utils.fields.AutoCreatedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="created",
),
),
(
"modified",
model_utils.fields.AutoLastModifiedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="modified",
),
),
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
(
"list_type",
models.CharField(
choices=[
("parks", "Parks"),
("rides", "Rides"),
("coasters", "Coasters"),
],
db_index=True,
help_text="Type of entities in this list",
max_length=20,
),
),
(
"title",
models.CharField(help_text="Title of the list", max_length=200),
),
(
"description",
models.TextField(blank=True, help_text="Description of the list"),
),
(
"is_public",
models.BooleanField(
db_index=True,
default=True,
help_text="Whether this list is publicly visible",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="top_lists",
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"db_table": "user_top_lists",
"ordering": ["-created"],
},
bases=(django_lifecycle.mixins.LifecycleModelMixin, models.Model),
),
migrations.CreateModel(
name="UserRideCredit",
fields=[
(
"created",
model_utils.fields.AutoCreatedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="created",
),
),
(
"modified",
model_utils.fields.AutoLastModifiedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="modified",
),
),
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
(
"first_ride_date",
models.DateField(
blank=True, help_text="Date of first ride", null=True
),
),
(
"ride_count",
models.PositiveIntegerField(
default=1, help_text="Number of times user has ridden this ride"
),
),
(
"notes",
models.TextField(
blank=True, help_text="User notes about this ride"
),
),
(
"ride",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="user_credits",
to="entities.ride",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="ride_credits",
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"db_table": "user_ride_credits",
"ordering": ["-first_ride_date", "-created"],
},
bases=(django_lifecycle.mixins.LifecycleModelMixin, models.Model),
),
migrations.CreateModel(
name="UserTopListItem",
fields=[
(
"created",
model_utils.fields.AutoCreatedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="created",
),
),
(
"modified",
model_utils.fields.AutoLastModifiedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="modified",
),
),
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
("object_id", models.PositiveIntegerField()),
(
"position",
models.PositiveIntegerField(
help_text="Position in the list (1 = top)"
),
),
(
"notes",
models.TextField(
blank=True,
help_text="User notes about why this item is ranked here",
),
),
(
"content_type",
models.ForeignKey(
limit_choices_to={"model__in": ("park", "ride")},
on_delete=django.db.models.deletion.CASCADE,
to="contenttypes.contenttype",
),
),
(
"top_list",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="items",
to="users.usertoplist",
),
),
],
options={
"db_table": "user_top_list_items",
"ordering": ["position"],
"indexes": [
models.Index(
fields=["top_list", "position"],
name="user_top_li_top_lis_d31db9_idx",
),
models.Index(
fields=["content_type", "object_id"],
name="user_top_li_content_889eb7_idx",
),
],
"unique_together": {("top_list", "position")},
},
bases=(django_lifecycle.mixins.LifecycleModelMixin, models.Model),
),
migrations.AddIndex(
model_name="usertoplist",
index=models.Index(
fields=["user", "list_type"], name="user_top_li_user_id_63f56d_idx"
),
),
migrations.AddIndex(
model_name="usertoplist",
index=models.Index(
fields=["is_public", "created"], name="user_top_li_is_publ_983146_idx"
),
),
migrations.AddIndex(
model_name="userridecredit",
index=models.Index(
fields=["user", "first_ride_date"],
name="user_ride_c_user_id_56a0e5_idx",
),
),
migrations.AddIndex(
model_name="userridecredit",
index=models.Index(fields=["ride"], name="user_ride_c_ride_id_f0990b_idx"),
),
migrations.AlterUniqueTogether(
name="userridecredit",
unique_together={("user", "ride")},
),
]