芹菜在dockerized Django项目中

时间:2018-07-11 09:00:51

标签: django docker rabbitmq celery

我有一个由博客,文章和评论应用程序组成的django项目。评论模型与文章应用程序中的文章模型和博客应用程序中的条目具有通用关系。我希望两个模型(条目和文章​​)显示评论数。为此,我重写了save()update()delete()方法(最后一个由信号处理),这些方法调用处理更新的特定任务。

现在输入代码。 comments/models.py

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

from model_utils.models import TimeStampedModel

from comments.tasks import update_comment_count_on_related_writing


class CommentManager(models.query.QuerySet):
    def update(self, **kwargs):
        comments_for_previous_writings = dict()
        for comment in self.all().prefetch_related('writing_object'):
            comments_for_previous_writings[comment] = {
                'writing_pk': comment.writing_id,
                'writing_type_pk': comment.writing_type.pk
            }

        rows = super().update(**kwargs)
        for comment in self.all():
            update_comment_count_on_related_writing.delay(
                comment.pk,
                comments_for_previous_writings[comment]['writing_pk'],
                comments_for_previous_writings[comment]['writing_type_pk']
            )
        return rows


class Comment(TimeStampedModel):
    body = models.TextField()
    __writing_type_choices = (
            models.Q(app_label='article', model='article') | models.Q(app_label='blog', model='entry'))
    writing_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, limit_choices_to=__writing_type_choices)
    writing_id = models.PositiveIntegerField()
    writing_object = GenericForeignKey('writing_type', 'writing_id')

    objects = CommentManager.as_manager()

    def __str__(self):
        return '({}) {} - {}...'.format(self.writing_type, self.writing_object, self.body[:15])

    def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
        old_writing = None
        old_writing_pk = None
        old_writing_type_pk = None

        created = not self.id
        if not created:
            # Trick used to avoid changing old_writing* references after update in order to allow introducing changes
            # to previously assigned writing.
            comment_before_update = Comment.objects.get(pk=self.pk)
            old_writing = comment_before_update.writing_object
            old_writing_type_pk = comment_before_update.writing_type.pk
            old_writing_pk = old_writing.pk

        super().save(force_insert=False, force_update=False, using=None, update_fields=None)

        if created or old_writing != self.writing_object:
            update_comment_count_on_related_writing.delay(self.pk, old_writing_pk, old_writing_type_pk)

comments/signals.py

from django.db.models.signals import post_delete
from django.dispatch import receiver

from .models import Comment
from comments.tasks import update_comment_count_on_related_writing


@receiver(post_delete, sender=Comment)
def update_writings_comments_count(instance: Comment, **_):
    update_comment_count_on_related_writing.delay(
        old_writing_pk=instance.writing_id,
        old_writing_type_pk=instance.writing_type.pk
    )

article/models.py

from django.contrib.contenttypes.fields import GenericRelation

from comments.models import Comment
from common.models import WritingBase


class Article(WritingBase):
    comments = GenericRelation(
        Comment, related_query_name='articles', object_id_field='writing_id', content_type_field='writing_type')

blog/models.py

from django.contrib.contenttypes.fields import GenericRelation

from comments.models import Comment
from common.models import WritingBase


class Entry(WritingBase):
    comments = GenericRelation(
        Comment, related_query_name='entries', object_id_field='writing_id', content_type_field='writing_type')

    class Meta:
        verbose_name_plural = 'Entries'

处理更新的任务-comments/tasks.py

from django.contrib.contenttypes.models import ContentType

from wiktor.celery import app


@app.task
def update_comment_count_on_related_writing(updated_comment_pk=None, old_writing_pk=None, old_writing_type_pk=None):
    from comments.models import Comment
    if updated_comment_pk is not None:
        updated_comment = Comment.objects.get(pk=updated_comment_pk)
        comments_count = Comment.objects.filter(
            writing_type=updated_comment.writing_type, writing_id=updated_comment.writing_id
        ).count()
        updated_comment.writing_object._meta.model.objects.filter(
            id=updated_comment.writing_object.id
        ).update(comments_count=comments_count)

    if old_writing_pk is not None:
        old_writing_class = ContentType.objects.get(pk=old_writing_type_pk).model_class()
        comments_count = Comment.objects.filter(
            writing_type__pk=old_writing_type_pk, writing_id=old_writing_pk).count()
        old_writing_class.objects.filter(pk=old_writing_pk).update(
            comments_count=comments_count)

配置-wiktor/celery.py

import os
from celery import Celery

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'wiktor.settings')

app = Celery('wiktor')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()


@app.task(bind=True)
def debug_task(self):
    print('Request: {0!r}'.format(self.request))

以及负责芹菜的部分设置:

CELERY_BROKER_URL = 'amqp://guest:guest@rabbitmq//'

CELERY_ACCEPT_CONTENT = ['json']
CELERY_RESULT_BACKEND = 'amqp://guest:guest@rabbitmq//'
CELERY_TASK_SERIALIZER = 'json'
CELERY_TASK_DEFAULT_QUEUE = 'wiktor_queue'

由于一切都可以在docker中运行,因此用于主要服务的dockerfile可以在Django中运行:

FROM python:3.5.2
ENV PYTHONUNBUFFERED 1
RUN apt-get update && apt-get install -y gettext
RUN mkdir /code
WORKDIR /code
ADD requirements/base.txt requirements/dev.txt /code/
RUN pip install -r base.txt -r dev.txt
ADD . /code/
RUN celery -A wiktor worker -D
WORKDIR /code

和docker-compose配置(名为dev.yml):

version: '3'
services:
  rabbitmq:
    image: rabbitmq:3.7-management-alpine
    ports:
      - "15672:15672"
  elasticsearch:
    image: elasticsearch:2.4.6-alpine
  postgres:
    image: postgres
  django:
    build:
      context: .
      dockerfile: ./compose/django/Dockerfile
    command: python manage.py runserver 0.0.0.0:8000
    ports:
      - "8000:8000"
    volumes:
      - .:/code
    depends_on:
      - postgres
      - elasticsearch
      - rabbitmq

现在,代码部分结束了,让我们了解它当前的工作方式。

我从docker-compose -f dev.yml up --build开始项目。在管理面板中,我看到“评论”已分配给特定条目

enter image description here

与此评论相关的条目有2条评论:

enter image description here

我将“注释”中的writing type字段从“条目”更改为“文章”,单击“保存”并继续编辑并刷新“条目”编辑页面。评论数仍为2。

从日志来看,rabbitmq似乎没有收到此任务:

rabbitmq_1       | 2018-07-11 08:53:51.822 [info] <0.640.0> accepting AMQP connection <0.640.0> (172.18.0.5:56766 -> 172.18.0.2:5672)
rabbitmq_1       | 2018-07-11 08:53:51.825 [info] <0.640.0> connection <0.640.0> (172.18.0.5:56766 -> 172.18.0.2:5672): user 'guest' authenticated and granted access to vhost '/'
django_1         | [11/Jul/2018 08:53:51] "POST /admin/comments/comment/1/change/ HTTP/1.1" 302 0
django_1         | [11/Jul/2018 08:53:51] "GET /admin/comments/comment/1/change/ HTTP/1.1" 200 5756
django_1         | [11/Jul/2018 08:53:51] "GET /admin/jsi18n/ HTTP/1.1" 200 3185
django_1         | [11/Jul/2018 08:53:51] "GET /static/admin/img/icon-yes.svg HTTP/1.1" 304 0
django_1         | [11/Jul/2018 08:54:34] "GET /admin/blog/entry/1/change/ HTTP/1.1" 200 5928
django_1         | [11/Jul/2018 08:54:34] "GET /admin/jsi18n/ HTTP/1.1" 200 3185
django_1         | [11/Jul/2018 08:55:30] "POST /admin/comments/comment/1/change/ HTTP/1.1" 302 0
django_1         | [11/Jul/2018 08:55:30] "GET /admin/comments/comment/1/change/ HTTP/1.1" 200 5748

我还通过删除对delay的调用来确保自己的逻辑没有问题,只有芹菜可以了,

update_comment_count_on_related_writing.delay(
                comment.pk,
                comments_for_previous_writings[comment]['writing_pk'],
                comments_for_previous_writings[comment]['writing_type_pk']
            )

成为

update_comment_count_on_related_writing(
                comment.pk,
                comments_for_previous_writings[comment]['writing_pk'],
                comments_for_previous_writings[comment]['writing_type_pk']
            )

在这种情况下,保存“评论”和刷新页面后,我看到comment_count得到了更新。

现在终于出现了一个问题:我该怎么做才能使comment_count自动更新以反映实际的评论数?

0 个答案:

没有答案