Search code examples
djangodatetimeintegration-testingfactory-boy

Datetime field in a DjangoModelFactory breaks integrated tests (2.1)


I want to add an "expiry_date" field to a DjangoModelFactory, to match its related model.

Here is my implementation :

models.py

def set_default_expiry_date():
    return timezone.now() + datetime.timedelta(days=7)

[...]

    expiry_date = models.DateTimeField(
            verbose_name=_('Expiry date'),
            default=set_default_expiry_date,
            validators=[validate_expiry_date]
    )

factories.py

class OfferFactory(factory.django.DjangoModelFactory):
[...]
    expiry_date = factory.LazyFunction(set_default_expiry_date)

test_views.py

def test_POST_error_messages(self):
    offer = factory.build(dict, FACTORY_CLASS=OfferFactory)
    offer['price'] = 9999
    offer['item_count'] = -123

    self.client.force_login(self.company_user)
    response = self.client.post(self.url, offer)

    self.assertEqual(2, len(response.context['form'].errors))
    self.assertTrue(
        'price' and 'item_count' in response.context['form'].errors
    )

This test should only return two error messages, both from failed validation constraints on the 'price' and 'item_count' fields. Yet I get a translated form error message saying that I should provide a valid date and time. This error message does not originate from the custom validator I have added for this field.

here is the form's definition, for the sake of completeness :

forms.py

class OfferForm(forms.ModelForm):
[...]
    class Meta:
        model = Offer
        fields = (
                [...]
                 'expiry_date'
        widgets = {
        [...]
                'expiry_date': forms.DateTimeInput(
                    attrs={'class': 'form-control', }
                )
        }

I have USE_TZ and USE_L10N enabled.

It looks like the datetime object should use a localized format but fails to do so.

When I run the server, the datetime field uses the localized format.

So this isn't a configuration issue at the form level.

Any insight is appreciated and thank you for your time.

EDIT:

output of print(offer)

{'title': 'Back grow artist.', 'description': '...', 'price': 9999, 'item_count': -123, 'discount': 18, 'created_by': <CustomUser: TestCompany5>, 'expiry_date': datetime.datetime(2019, 4, 15, 13, 9, 52, 202191, tzinfo=<UTC>)}

Solution

  • In django, widgets are responsible for rendering the view. so you might expect that rendering a non-formatted DateTimeInput cause this unexpected behavior.

    Try doing this:

    class OfferForm(forms.ModelForm):
    [...]
        class Meta:
            model = Offer
            fields = (
                    [...]
                     'expiry_date'
            widgets = {
            [...]
                    'expiry_date': forms.DateTimeInput(
                        attrs={'type': 'datetime-local', 
                               'class': 'form-control', },
                        format='your-desired-format'
                    )
            }
    

    Also add your desired format to your model field supported formats

    input_formats = ['list-of-desired-formats']
    

    For more details here: docs