Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 507
Ibolit Avatar asked Dec 28 '25 22:12

Ibolit


2 Answers

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.

like image 147
tbm Avatar answered Dec 31 '25 14:12

tbm


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
like image 32
goofus gallant Avatar answered Dec 31 '25 14:12

goofus gallant



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!