You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中如何实现应用间无依赖的通用关联?(商品点赞场景)

问题

我有一个电商项目,包含三个应用:

  • core:用于满足电商项目的专属需求
  • likes:独立可复用应用,用于关联其他应用的不同模型
  • store:独立可复用应用,包含电商的核心模型与视图

现在需要实现用户点赞商品的功能,让likes应用的点赞关联到store应用的商品,但要求不添加应用间显式依赖(比如不能在store中导入likes)。

现有模型代码

likes应用的LikedItem模型:

from django.conf import settings
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey

class LikedItem(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey()

store应用的Product模型:

from django.db import models
from django.core.validators import MinValueValidator

class Product(models.Model):
    title = models.CharField(max_length=255)
    slug = models.SlugField()
    description = models.TextField(null=True, blank=True)
    unit_price = models.DecimalField(
        max_digits=6,
        decimal_places=2,
        validators=[MinValueValidator(1)])
    inventory = models.IntegerField(validators=[MinValueValidator(0)])
    last_update = models.DateTimeField(auto_now=True)
    collection = models.ForeignKey(
        'Collection', on_delete=models.PROTECT, related_name='products')
    promotions = models.ManyToManyField('Promotion', blank=True)

    def __str__(self) -> str:
        return self.title

    class Meta:
        ordering = ['title']

我曾尝试在core中创建继承自Product的CustomProduct模型并添加likes = GenericRelation(LikedItem),但序列化器部分变得混乱,无法直接扩展Product序列化器添加likes字段,担心需要在core中完全重写Product模型。

解决方案

方法一:动态给Product模型添加GenericRelation(无显式依赖)

利用core作为项目专属应用的特性,在core的应用初始化钩子中动态给store的Product模型添加GenericRelation,完全避免store依赖likes:

  1. 修改core应用的apps.py:
from django.apps import AppConfig
from django.contrib.contenttypes.fields import GenericRelation
from likes.models import LikedItem

class CoreConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'core'

    def ready(self):
        # 延迟导入Product,避免循环依赖
        from store.models import Product
        # 动态为Product添加点赞关联字段
        Product.add_to_class('likes', GenericRelation(LikedItem))

优势:

  • store应用完全保持独立,无需引入likes的任何代码
  • 不需要重写Product模型,原有视图、序列化器可直接复用
  • 可直接在查询中使用likes关联,比如Product.objects.prefetch_related('likes')

方法二:在序列化器中直接查询点赞状态(无需修改模型)

如果不想改动模型结构,可以在core中扩展Product的序列化器,通过ContentType查询点赞数据,彻底解耦store和likes:

  1. 在core中编写扩展序列化器:
from rest_framework import serializers
from store.serializers import ProductSerializer
from likes.models import LikedItem
from django.contrib.contenttypes.models import ContentType

class ProductWithLikeSerializer(ProductSerializer):
    is_liked = serializers.SerializerMethodField()
    likes_count = serializers.SerializerMethodField()

    def get_is_liked(self, obj):
        user = self.context['request'].user
        if user.is_anonymous:
            return False
        content_type = ContentType.objects.get_for_model(obj)
        return LikedItem.objects.filter(
            user=user,
            content_type=content_type,
            object_id=obj.id
        ).exists()

    def get_likes_count(self, obj):
        content_type = ContentType.objects.get_for_model(obj)
        return LikedItem.objects.filter(
            content_type=content_type,
            object_id=obj.id
        ).count()

    class Meta(ProductSerializer.Meta):
        fields = ProductSerializer.Meta.fields + ['is_liked', 'likes_count']

优势:

  • 无需修改任何模型,完全避免应用间依赖
  • 直接继承原有序列化器,避免重复编写字段代码
  • 可灵活扩展点赞相关字段(是否点赞、点赞数等)

注意事项

  • 方法一中,需确保core应用在INSTALLED_APPS中排在store和likes之后,避免初始化时找不到模型
  • 方法二中,要保证序列化器能通过context获取request对象(比如视图继承自GenericAPIView类)

内容的提问来源于stack exchange,提问作者Mo 21

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 21:14:49