Django景点印象服务系统是一个专为旅游爱好者打造的Web应用平台。作为一名有5年Django开发经验的工程师,我在实际开发中发现旅游信息碎片化问题严重,游客很难获取真实可靠的景点评价。这个系统正是为了解决这一痛点而生。
系统采用Python+Django技术栈构建,整合了景点信息管理、用户互动社区、智能推荐算法等核心功能。相比市面上常见的旅游平台,我们的特色在于:
选择Django框架主要基于三个实际考量:
数据库选用MySQL 5.7+版本,配置建议:
python复制DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'scenic_spot',
'USER': 'admin',
'PASSWORD': 'ComplexPwd@123',
'HOST': '127.0.0.1',
'PORT': '3306',
'OPTIONS': {
'charset': 'utf8mb4' # 支持emoji存储
}
}
}
采用Bootstrap 5 + Vue.js 3的组合主要解决:
实测数据显示:
采用Django内置的authenticate系统扩展开发:
python复制# 自定义用户模型
class User(AbstractUser):
avatar = models.ImageField(upload_to='avatars/')
bio = models.TextField(max_length=500, blank=True)
# 登录逻辑优化
def custom_login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
# 记录登录设备信息
DeviceInfo.objects.create(user=user, device=request.META['HTTP_USER_AGENT'])
return JsonResponse({'status': 'success'})
return JsonResponse({'status': 'error'}, status=400)
关键点:增加设备信息记录可以有效防止盗号行为,同时为多端同步提供基础
核心数据结构设计:
python复制class ScenicSpot(models.Model):
name = models.CharField(max_length=100)
location = models.PointField() # 使用GeoDjango
ticket_price = models.DecimalField(max_digits=8, decimal_places=2)
class Impression(models.Model):
RATING_CHOICES = [(i, str(i)) for i in range(1, 6)]
spot = models.ForeignKey(ScenicSpot, on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField()
rating = models.PositiveSmallIntegerField(choices=RATING_CHOICES)
created_at = models.DateTimeField(auto_now_add=True)
tags = TaggableManager() # django-taggit
上传处理逻辑要点:
基于用户的协同过滤算法核心代码:
python复制def recommend_spots(user):
# 获取用户历史行为
user_impressions = Impression.objects.filter(author=user)
# 计算用户相似度矩阵
similarity_matrix = defaultdict(dict)
all_users = User.objects.exclude(id=user.id)
for other in all_users:
common_spots = set(i.spot for i in user_impressions) & \
set(i.spot for i in Impression.objects.filter(author=other))
if common_spots:
# 使用余弦相似度计算
vec1 = [i.rating for i in user_impressions if i.spot in common_spots]
vec2 = [i.rating for i in Impression.objects.filter(author=other, spot__in=common_spots)]
similarity = cosine_similarity([vec1], [vec2])[0][0]
similarity_matrix[other.id] = similarity
# 获取Top3相似用户
similar_users = sorted(similarity_matrix.items(), key=lambda x: x[1], reverse=True)[:3]
# 生成推荐列表
recommendations = []
for user_id, _ in similar_users:
spots = Impression.objects.filter(author_id=user_id)\
.exclude(spot__in=[i.spot for i in user_impressions])\
.values('spot').annotate(avg_rating=Avg('rating'))\
.order_by('-avg_rating')[:5]
recommendations.extend(spots)
return sorted(recommendations, key=lambda x: x['avg_rating'], reverse=True)[:10]
对于新用户采用混合推荐策略:
python复制class Impression(models.Model):
class Meta:
indexes = [
models.Index(fields=['spot', 'created_at']),
models.Index(fields=['author', 'created_at'])
]
采用Redis三级缓存:
配置示例:
python复制CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"SOCKET_CONNECT_TIMEOUT": 5, # 秒
"SOCKET_TIMEOUT": 5, # 秒
}
}
}
推荐服务器规格:
使用Docker Compose部署:
yaml复制version: '3'
services:
web:
build: .
command: gunicorn core.wsgi:application --bind 0.0.0.0:8000
volumes:
- static_data:/app/static
depends_on:
- redis
- db
db:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: yourpassword
MYSQL_DATABASE: scenic_spot
volumes:
- db_data:/var/lib/mysql
redis:
image: redis:alpine
volumes:
db_data:
static_data:
初期直接存储原图导致:
解决方案:
python复制from PIL import Image
from io import BytesIO
def compress_image(image_file):
img = Image.open(image_file)
if img.mode != 'RGB':
img = img.convert('RGB')
img.thumbnail((2000, 2000)) # 长边不超过2000px
output = BytesIO()
img.save(output, format='JPEG', quality=75, optimize=True)
output.seek(0)
return ContentFile(output.read(), name=image_file.name)
高峰时段出现点赞计数不一致,最终采用:
python复制from django.db import transaction
@transaction.atomic
def like_impression(request, impression_id):
impression = Impression.objects.select_for_update().get(id=impression_id)
impression.like_count += 1
impression.save()
这个项目从技术选型到功能实现都经过精心设计,特别是在处理用户生成内容方面积累了许多实战经验。对于想要学习Django全栈开发的同学,建议从用户认证模块开始逐步深入,理解Django的MTV架构精髓。