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?
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.
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With