Python校园外卖推荐系统(协同过滤算法)

部署运行你感兴趣的模型镜像

项目简介:

Hello 今天和同学们分享一个Python推荐系统的项目,校园外卖推荐系统,下面先直接介绍一下这个项目的大概实现功能和相关涉及技术吧~

📖Python校园外卖推荐系统

📖首页模块:展示全部外卖数据,用户可以点击查看外卖详情,搜索外卖,也可以通过分类标签查看外卖,以及展示用户最近购买下单过的商品.

📖 热门模块:根据用户收藏和下单的数量位指标来评选展示热门的商品,同时用户可以搜索外卖,查看最近下单过的外卖数据.

📖 推荐模块:根据用户收藏和下单以及浏览等参数作为指标依据,结合协同过滤算法个性化给用户推荐可能喜欢的商品,并支持用户搜索和分类查询.

📖 购物车模块:展示用户购物车的外卖数据,添加了哪些外卖以及数量,并展示总金额,用户再次页面进行支付.

📖 订单模块:展示用户所有下单的数据(时间,外卖,数量以及金额)

📖 今日数据:可视化出现外卖的字段数据,给用户友好清晰展示外卖的详细数据,为用户下单和选择提供友好的选择和参考~

📖 管理员模块:管理员可以对用户数据,外卖数据,用户购物车数据,用户订单数据,热门数据,推荐数据进行综合管理.

📖 涉及技术:Python,Django,MySQL,协同过滤,前端.

项目核心代码:

数据库设计:

from django.db import models
# Create your models here.
import uuid
from django.db import models
from django.contrib.auth.models import User
class UserInfo(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, verbose_name='用户',related_name='userInfo')
    class_name = models.CharField(max_length=50, verbose_name='班级')  # 增加班级字段
    grade = models.CharField(max_length=50, verbose_name='年级')  # 增加年级字段
    phone = models.CharField(max_length=11, verbose_name='电话')
    address = models.CharField(max_length=255, verbose_name='地址')

    def __str__(self):
        return self.user.username

    class Meta:
        verbose_name = '用户信息'
        verbose_name_plural = '用户信息'

class Category(models.Model):
    name = models.CharField(max_length=100, unique=True, verbose_name='分类名称')

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = '分类'
        verbose_name_plural = '分类'

class Product(models.Model):
    category = models.ForeignKey(
        Category,
        on_delete=models.CASCADE,
        related_name='products',
        verbose_name='分类'
    )
    name = models.CharField(max_length=255, verbose_name='名称')
    price = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        verbose_name='价格'
    )
    stock = models.IntegerField(verbose_name='库存')
    image = models.ImageField(
        upload_to='products/',
        blank=True,
        null=True,
        verbose_name='封面'
    )
    description = models.TextField(
        blank=True,
        null=True,
        verbose_name='描述'
    )
    view_count = models.IntegerField(verbose_name='浏览量',default=0)
    created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建日期')

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = '产品'
        verbose_name_plural = '产品'

class Favorite(models.Model):
    user = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name='favorites',
        verbose_name='用户'
    )
    product = models.ForeignKey(
        Product,
        on_delete=models.CASCADE,
        related_name='favorited_by',
        verbose_name='产品'
    )
    date_added = models.DateTimeField(auto_now_add=True, verbose_name='添加日期')

    def __str__(self):
        return f'{self.user.username} - {self.product.name}'

    class Meta:
        verbose_name = '用户收藏'
        verbose_name_plural = '用户收藏'
        unique_together = ('user', 'product')  # 确保每个用户只能收藏同一个产品一次

class CartItem(models.Model):
    user = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        verbose_name='用户'
    )
    product = models.ForeignKey(
        Product,
        on_delete=models.CASCADE,
        verbose_name='产品'
    )
    quantity = models.IntegerField(default=1, verbose_name='数量')

    def __str__(self):
        return f"{self.quantity} of {self.product.name}"

    def get_total_price(self):
        return self.quantity * self.product.price

    class Meta:
        verbose_name = '购物车项'
        verbose_name_plural = '购物车项'

#商品评分
class Product_score(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    product = models.ForeignKey('Product', on_delete=models.CASCADE)
    score = models.IntegerField('热度', null=True,default=0)

    def __str__(self):
        return f'用户 {self.user.username} 对 {self.product.name} 的评分:{self.score}'

    class Meta:
        verbose_name = '商品评分'
        verbose_name_plural = '商品评分'

# 订单
class Order(models.Model):
    # 定义订单状态的选项
    ORDER_STATUS_CHOICES = (
        ('未支付', '未支付'),
        ('已支付', '已支付'),
        ('已发货', '已发货'),
        ('已完成', '已完成'),
        ('已取消', '已取消'),
    )
    user = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        verbose_name='用户'
    )
    date_ordered = models.DateTimeField(
        auto_now_add=True,
        verbose_name='下单日期'
    )
    status = models.CharField(
        max_length=10,
        choices=ORDER_STATUS_CHOICES,
        default='未支付',
        verbose_name='订单状态'
    )

    transaction_id = models.CharField(
        max_length=100,
        default=uuid.uuid4(),
        blank=True,
        verbose_name='交易ID'
    )

    def __str__(self):
        return f"订单 {self.id} 用户 {self.user.username}"

    def get_total_cost(self):
        return sum(item.get_total_price() for item in self.orderitem.all())

    class Meta:
        verbose_name = '订单'
        verbose_name_plural = '订单'

class OrderItem(models.Model):
    order = models.ForeignKey(
        Order,
        on_delete=models.CASCADE,
        related_name='orderitem',
        verbose_name='订单'
    )
    product = models.ForeignKey(
        Product,
        on_delete=models.CASCADE,
        verbose_name='产品'
    )
    quantity = models.IntegerField(
        default=1,
        verbose_name='数量'
    )
    date_added = models.DateTimeField(
        auto_now_add=True,
        verbose_name='添加日期'
    )

    def __str__(self):
        return f"{self.quantity} of {self.product.name}"

    def get_total_price(self):
        return self.quantity * self.product.price

    class Meta:
        verbose_name = '订单项'
        verbose_name_plural = '订单项'

路由设置:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', account.login, name='login'),
    path('index/', account.index, name='index'),
    path('login/', account.login, name='login'),
    path('logout/', account.logout, name='logout'),
    path('register/', account.register, name='register'),
    re_path('media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
    path('profile/update/', account.profile_update, name='profile_update'),
    path('password/change/', account.change_password, name='password_change'),
      path('product/detail/<int:pk>/', index.product_detail, name='product_detail'),
      path('cart/', index.cart, name='cart'),
      path('order/success/<int:order_id>/',index.order_success, name='order_success'),
      path('order/fail/',index.order_fail, name='order_fail'),
      path('unorder/success/<int:order_id>/', index.unorder_success, name='unorder_success'),
      path('order/process_payment/<int:order_id>/', index.process_payment, name='process_payment'),
      path('orders/', index.order_list, name='order_list'),
      path('orders/<int:order_id>/', index.order_detail, name='order_detail'),
      path('toggle_favorite/<int:product_id>/', index.toggle_favorite, name='toggle_favorite'),
      path('product/hot/', index.hot_products, name='hot'),
      path('product/recommend/', recommd.recommend, name='recommend'),
      path('today/chart/', chart.today_chart, name='today_chart'),
      path('echart1/', chart.top_viewed_products, name='echart1'),
      path('echart2/', chart.order_rank, name='echart2'),
      path('echart3/', chart.favorite_rank, name='echart3'),
      path('echart4/', chart.daily_order_volume, name='echart4'),
]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

后端代码:

from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.db.models import Q, Sum
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse
from app.forms import CartAddForm
from app.models import Category, Product, CartItem, Order, OrderItem, Favorite, Product_score

# 产品详情页
@login_required
def product_detail(request, pk):
    product = get_object_or_404(Product, pk=pk)
        # 检测用户是否收藏
    is_favorited = Favorite.objects.filter(user=request.user, product=product).exists()
    # 提交购物车
    if request.method == 'POST':
        form = CartAddForm(request.POST)
        if form.is_valid():
            quantity = form.cleaned_data['quantity']
            # 创建或更新购物车项
            if request.user.is_authenticated:
                cart_item, created = CartItem.objects.get_or_create(user=request.user, product=product)
                cart_item.quantity = quantity
                cart_item.save()

                # 商品评分 加 3
                product_score, score_created = Product_score.objects.get_or_create(user=request.user,product=product)
                product_score.score += 3
                product_score.save()
                return redirect('cart')
            else:
                # 如果用户未登录,处理逻辑可能不同,例如使用 session 或 cookie
                pass
            return redirect('product_detail', pk=product.pk)
    else:
        form = CartAddForm()

        # 浏览量 +1
        product.view_count += 1
        product.save()
        # 商品评分+1
        product_score, score_created = Product_score.objects.get_or_create(user=request.user, product=product)
        product_score.score += 1
        product_score.save()

    return render(request, 'product_detail.html', {'product': product, 'form': form,'is_favorited':is_favorited})

# 购物车
@login_required
def cart(request):
    cart_items = CartItem.objects.filter(user=request.user)
    if request.method == 'POST':
        if 'cart_items' in request.POST:
            cart_item_ids = request.POST.getlist('cart_items')
            order = Order(user=request.user)
            order.save()
            for cart_id in cart_item_ids:
                try:
                    cart_item = CartItem.objects.get(id=cart_id)
                    quantity = request.POST.get(f'quantity_{cart_item.id}')
                    # 如果库存不足
                    if cart_item.product.stock < int(quantity):
                        return redirect(reverse('order_success', args=[order.id]))  # 重定向到订单失败页面
                    # 创建订单
                    OrderItem.objects.create(
                        order=order,
                        product=cart_item.product,
                        quantity=quantity,
                    )
                    # 清楚购物车栏
                    cart_item.delete()
                    # 更新库存
                    cart_item.product.stock -= int(quantity)
                    cart_item.product.save()
                except OrderItem.DoesNotExist:
                    pass  # 如果项不存在,则忽略
            return redirect(reverse('order_success', args=[order.id]))  # 重定向到订单成功页面
        elif 'update' in request.POST:
            # 更新购物车
            for cart_item in cart_items:
                quantity = request.POST.get(f'quantity_{cart_item.id}')
                cart_item.quantity = int(quantity) if quantity else 1
                cart_item.save()
            return redirect('cart')
        elif 'delete' in request.POST:
            # 获取所有被选中的项的 ID
            cart_item_ids = request.POST.getlist('delete')
            for cart_item_id in cart_item_ids:
                try:
                    cart_item = CartItem.objects.get(id=cart_item_id)
                    cart_item.delete()
                except CartItem.DoesNotExist:
                    pass  # 如果项不存在,则忽略
            return redirect('cart')
    else:
        cart_items = CartItem.objects.filter(user=request.user)
        total = sum(item.product.price * item.quantity for item in cart_items)
        context = {
            'cart_items': cart_items,
            'total': total,
        }
        return render(request, 'cart.html', context)


# 提交订单
@login_required
# 提交订单成功
def order_success(request,order_id):
    # 假设订单成功后,返回订单成功页面
    order = Order.objects.get(id=order_id)
    context = {'order': order,'total_cost':order.get_total_cost(),'title':'您的订单已经提交成功','flag':'order'}
    return render(request, 'order_success.html', context)

# 订单失败
@login_required
# 提交订单成功
def order_fail(request):
    # 假设订单成功后,返回订单成功页面
    context = {'title':'订单创建失败,请检查库存'}
    return render(request, 'order_fail.html',context)

@login_required
# 取消订单成功
def unorder_success(request,order_id):
    # 假设订单成功后,返回订单成功页面
    order = Order.objects.get(id=order_id)
    order.status = '已取消'
    order.save()
    context = {'order': order,'total_cost':order.get_total_cost(),'title':'您的订单已经取消成功','flag':'unorder'}
    return render(request, 'order_success.html', context)

@login_required
# 支付成功
def process_payment(request, order_id):
    # 这里添加支付逻辑,比如调用支付网关
    # 以下代码仅为示例,实际项目中需要替换为真实的支付逻辑
    try:
        order = Order.objects.get(id=order_id)
        # 假设支付成功后,设置订单为完成状态
        order.status = '已支付'
        order.save()
        # 商品评分+4
        for item in order.orderitem.all():
            product_score, score_created = Product_score.objects.get_or_create(user=request.user, product=item.product)
            if score_created:
                product_score.score += 4
                product_score.save()
        return render(request, 'pay_success.html',{'order':order})
    except:
        return HttpResponse("支付处理错误", status=400)

@login_required
# 我的订单
def order_list(request):
    orders = Order.objects.filter(user=request.user)
    context = {
        'orders': orders,
    }
    return render(request, 'order_list.html', context)

@login_required
# 订单详情
def order_detail(request, order_id):
    order = Order.objects.get(user=request.user, id=order_id)
    context = {
        'order': order,
    }
    return render(request, 'order_detail.html', context)

# 收藏
@login_required
def toggle_favorite(request, product_id):
    product = get_object_or_404(Product, id=product_id)
    # 这里添加你的逻辑来切换收藏状态
    # 例如,如果有一个 Favorite 模型来存储收藏的商品
    favorite, created = Favorite.objects.get_or_create(user=request.user, product=product)
    product_score , score_created= Product_score.objects.get_or_create(user=request.user, product=product)
    if not created:
        product_score.score -= 2
        product_score.save()
        favorite.delete()
    else:
        # 给商品收藏加2分
        product_score.score += 2
        product_score.save()
        favorite.save()
    is_favorited = Favorite.objects.filter(user=request.user, product=product).exists()
    return JsonResponse({'isFavorited': is_favorited})

@login_required
# 热门
def hot_products(request):

    # 计算每个商品的总热度
    product_scores = Product_score.objects.filter(score__gt=0).values('product').annotate(total_score=Sum('score'))

    # 获取总热度排名前十的商品
    top_10_hot_products = product_scores.order_by('-total_score')[:10]

    # 获取热门商品的ID列表
    hot_product_ids = [item['product'] for item in top_10_hot_products]

    # 获取最近购买的商品,这里假设我们只取最新的5个订单的商品
    recent_items = OrderItem.objects.filter(
        Q(product_id__in=hot_product_ids) &
        Q(order__user=request.user)
    ).select_related('product').order_by('-date_added')[:5]
    products = Product.objects.filter(id__in=[item.product.id for item in recent_items])
    # 自己最近购买商品 以及热度
    recent_products = products.annotate(total_score=Sum('product_score__score')).order_by('-total_score')
    query = request.GET.get('q')
    # 如果没有查询参数,只按照category_id排序
    products = Product.objects.filter(
        id__in=hot_product_ids
    ).order_by('category_id').annotate(total_score=Sum('product_score__score'))
    if query:
        products = Product.objects.filter(
            Q(id__in=hot_product_ids) &
            Q(name__icontains=query)
        ).order_by('category_id').annotate(total_score=Sum('product_score__score'))
    categories = Category.objects.all()

    # 如果有分类选择,过滤商品
    category_id = request.GET.get('category')
    selected_category = None  # 记录选中的分类
    if category_id:
        try:
            selected_category = Category.objects.get(id=category_id)
            products = products.filter(
                Q(id__in=hot_product_ids) & Q(category=selected_category))
        except Category.DoesNotExist:
            products = Product.objects.none()

    # 按照热度排序
    products = products.order_by('-total_score')
    # 分页
    paginator = Paginator(products, 6)  # 每页显示10个商品
    page_number = request.GET.get('page')
    page_obj = paginator.get_page(page_number)

    context = {
        'categories': categories,
        'products': page_obj,
        'selected_category': selected_category,
        'recent_products': recent_products,
    }
    return render(request, 'hot_products.html', context)


推荐算法:

import os
from math import *
import time

from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.db.models import Q
from django.http import JsonResponse
from django.shortcuts import render, redirect
from app import models
from app.models import Product_score, Product, User, OrderItem, Category


def getdata():#获取数据
    alldata=Product_score.objects.values_list('user','product','score')
    data={}
    # for i in alldata:
    #     if not i[0] in data.keys():
    #         data[i[0]]={i[1]:i[2]}
    #     else:
    #         data[i[0]][i[1]]=i[2]
    for user, product, score in alldata:
        if user not in data:
            data[user] = {}
        data[user][product] = score
    return data

#计算用户相似度
def Euclid(user1,user2):#尤几里得距离也就是欧式距离
    #取出两个用户都查看过的的职位
    data=getdata()
    print('data',data)
    user1_data = data.get(user1, {})
    user2_data = data.get(user2, {})
    #默认距离,相似度越大距离越短
    distance=0
    #遍历找出相同职位
    for key in user1_data.keys():
        if key in user2_data.keys():
            distance+=pow(float(user1_data[key])-float(user2_data[key]),2)
    return 1/(1+sqrt(distance))
#计算某用户和其他用户相似度的比对
def top_simliar(user):
    res=[]
    data=getdata()
    for userid in data.keys():
        #首先排除当前用户
        if not userid==user:
            simliar=Euclid(user,userid)
            res.append((userid,simliar))
    res.sort(key=lambda val:val[1],reverse=True)
    return res,data
def recommend(request):#给用户推荐职位方法
    # id =request.session.get('user').get('id')
    #print(top_simliar(id))
    user = request.user
    res,data=top_simliar(user.id)
    print('res+data',res,data)
    workid={}
    try:
        for i in range(5):
            top_user=res[i][0]
            #print(top_user)
            #print(data[top_user])
            workid.update(data[top_user])
    except:
        pass
    #print(workid)
    recommend_list=[]
    workid=sorted(workid.items(),key=lambda x:x[1],reverse=True)[:10]#给列表按打分排序,取出前十个推荐
    for i in workid:
        recommend_list.append(i[0])

    if len(recommend_list) == 0:
        product_scores = Product_score.objects.values_list('product', flat=True)
        recommend_list = list(product_scores)
    print(recommend_list)
    products= Product.objects.filter(id__in=recommend_list)

    # 获取最近购买的商品,这里假设我们只取最新的5个订单的商品
    recent_items = OrderItem.objects.filter(
        Q(product_id__in=recommend_list) &
        Q(order__user=request.user)
    ).select_related('product').order_by('-date_added')[:5]
    recent_products = Product.objects.filter(id__in=[item.product.id for item in recent_items])


    if products:

        query = request.GET.get('q')
        if query:
            products = products.filter(name__icontains=query)

        categories = Category.objects.all()
        # 如果有分类选择,过滤商品
        category_id = request.GET.get('category')
        selected_category = None  # 记录选中的分类
        if category_id:
            try:
                selected_category = Category.objects.get(id=category_id)
                products = products.filter(
                    Q(id__in=recommend_list) & Q(category=selected_category))
            except Category.DoesNotExist:
                products = Product.objects.none()

        paginator = Paginator(products, 10)
        page = request.GET.get('page')

        try:
            page_obj = paginator.page(page)
        except PageNotAnInteger:
            page_obj = paginator.page(1)
        except EmptyPage:
            page_obj = paginator.page(paginator.num_pages)
        print(page_obj)

        context = {
            'recent_products':recent_products,
             'categories': categories,
            'products': page_obj,
            'selected_category': selected_category,
        }

        return render(request,'recommend.html',context)
    return render(request, 'recommend.html')

项目截图:

 

 

 

 

 

 

 

 

 项目最后:

需要该项目的同学可以私信我哦,或者添加我下方名片或者我简介信息获取哦~

支持Python项目的答疑和指导~

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

追寻定义的熊百涛!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值