Override save_model and close window - django

I am writing code to open a new window inside the django admin to add a model instance and to close on save. This is very similar to the behaviour of ForeignKey field add (green plus sign) does, but without selecting the newly created model instance (because its not a foreign key field).
The code I add to make the pop-up link is:
link = '<a id="add_id_event" class="add-another" onclick="return showAddAnotherPopup(this);" href="%s?date=%s">add</a>' % ( addurl,currentdate)
where my model is called Event. I correctly add RelatedObjectLookups.js
When I try to save this model, django applies the same code it would use on a ForeignKey field and tries to activate a SelectBox which I don't have. This causes the javascript to fail before it gets to the window.close()
I've tried overriding the save_model function with
def save_model(self, request, obj, form, change):
if request.GET.get('_popup') == '1':
obj.save()
return HttpResponse('<script type="text/javascript">window.close()</script>')
This code is used but the HttpResponse call is ignored and django renders the default. e.g.
<script type="text/javascript">opener.dismissAddAnotherPopup(window, "14382", "TMC 2012\u002D02\u002D02 10:00:00 DDT2010B\u002D028");</script>
which fails because there is no destination SelectBox object.
Thanks for your help.

You'll need to override ModelAdmin.response_add. That's where the redirect is happening.
In my case, I needed to override the dismissAddAnotherPopup method, so I created a new one called dismissAddAnotherPopupWithUpdate to handle my fancy M2M widgets. Here's the code i used:
def response_add(self, request, obj, post_url_continue='../%s/'):
"""
Overriding to force the widget to update
"""
resp = super(ModelAdmin, self).response_add(request, obj, post_url_continue)
if request.POST.has_key("_popup"):
return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopupWithUpdate(window, "%s", "%s");</script>' % \
# escape() calls force_unicode.
(escape(obj._get_pk_val()), escape(obj)))
return resp

While Cari's solution certainly works, a simple solution to this would be to specify a valid id in your <a> tag. The id is used so dismissAddAnotherPopup() can select the appropriate field after closing the window. It doesn't matter which id you specify for window.close() to work, as long as it exists.

Related

Is there a way to remove hyperlinks from objects on django admin changelist that user does not have permission to change?

I am currently utilizing the has_change_permission hook in my custom django admin class to implement a simple form of row-level permissions and determine whether a non-superuser can edit a particular object, like so:
def has_change_permission(self, request, obj=None):
if obj is None or request.user.is_superuser or (obj and not obj.superuser_only): # (my model has a 'superuser_only' flag that gets set via fixtures, but its beyond the scope of this question)
return True
return False
This works well enough: all objects are shown to a user, but if they click on an object that they don't have permission to edit then they are taken to my 403 page, presumably because PermissionDenied is raised. However, giving them a hyperlink to a permission denied page doesn't seem ideal for this case; I would like to show the objects but not provide any hyperlinks to the edit page on the list page (in addition to raising PermissionDenied if they tried to manually use the URL for the object). Is there a straightforward hook for removing these hyperlinks without a horrendous hack? I'm an experienced django developer so if you can point me in the right direction (if any exists), I'll be able to implement the details or determine that its not worth it for now.
I was able to accomplish this in a fairly straightforward way by overwriting the default get_list_display_links function in the ModelAdmin class (in my admin.py file):
def get_list_display_links(self, request, list_display):
if request.user.is_superuser:
if self.list_display_links or not list_display:
return self.list_display_links
else:
# Use only the first item in list_display as link
return list(list_display)[:1]
else:
# Ensures that no hyperlinks appear in the change list for non-superusers
self.list_display_links = (None, )
return self.list_display_links
Note that my code maintains the hyperlink for superusers. You can easily just use the latter part of the function if you don't want to make this distinction.

Django Override of change_view not saving editable list

I have created an override of changelist_view so that after the save button is hit on an editable list in the admin, the editable list is turned off as follows:
def changelist_view(self,request,extra_context=None):
if request.POST.has_key("_save"):
self.list_editable = []
return super(InventoryAdmin, self).changelist_view(request, extra_context=None)
Everything is working except the data is not being saved. I am fairly new to Django, but I assume the:
super(InventoryAdmin, self).changelist_view(request, extra_context=None)
is supposed to call the original changelist_view function so that the data still gets saved, but it's not working. How do I properly call the original changelist_view function so that it saves the changes to the data?
Because you are essentially disabling list_editable before you call the admin's own changelist_view, it is skipping the saving of any changes.
See the appropriate code here in the Django docs:
https://github.com/django/django/blob/master/django/contrib/admin/options.py#L1270
You will see that when self.editable is empty, it doesn't do the bulk update.
Add 'action_checkbox' in the list_display as the first parameter. For
example:
self.list_display = ('action_checkbox', ...)

Django Overwrite form data saved

I've posted about this problem before, but I still haven't found a solution so I'm hoping I'll have better luck this time.
I have a form that takes inputted data by the user. In another page, I am creating the identical form that the user has populated (pre-filled with that information) for editing purposes. Users will come to this page to EDIT the information they have already put in. My problem is that it isn't overwriting the instance.
def edit(request):
a = request.session.get('a', None)
if a is None:
raise Http404('a was not found')
if request.method == 'POST':
form = Name_Form(request.POST, instance=a)
if form.is_valid():
j = form.save( commit=False )
j.save()
else:
form = Name_Form( instance = a )
For this form, I'm using "unique_together" for some of the values. I'm also calling on `{{ form.non_field_errors }} in the template.
What is happening is when I make changes in the editing view, if the fields changes involves those defined in "unique_together" then an error is returned telling me that the instance already exists. Otherwise it saves a new instance. It isn't OVERWRITING.
Note that the reason i am using unique_together is that I want to prevent users from initially inputting the same form twice (before the editing stage, in the initial inputting view).
Any ideas?
EDIT: note that "a" refers to a session that includes a drop down box of all the available instances. This carried forward will indicate which instance the user wants to edit.
`
Why not do a database lookup of the model your trying to save and pull the fields from the form to the model then save the model?
Instead to store model a in session you should store it on database. Then edit it:
def edit(request, pk):
a = A.objects.get( pk = pk)
...
pk it the a identifier, you can send it to view via urls.py. I encourage to you to use POST/Redirect/GET pattern.
You can add a 'state' field on your model to control workflow (draft, valid)
You should not save objects in the session. If you really need to use a session - save a PK there and retrieve object right before giving it to Form. But the better solution is to send it in GET or POST parameters or included in url. Sessions are unreliable, data inside it can be destroyed between user's requests.
And you can retrieve value from a session in a more pythonic way:
try:
a = request.session['a']
except KeyError:
raise Http404('a was not found')

Django: Form in base template

Hi Stackoverflow people,
In my Django project I created a form to register users. This forms can be called through a specific url -> view method. Once the user has filled in the form, presses submit, the same view method will be called and if form.is_valid() is true, then ... simply a standard form, nothing special.
Now, I would like to integrate this little form on every page, and therefore I would like to add it to the base template. I have read that I could populate the form variable through a context_processor, but could I define the process after the submission of the form?
I have created the context_processor.py (as below), added the context_processor to the TEMPLATE_CONTEXT_PROCESSOR dir in the settings (as described here):
from app.forms import Form
def registration_form(request):
return {
registration_form : Form()
}
First of all, the form variable won't be displayed.
And secondly, how do I manipulate the form submission?
I think I misunderstanding the context_processor of Django and would be more than happy about comments on the overall process.
Thank you!
how are you trying to access to form in your template? you probably don't want to use a function name as your dictionary key, maybe you want
return {
'registration_form': Form(),
}
not sure what you mean by manipulate the form submission, but i guess you'd need all the form processing logic in your context processor
if request.POST:
form = Form(request.POST)
# validate etc
instead of creating context processor, create template tag for the purpose and place the tag in base.html
for form submission and displaying errors use ajax, and front-end validations.

overriding methods of ModelAdmin

I am working on a Django project where any time an admin does something in the admin console (CRUD), a set of people gets notified. I was pointed to three methods on ModelAdmin called log_addition, log_created and log_deleted which save all the necessary info into a special database called "django_admin_log".
I placed the following code into my admin.py:
class ModelAdmin(admin.ModelAdmin):
def log_addition(self, request, object):
subject = 'admin test of creation'
message = 'admin creation detected'
from_addr = 'no_reply#example.com'
recipient_list = ('luka#example.com',)
send_mail(subject, message, from_addr, recipient_list)
return super(ModelAdmin, self).log_addition( *args, **kwargs )
This code however, gets ignored when I create new users. Many posts actually recommend to create a different class name (MyModelAdmin) and I am not entirely sure why - the point is to override the existing model. I tried it, but with the same result. Can anyone point me in the right direction please? How exactly do you override a method of an existing class and give it some extra functionality?
Thank you!
Luka
EDIT: I figured it out, it seems that I had to unregister and re-register User for my change to work.
remove the return at the end.
If that doesn't work, you can instead put the code in a function called add_view:
class ModelAdmin(admin.ModelAdmin):
add_view(self, request):
...
super(ModelAdmin, self).add_view( *args, **kwargs)
This function can be overwritten to add functionality to the admin's view. If you look at the admin code:
https://code.djangoproject.com/browser/django/trunk/django/contrib/admin/options.py#L923
you will see that the function you have tried to overwrite is called from within the add_view function so you could equally put the code here