在ModelViewSet和Django Rest Framework中使用自定义函数

时间:2018-07-10 20:57:11

标签: python django django-rest-framework

Django 2.0,Python 3.6,Django Rest Framework 3.8

我仍然对Django Rest Framework还是很陌生,我正在努力将观点包裹在视图集中使用函数的逻辑上(即使这是包含函数的正确位置)。

基本上,我想在用户在此特定视图集中将某些内容发布到api时发送电子邮件。我尝试使用send_mail函数,但未成功。我有以下基于类的视图:

class SendInviteView(viewsets.ModelViewSet):
    queryset = models.Message.objects.all()
    serializer_class = serializers.MessageSerializer

    @action(methods=['post'], detail=True)
    def send_the_mail(self, request):
        send_mail(
            'Invitation',
            'Try our app!',
            'exampleemail@gmail.com',
            ['examplerecipient@gmial.com'],
            fail_silently=False,
        )

[模型和序列化器是非常基本的,对于这个问题,我认为不需要,基本上只是一个EmailField()。我最终计划使用该电子邮件字段的输入来替换examplerecipient@gmail.com,但现在我只想了解如何向视图集添加功能]

运行python manage.py check

会导致错误

我通过sendgrid设置了我的电子邮件客户端,并且能够成功地将电子邮件发送给要求通过rest-auth重设密码的用户,但是我不了解在该上下文之外如何发送电子邮件

非常感谢您的帮助。

2 个答案:

答案 0 :(得分:5)

讨论之后,我将提出以下建议。

from django.conf import settings
from django.core.mail import send_mail
from django.db import models
from rest_framework import serializers, viewsets, routers, mixins
from rest_framework.response import Response


class Message(models.Model):
    sender = models.ForeignKey(settings.AUTH_USER_MODEL)
    recipient = models.EmailField()


class MessageSerializer(serializers.ModelSerializer):
    message = serializers.CharField(write_only=True) 

    class Meta:
        model = Message
        fields = ['recipient', 'message']

    def create(self, validated_data):
        message = validated_data.pop('message')
        message_obj = super().create(validated_data)
        send_mail(
            'Invitation',
            message,
            'exampleemail@gmail.com',
            [message_obj.recipient]
        )
        return message_obj

class SendInviteView(mixins.CreateModelMixin, viewsets.GenericViewSet):
    serializer_class = MessageSerializer

    def perform_create(self, serializer):
        serializer.save(sender=self.request.user)


router = routers.DefaultRouter()
router.register('send_invite', SendInviteView, base_name='send_invite')
urlpatterns = router.urls

让我们分解。

如果要存储发件人,则需要在模型中将ForeignKeyUser

对于序列化程序,您需要手动添加message字段,因为该字段在您的模型中不存在,但用户应提交。我们将其设置为只写,因为该序列化程序还将用于将创建的Message序列化回用户以供响应,并且Message没有message字段。该序列化程序还将根据recipient模型为Message自动生成字段。

然后,我们在此序列化程序中覆盖create,因此无论何时使用它创建新的Message,它都会发送一封电子邮件。它调用super().create(..)来将Message实际保存到数据库中,然后发送电子邮件。我们使用.pop()message中删除validated_data,因为Message不包含这样的字段。

对于视图,我们不需要ModelViewSet提供的全部内容。它增加了创建,读取,更新和删除(CRUDMessage的能力,而实际上并不需要。您只需要简单的Create(创建)就可以将HTTP请求转换为POSTGenericViewSetCreateModelMixin正是我们想要的东西(实际上ModelViewSet仅具有更多mixin)。来自用户的数据将由序列化程序验证,然后将调用perform_create方法。我们将sender=self.request.user传递给serializer.save()是因为我们需要将sender保存到Message中,并且sender字段实际上不在数据中,它是当前的用户登录。 serializer.save()将运行我们的MessageSerializer.create(),所以我们很高兴。

请注意,这些内容仅适用于登录用户,因为我们不知何故需要填充数据库中的sender字段,因此添加

是正确的
class SendInviteView(mixins.CreateModelMixin, viewsets.GenericViewSet):
    permission_classes = [IsAuthenticated]
    ....

因此,只有经过身份验证的用户才能发出请求。 希望这会为您澄清事情。此致)

答案 1 :(得分:0)

如果我的理解正确,您可以在urls中提及如下函数/方法,

url(r'some/end/point/', views.SendInviteView.as_view({"post": "send_the_mail"})

因此,您的看法就像,

class SendInviteView(viewsets.ModelViewSet):
    queryset = models.Message.objects.all()
    serializer_class = serializers.MessageSerializer

    def send_the_mail(self, request):
        recipient = request.data['recipient']  # json array
        send_mail(
            'Invitation',
            'Try our app!',
            'exampleemail@gmail.com',
            recipient,
            fail_silently=False,
        )
        return Response("mail sent successfully")


由于recipient需要一个数组,所以POST payload就像是

{
    "recipient": ["mail1@dom.com", "mail2@dom.com", "mail3@dom.com"]
}