Ibolit
Ibolit

Reputation: 9720

Django admin: postgres DateTimeRangeField not displayed properly

Some of my models have postgres-specific django.contrib.postgres.fields.DateTimeRangeFields, and those fields are exposed in the corresponding admin panels. I expected that the ranges forms would consist of two Django-style datetime pickers, with a separate one for the date part and a separate part for the time part (just like the DateTimeField would). However, I get two text inputs which expect input in a very particular format. Is there anything I am missing or have to configure separately?

The relevant code is:

from django.contrib.postgres.fields import DateTimeRangeField
...

class MyModel(models.Model):
    time_off = DateTimeRangeField()

admin:

@register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
    pass

Upvotes: 0

Views: 833

Answers (3)

goofus gallant
goofus gallant

Reputation: 11

To get this working I needed to combine both parts of the split datetimefield (to fix the "list has no method strip()" as other commentors had noted). Some additional validation should probably be present here to ensure "value" has the correct format.

from django.contrib.admin.widgets import AdminSplitDateTime
from django.contrib.postgres.forms import RangeWidget, DateTimeRangeField
from django.forms import ModelForm

class CombinedDateTimeRangeField(DateTimeRangeField):
    def combine(self, value):
        if value:
            return [f"{value[0][0]}:{value[0][1]}", f"{value[1][0]}:{value[1][1]}"]
        return None

    def clean(self, value):
        value = self.combine(value)
        return super().clean(value)

    def has_changed(self, initial, data):
        data = self.combine(data)
        return super().has_changed(initial, data)

class MyModelForm(ModelForm):
    time_off = CombinedDateTimeRangeField(widget=RangeWidget(AdminSplitDateTime))
    class Meta:
        model = MyModel
        fields = "__all__"

@register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
    form = MyModelForm

Upvotes: 1

tbm
tbm

Reputation: 1211

I used the following:

from django.contrib.admin import widgets as admin_widgets
from django.contrib.postgres import fields as pg
from django.contrib.postgres import forms as pg_widgets

@register(MyModel)
class MyModelAdmin(admin.ModelAdmin):

    formfield_overrides = {
        pg.DateRangeField: {
            'widget': pg_widgets.RangeWidget(admin_widgets.AdminDateWidget),
        },
    }

Works perfectly, with a calendar pop-up, 'today' button, and everything. This is for a Date, but you should get similar results with a DateTime.

Upvotes: 4

Pedram
Pedram

Reputation: 3920

You are looking for SplitDateTimeWidget.

Simply change the admin part as:

class MyModelAdminForm(forms.ModelForm):
    class Meta:
        model = MyModel
        widgets = {
          'time_off': RangeWidget(SplitDateTimeWidget())
        }


@register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
    form = MyModelAdminForm

or use formfield_overrides to override the widget if you wish.

Upvotes: 1

Related Questions