开发者

Django - Custom Model Method - How to specify datatype so Admin formats it properly?

Example:

class MyModel(models.Model):
    field1=models.CharField(..)
    field2=models.DateTimeField()
    def today(self):
        return self.field2

When I look at this in the admin site, field2 is formatted differently than the today field.

How can I tell the admin site to treat today like it's treating field2? I.e., tell Django admin that 'today' is a models.DateTimeFie开发者_StackOverflow中文版ld?

Here is what it's showing:

Field2                  today
April 5, 2011, 9:10 a.m.    2011-04-11 08:47:27


To obtain DateTime object call datetime.datetime.now() instead of datetime.datetime.today()

EDIT:

Or use models.DateField() for field2 instead of models.DateTimeField() :-)

EDIT2:

Here is the solution:

def today(self):
    from django.utils import formats
    return formats.localize(self.field2)


That's some really really weird behaviour. At a total guess, it may have something to do with django settings; specifically the DATETIME_FORMAT (and related) settings. The framework probably does introspection on fields, and if they are of DateTime type, are rendered according to the aforementioned settings.

Introspection on methods wouldn't make sense in the majority of cases, so I could understand this behaviour if it is the case.

Try modifying the settings accordingly (provide different datetime formats), and see if the fields change and the method remains the same.

Edit:

Looking at django.contrib.databrowse.datastructures, there is a section of code that does something like:

if isinstance(self.field, models.DateTimeField):
    objs = capfirst(formats.date_format(self.raw_value, 'DATETIME_FORMAT'))

I'd imagine a similar thing happening within the admin app, though I can't find an exact reference at the moment.

To achieve what you want, you'll need to format your datetime appropriately:

def today(self):
    from django.conf import settings
    return self.field2.strftime(settings.DATETIME_FORMAT)

Or, using @cata's comment:

def today(self):
    from django.utils.formats import localize
    return localize(self.field2)


If you choose to supply a "list_display" item through your own function, and you're not happy with the default output, you'll need to format it yourself. In this case, if you want to have identical formatting to what the DateTime database field ends up with:

from django.utils import formats
def today(self):
        return formats.localize(self.field2)

Background:

templates/admin/change_list.html

uses the template tag

django.contrib.admin.templatetags.admin_list.result_list

which in turn will call

django.contrib.admin.templatetags.admin_list.items_for_result()

to render the individual column values for each row.

You'll see that both your values start off being a DateTime instance, either through database lookup or calling your function, see

django.contrib.admin.util.lookup_field()

but the return value "f" will only be a field if there was a database field. You provided a function, so lookup_field() will only provide the value, and "f" will be None.

So in items_for_result(), your value will run through the "if f is None" block and miss out on

result_repr = display_for_field(value, f)

In other words,

django.contrib.admin.util.display_for_field()

will only be called on the database value to format according to the field type, so this is the treatment your function value is missing out on:

elif isinstance(field, models.DateField) or isinstance(field, models.TimeField):
    return formats.localize(value)

and you'll need to do that last line yourself, as shown above.

EDIT: Regarding your question

How can I tell the admin site to treat today like it's treating field2? I.e., tell Django admin that 'today' is a models.DateTimeField?

It's not a models.DateTimeField, it's a function value. If it were a models.DateTimeField, it would be describing your model. Look at all the stuff that entails: http://docs.djangoproject.com/en/dev/ref/models/fields/ In your example, you really could just use field2. Apparently you want to do things to its value, calculate it etc. - so what's today.db_column then?

That said, it would be nice if function values that are DateTime instances were run through format.localize() by default, as that's what the documentation on localization seems to be promising. By the way, I would rather define a formatted value in the ModelAdmin than in the model itself. I usually call it something like "formatted_today" (to keep the datetime value of the original today()), it's just that if the Admin is the only place that needs the formatted value, imho that's where it should be defined.


All previous answers provide solutions, that will handle timezone info incorrectly in new Django versions.

field2 and today will generally show different time if settings.USE_TZ==True.

I found this question today and have spent some time to figure out the correct way:

from django.db import models
from django.contrib import admin
from django.utils.timezone import template_localtime
from django.utils.formats import localize


class MyModel(models.Model):
    # ...
    field2=models.DateTimeField()

class MyModelAdmin(admin.ModelAdmin):
    # ...
    def today(self, obj):
        return localize(template_localtime(obj.field2))

admin.site.register(MyModel, MyModelAdmin)
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜