返回每篇博文的假日期 - 现在绕过 django 的自动

问题描述 投票:0回答:2

我正在测试我的博客文章是否按时间倒序排列。为此,我必须为创建的每个帖子设置随机日期。我正在使用

faker
来设置日期。我找回了一个假日期,但每个帖子的日期都是相同的。 auto 现在仍然是这里的问题,还是我没有正确使用 Faker?

工厂:

fake = Faker()
mocked = fake.date_time()

class BlogPageFactory(wagtail_factories.PageFactory):
    class Meta:
        model = models.BlogPage

    with patch('django.utils.timezone.now', mocked):
        date = mocked
    # date = datetime.date.today()
    author = factory.SubFactory(UserFactory)
    slug = factory.sequence(lambda n: f"post{n}")
    snippet = factory.sequence(lambda n: f"Article {n} snippet...")
    body = "Test post..."
    featured_image = factory.SubFactory(wagtail_factories.ImageFactory)
    featured_article = False

型号:

class BlogPage(Page):
    date = models.DateField("Post date")
    snippet = models.CharField(
        max_length=250, help_text="Excerpt used in article list preview card."
    )
    body = RichTextField(blank=True)
    tags = ClusterTaggableManager(through=BlogPageTag, blank=True)
    featured_image = models.ForeignKey("wagtailimages.Image", on_delete=models.CASCADE)
    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
    featured_article = models.BooleanField(default=False)

    content_panels = Page.content_panels + [
        MultiFieldPanel(
            [
                FieldPanel("date"),
                FieldPanel("tags"),
            ],
            heading="Blog Information",
        ),
        FieldPanel("snippet"),
        FieldPanel("featured_image"),
        FieldPanel("body"),
        FieldPanel("author"),
        InlinePanel("page_comments", label="Comments"),
    ]

    search_fields = Page.search_fields + [index.SearchField("body")]

    parent_page_types = ["CategoryIndexPage"]
    subpage_types = []

    def serve(self, request, *args, **kwargs):
        """
        Method override to handle POST conditions for blog comments, ``BlogComment``.
        """
        from .forms import CommentForm

        if request.method == "POST":
            form = CommentForm(request.POST)
            if form.is_valid():
                new_comment = form.save(commit=False)
                new_comment.user = request.user
                new_comment.page = self
                new_comment.save()
                messages.success(
                    request,
                    "Your message was successfully "
                    "submitted and is awaiting moderation. "
                    "Thank you for contributing!",
                )
                return redirect(self.get_url())
        else:
            form = CommentForm

        return render(request, "blog/blog_page.html", {"page": self, "form": form})
django django-testing factory-boy
2个回答
0
投票

尝试date_time_this_decade()

fake = Faker()

class BlogPageFactory(wagtail_factories.PageFactory):
    class Meta:
        model = models.BlogPage

    date = fake.date_time_this_decade()
    ...

0
投票

您当前的代码有两个问题,导致您遇到的问题:

  1. 你在模块导入时生成了一个 固定
    mocked
    日期;
  2. 你实际上并没有用你的
    auto_now
    覆盖任何
    patch(..., mocked)

获得一个真正随机的
mocked
约会

问题来自您的代码编写方式;你在模块导入时计算

mocked
日期——因此对于所有未来的调用它总是相同的。

为了获得动态值,您需要使用 factory_boy 的专用声明之一——每次要求工厂生成实例时都会对它们进行评估。

我建议看看我的另一个答案以获得更深入的解释。

覆盖模型中的
date

在这里,你的模型字段声明是一个“标准的”Django 字段,没有魔法;无需修补

django.utils.timezone.now()
,因为将
date=mocked
传递给
BlogPage.objects.create(...)
会起作用。 顺便说一句,这正是 factory_boy 在后台执行的调用。

如果你有一个带有

auto_now_add
的模型,Django 不允许覆盖该值——正如 在他们的文档中看到的

这会给你留下两个可能的选择:

  1. auto_now_add=True
    替换为
    default=timezone.now
    :当
    default
    是可调用对象时,Django 将为模型的每个新实例执行可调用对象,除非调用者提供了一个值——就像您对工厂所做的那样;

  2. 如果那不可能,您可以改写

    _create()
    方法:

    class BlogPageFactory(factory.django.DjangoModelFactory):
      ...
    
      date = factory.Faker("date_time")
    
      @classmethod
      def _create(cls, model_class, *args, **kwargs):
        # Extract the "date" kwarg, as an explicit value will be ignore
        # by auto_now_add
        date = kwargs.pop("date")
    
        # Force `timezone.now()` to always return the expected date
        # for the duration of the instance creation
        with patch("django.utils.timezone.now", lambda: date):
    
          # Let factory_boy actually create the instance
          return super()._create(model_class, *args, **kwargs)
    
© www.soinside.com 2019 - 2024. All rights reserved.