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

Django DRF根据选中userid获取用户邮箱实现告警邮件定向发送

DRF 价格告警定向发送邮件实现方案

核心问题定位

现有代码存在4个直接导致功能异常的问题:

  • 模型字段冗余:SectionAlerts表同时设计了字符串类型user、关联用户的外键userid、关联用户的外键email三个和用户相关的字段,数据重复存储且赋值逻辑混乱
  • 创建告警时用户关联不完整:perform_create方法仅给字符串类型的user字段传了当前用户对象,userid、email两个外键字段始终为空
  • 发信逻辑完全错误:触发告警条件后遍历全量用户表发邮件,没有读取当前告警条目的关联用户作为收件人,导致所有用户都会收到邮件
  • 业务逻辑位置错误:发邮件、删除告警这类有数据修改副作用的逻辑写在了get_queryset方法中,只要访问告警列表接口就会触发发信,不符合HTTP接口规范,容易导致重复发信

分步修正方案

1. 调整模型结构(models.py)

删除冗余的用户关联字段,仅保留一个用户外键,用户邮箱、用户名等信息直接通过外键关联查询,不需要重复存储:

from django.db import models
from ticker.models import Ticker
from users.models import NewUser

class SectionAlerts(models.Model):
    id = models.AutoField(primary_key=True)
    valuenow = models.FloatField(null=True, blank=True, default=None)
    valuealarm = models.FloatField(null=True, blank=True, default=None)
    # 单外键关联用户,调整related_name为语义明确的值
    user = models.ForeignKey(
        NewUser,
        related_name='price_alerts',
        on_delete=models.CASCADE
    )
    ticker = models.ForeignKey(Ticker, on_delete=models.CASCADE)

注意:修改完模型后需要依次执行python manage.py makemigrations、python manage.py migrate同步数据库结构,测试阶段可以先清空原有告警表数据避免迁移冲突。

2. 修正序列化器逻辑(serializers.py)

移除冗余的用户邮箱嵌套序列化,直接通过用户外键读取邮箱字段,同时设置用户字段为只读,由后端自动赋值,不接收前端传参:

from rest_framework import serializers
from ticker.models import Ticker
from alerts.models import SectionAlerts
from ticker.serializers import TickerSerializer

class AlertsSerializer(serializers.ModelSerializer):
    ticker = TickerSerializer(read_only=True)
    # 直接通过关联的user对象取邮箱,设为只读
    email = serializers.EmailField(source='user.email', read_only=True)
    ticker_id = serializers.SlugRelatedField(
        queryset=Ticker.objects.all(),
        source="ticker",
        slug_field='crypto',
        write_only=True
    )
    class Meta:
        model = SectionAlerts
        fields = ['id', 'valuenow', 'valuealarm', 'ticker', 'ticker_id', 'user', 'email']
        read_only_fields = ['user']

3. 修正视图层逻辑(views.py)

首先给创建、查询告警的接口加上登录权限校验,创建告警时正确绑定当前登录用户到外键;其次把发邮件、删告警的逻辑从列表查询接口中移出,修正发信时的收件人读取逻辑:

3.1 修正创建告警接口

from rest_framework import generics
from rest_framework.permissions import IsAuthenticated
from .serializers import AlertsSerializer
from .models import SectionAlerts

class CreateSectionAlerts(generics.CreateAPIView):
    serializer_class = AlertsSerializer
    # 必须加登录权限,否则匿名请求无法获取request.user
    permission_classes = [IsAuthenticated]
    def perform_create(self, serializer):
        # 保存时直接绑定当前登录用户,邮箱后续通过user.email读取即可
        serializer.save(user=self.request.user)

3.2 修正列表查询接口

移除接口内的发信、删数据逻辑,列表接口仅负责返回告警数据:

class ListSectionAlerts(generics.ListAPIView):
    serializer_class = AlertsSerializer
    permission_classes = [IsAuthenticated]
    def get_queryset(self):
        # 支持通过url传pk查指定用户告警,默认返回当前登录用户自己的告警
        if 'pk' in self.kwargs:
            return SectionAlerts.objects.filter(user_id=self.kwargs["pk"])
        return SectionAlerts.objects.filter(user=self.request.user)

3.3 修正告警检查与发信逻辑

不要把这类逻辑放在GET请求流程中,建议放到Celery定时任务、Django自定义命令里周期性执行,核心发信逻辑如下,禁止遍历全量用户:

from django.db.models import F
from .utils import Util

def check_triggered_alerts():
    # 一次性查出所有满足触发条件的告警,select_related提前连表查用户和币种数据,减少DB查询
    triggered_alerts = SectionAlerts.objects.select_related('user', 'ticker').filter(
        ticker__c_0__lt=F('valuealarm')
    )
    for alert in triggered_alerts:
        # 收件人直接取当前告警绑定的用户邮箱
        email_body = (
            f'Hi {alert.user.user_name}, you received this message because the value of the crypto '
            f'{alert.ticker} is now {alert.ticker.c_0}€ and reached the alert value of {alert.valuealarm}€.\n'
            f'This email confirms this event.\n'
            f'Your Cryptothlon Team'
        )
        data = {
            'email_body': email_body,
            'to_email': alert.user.email,
            'email_subject': 'Crypto alert'
        }
        Util.send_email(data)
        # 发信完成后删除该条告警
        alert.delete()

额外优化建议

  • 删除、修改告警的接口也要加权限校验,确保用户只能操作自己创建的告警,避免越权
  • 发邮件逻辑加异常捕获,避免某封邮件发送失败导致整个检查流程中断
  • 可以给告警加is_sent标记字段代替直接删除,方便后续排查发信记录

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 21:36:38