mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 13:51:13 -05:00
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:
@@ -12,7 +12,7 @@ from unfold.decorators import display
|
||||
from import_export import resources
|
||||
from import_export.admin import ImportExportModelAdmin
|
||||
|
||||
from .models import User, UserRole, UserProfile
|
||||
from .models import User, UserRole, UserProfile, UserRideCredit, UserTopList, UserTopListItem
|
||||
|
||||
|
||||
class UserResource(resources.ModelResource):
|
||||
@@ -370,3 +370,215 @@ class UserProfileAdmin(ModelAdmin):
|
||||
"""Optimize queryset."""
|
||||
qs = super().get_queryset(request)
|
||||
return qs.select_related('user')
|
||||
|
||||
|
||||
@admin.register(UserRideCredit)
|
||||
class UserRideCreditAdmin(ModelAdmin):
|
||||
"""Admin interface for UserRideCredit model."""
|
||||
|
||||
list_display = [
|
||||
'user_link',
|
||||
'ride_link',
|
||||
'park_link',
|
||||
'first_ride_date',
|
||||
'ride_count',
|
||||
'created',
|
||||
]
|
||||
|
||||
list_filter = [
|
||||
'first_ride_date',
|
||||
'created',
|
||||
]
|
||||
|
||||
search_fields = [
|
||||
'user__email',
|
||||
'user__username',
|
||||
'ride__name',
|
||||
'notes',
|
||||
]
|
||||
|
||||
ordering = ['-first_ride_date', '-created']
|
||||
|
||||
readonly_fields = ['created', 'modified']
|
||||
|
||||
fieldsets = (
|
||||
('Credit Information', {
|
||||
'fields': ('user', 'ride', 'first_ride_date', 'ride_count')
|
||||
}),
|
||||
('Notes', {
|
||||
'fields': ('notes',)
|
||||
}),
|
||||
('Timestamps', {
|
||||
'fields': ('created', 'modified'),
|
||||
'classes': ('collapse',)
|
||||
}),
|
||||
)
|
||||
|
||||
@display(description='User', ordering='user__username')
|
||||
def user_link(self, obj):
|
||||
url = reverse('admin:users_user_change', args=[obj.user.pk])
|
||||
return format_html('<a href="{}">{}</a>', url, obj.user.username)
|
||||
|
||||
@display(description='Ride', ordering='ride__name')
|
||||
def ride_link(self, obj):
|
||||
url = reverse('admin:entities_ride_change', args=[obj.ride.pk])
|
||||
return format_html('<a href="{}">{}</a>', url, obj.ride.name)
|
||||
|
||||
@display(description='Park')
|
||||
def park_link(self, obj):
|
||||
if obj.ride.park:
|
||||
url = reverse('admin:entities_park_change', args=[obj.ride.park.pk])
|
||||
return format_html('<a href="{}">{}</a>', url, obj.ride.park.name)
|
||||
return '-'
|
||||
|
||||
def get_queryset(self, request):
|
||||
"""Optimize queryset."""
|
||||
qs = super().get_queryset(request)
|
||||
return qs.select_related('user', 'ride', 'ride__park')
|
||||
|
||||
|
||||
class UserTopListItemInline(admin.TabularInline):
|
||||
"""Inline for top list items."""
|
||||
model = UserTopListItem
|
||||
extra = 1
|
||||
fields = ('position', 'content_type', 'object_id', 'notes')
|
||||
ordering = ['position']
|
||||
|
||||
|
||||
@admin.register(UserTopList)
|
||||
class UserTopListAdmin(ModelAdmin):
|
||||
"""Admin interface for UserTopList model."""
|
||||
|
||||
list_display = [
|
||||
'title',
|
||||
'user_link',
|
||||
'list_type',
|
||||
'item_count_display',
|
||||
'visibility_badge',
|
||||
'created',
|
||||
]
|
||||
|
||||
list_filter = [
|
||||
'list_type',
|
||||
'is_public',
|
||||
'created',
|
||||
]
|
||||
|
||||
search_fields = [
|
||||
'title',
|
||||
'description',
|
||||
'user__email',
|
||||
'user__username',
|
||||
]
|
||||
|
||||
ordering = ['-created']
|
||||
|
||||
readonly_fields = ['created', 'modified', 'item_count']
|
||||
|
||||
fieldsets = (
|
||||
('List Information', {
|
||||
'fields': ('user', 'list_type', 'title', 'description')
|
||||
}),
|
||||
('Privacy', {
|
||||
'fields': ('is_public',)
|
||||
}),
|
||||
('Statistics', {
|
||||
'fields': ('item_count',)
|
||||
}),
|
||||
('Timestamps', {
|
||||
'fields': ('created', 'modified'),
|
||||
'classes': ('collapse',)
|
||||
}),
|
||||
)
|
||||
|
||||
inlines = [UserTopListItemInline]
|
||||
|
||||
@display(description='User', ordering='user__username')
|
||||
def user_link(self, obj):
|
||||
url = reverse('admin:users_user_change', args=[obj.user.pk])
|
||||
return format_html('<a href="{}">{}</a>', url, obj.user.username)
|
||||
|
||||
@display(description='Items', ordering='items__count')
|
||||
def item_count_display(self, obj):
|
||||
count = obj.item_count
|
||||
return format_html('<span style="font-weight: bold;">{}</span>', count)
|
||||
|
||||
@display(description='Visibility', ordering='is_public')
|
||||
def visibility_badge(self, obj):
|
||||
if obj.is_public:
|
||||
return format_html(
|
||||
'<span style="background-color: green; color: white; padding: 3px 8px; '
|
||||
'border-radius: 3px; font-size: 11px;">PUBLIC</span>'
|
||||
)
|
||||
else:
|
||||
return format_html(
|
||||
'<span style="background-color: gray; color: white; padding: 3px 8px; '
|
||||
'border-radius: 3px; font-size: 11px;">PRIVATE</span>'
|
||||
)
|
||||
|
||||
def get_queryset(self, request):
|
||||
"""Optimize queryset."""
|
||||
qs = super().get_queryset(request)
|
||||
return qs.select_related('user').prefetch_related('items')
|
||||
|
||||
|
||||
@admin.register(UserTopListItem)
|
||||
class UserTopListItemAdmin(ModelAdmin):
|
||||
"""Admin interface for UserTopListItem model."""
|
||||
|
||||
list_display = [
|
||||
'position',
|
||||
'list_link',
|
||||
'entity_type',
|
||||
'entity_link',
|
||||
'created',
|
||||
]
|
||||
|
||||
list_filter = [
|
||||
'content_type',
|
||||
'created',
|
||||
]
|
||||
|
||||
search_fields = [
|
||||
'top_list__title',
|
||||
'notes',
|
||||
]
|
||||
|
||||
ordering = ['top_list', 'position']
|
||||
|
||||
readonly_fields = ['created', 'modified']
|
||||
|
||||
fieldsets = (
|
||||
('Item Information', {
|
||||
'fields': ('top_list', 'position', 'content_type', 'object_id')
|
||||
}),
|
||||
('Notes', {
|
||||
'fields': ('notes',)
|
||||
}),
|
||||
('Timestamps', {
|
||||
'fields': ('created', 'modified'),
|
||||
'classes': ('collapse',)
|
||||
}),
|
||||
)
|
||||
|
||||
@display(description='List', ordering='top_list__title')
|
||||
def list_link(self, obj):
|
||||
url = reverse('admin:users_usertoplist_change', args=[obj.top_list.pk])
|
||||
return format_html('<a href="{}">{}</a>', url, obj.top_list.title)
|
||||
|
||||
@display(description='Type', ordering='content_type')
|
||||
def entity_type(self, obj):
|
||||
return obj.content_type.model.title()
|
||||
|
||||
@display(description='Entity')
|
||||
def entity_link(self, obj):
|
||||
if obj.content_object:
|
||||
model_name = obj.content_type.model
|
||||
url = reverse(f'admin:entities_{model_name}_change', args=[obj.object_id])
|
||||
return format_html('<a href="{}">{}</a>', url, str(obj.content_object))
|
||||
return f"ID: {obj.object_id}"
|
||||
|
||||
def get_queryset(self, request):
|
||||
"""Optimize queryset."""
|
||||
qs = super().get_queryset(request)
|
||||
return qs.select_related('top_list', 'content_type')
|
||||
|
||||
Reference in New Issue
Block a user