6

I want to create a link which opens the django admin add page of a model with some fields pre filled.

I checked that it is possible to add parameters to the GET dictionary of the add form, as in:

<a href='/admin/myapp/mymodel/add?name=John'>add new mymodel with name John</a>

(actually I get the url with {% url 'admin:myapp_mymodel_add' %} but this is just to be explicit).

Now this works fine with numerical values, text values and foreign keys. But what about DateTime fields? When I try to pass such a field (I tried many formats like for example "2014-05-09 10:10:00") I always get a "server error" complaining that:

'unicode' object has no attribute 'date'

in the line of code:

django/forms/widgets.py in decompress, line 902

 def decompress(self, value):
        if value:
            value = to_current_timezone(value)
            return [value.date(), value.time().replace(microsecond=0)] ...
        return [None, None]

where the variable "value" has the value I'm passing on the URL...

Question 1. I would assume that server code should not raise an exception depending on values passed by the user... isn't this a bug in the django libraries?

Question 2. How can I solve my problem i.e. pass an initial datetime through GET parameters to the admin "add" page of a model?

Emanuele Paolini
  • 9,912
  • 3
  • 38
  • 64
  • Have you tried this format: `2010-09-28+21:00:59`? – alecxe May 12 '14 at 02:34
  • Tried now: same error... – Emanuele Paolini May 12 '14 at 08:24
  • Ok, what if you split the date and time into 2 separate parameters: `?date_field_name_0=2010-09-28&date_field_name_1=21:00:59`, where `date_field_name` is the name of your datetime field? – alecxe May 12 '14 at 10:26
  • @alecxe this was in fact the first thing I tried (looking at POST parameters). But the GET parameters with suffix _0 and _1 are simply ignored (no error, but no initial value). – Emanuele Paolini May 12 '14 at 14:20
  • What datetime field do you want to set up through the GET parameters? Could you show the model definition including the `DatetimeField`? Thanks. – alecxe May 12 '14 at 16:01

2 Answers2

4

No, it isn't a bug; there's maybe an argument that the default behaviour could be improved, but the existing implementation is a reasonable first pass (as evidenced by the fact that it works on integers, strings, foreign keys, etc). There's enough metadata about the model, and enough deserialization code that it would be possible to do slightly better handling here, but I don't think it's a straight up bug.

The good news is that there is an official way to handle this; on your ModelAdmin class, define:

def get_changeform_initial_data(self, request):

This method takes the request, and converts that request into the value that will be passed in as the initial argument to the form on a change list (i.e., an add or edit page in Admin). As you might expect, the default implementation of this method is "take the GET arguments and convert into a dictionary"; if you do some additional handling for the fields that you know to be datetimes, then

Alternatively, you don't have to use the request object at all. If you know that "Person" objects will always have an initial name of "Unknown", and an initial start date of 1 Jan 2014, then you can just write:

class PersonAdmin(ModelAdmin):
    ...
    def get_changeform_initial_data(self, request):
        return {
            'name': 'Unknown',
            'start_date': date(2014, 1, 1)
        }

i.e., don't pay any attention to the request data, just populate the initial data manually. Since it's initial data to the form, any existing data on the object will override the value provided in initial, and that initial data will be used without the user needing to provide any GET arguments in their request.

freakboy3742
  • 1,026
  • 10
  • 15
  • Thanks! I see that this method is available with django 1.7 unfortunately I'm using 1.5 currently. About the bug or not: every admin interface based on a model with Date fields is affected by this problem. Even if the programmer does not plan to use GET parameters (and maybe is not aware of this possibility), the admin interface exposes them and opens the possibility to every admin user to generate a server error. Before version 1.7 this is also very hard to fix (I think it would require to redefine ModelAdmin.changeform_view). – Emanuele Paolini May 13 '14 at 06:48
  • Anyway this is only an academic discussion since I agree that the admin interface should be used by a selected class of users which already have many more "dangerous" privileges on accessing and modifying the database. However I think that the "correct" default behavior for the admin application should be to write an error message to the user as if the value was inserted with a POST parameter or, maybe, raise an InvalidRequest instead of a ServerError. – Emanuele Paolini May 13 '14 at 06:50
2

It appears the problem is your passing a string when it expects a date. You need to convert your string into a date first.

You can use the built in python library datetime:

 import datetime
 value = datetime.datetime.strptime(value, "%Y-%m-%d %H:%M:%S")
 def decompress(self, value):
     ...

Edit: alecxe correctly pointed out that I was not properly answering your question. With a bit of a searching I found something that might be more relevant.

According to this answer, Django allows you to replace the GET dictionary before it is processed. Borrowing from this example, it seems feasible that we could intercept your get parameters and replace the string dates with datetime objects in the GET dictionary

    def add_view(self, request, form_url='', extra_context=None):
        // any extra processing can go here...
        g = request.GET.copy()
        g.update({
            'date':datetime.datetime.strptime(request.GET.get('date'), "%Y-%m-%d %H:%M:%S"),
        })

        request.GET = g
        return super(MyModelAdmin, self).add_view(request, form_url, extra_context)
Community
  • 1
  • 1
Del
  • 667
  • 7
  • 23
  • It is not relevant to the problem described in the question. – alecxe May 12 '14 at 19:05
  • I disagree. Emanuele Paolini reports the error message received is "'unicode' object has no attribute 'date'". This is caused by attempting to call .date() on a unicode object. – Del May 12 '14 at 19:29
  • I'm not arguing about this. The problem is that it's not the code he has written by himself. It's a part of a django source codebase. It's either a bug that should be reported, or there should be a way (or workaround) to make it work. – alecxe May 12 '14 at 19:30