Django object deletion does not work - python

I've spend hours today figuring out how to delete objects in Django.
Now I have found and tried 4 different approaches on the Net, that only differ in whether a form/POST/GET approach is used or not. CSRF attacks are irrelevant for me, as the page is supposed to run locally.
What does the app do? So far only uploading files under a certain project name. This name is slugified and used as a key to hash files of a project together. Now I want to also delete files (later whole projects).
Here one of the approaches:
In the .html:
<a href="{{file.pk}}/delete" class="btn btn-sm" role="button" title="Delete File">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
</a>
This link is diplayed in a detail view, so the url already looks somethin like /files/SLUG/
Then in urls.py:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^upload/$', views.file_upload, name ='upload'),
url(r'^upload/(?P<slug>[\w-]+)/$', views.file_upload, name ='upload'),
url(r'^(?P<slug>[\w-]+)/$', views.file_detail, name='detail'),
url(r'^(?P<slug>[\w-]+)/(?P<pk>\d+)/delete$', views.file_delete, name="file_delete"),
]
And finally the views.py:
def file_delete(request, slug=None, pk=None):
instance = get_object_or_404(File, slug=slug, pk=pk)
# instance = File.objects.filter(pk=pk, slug=slug)[0] # the same as above
instance.delete() #does not work
return redirect("index") # or anything, this part is no problem
Everything runs through without any errors, but when I check the database on /admin, no file is gone. It simply does not get deleted. Sometimes, when logging in on /admin after trying a couple of times, I see multiple "File successfully deleted" messages at the login screen. But then the files are still in the database list.
Django Docs tells me delete() should return some kind of dictionary and how many objects were deleted e.g. like so:
>>> e.delete()
(1, {'weblog.Entry': 1})
But in may case it just says: None or returns a path starting with a slug.
I'd be very gratefull for any hint. I know there is at least one other post on stackoverflow concerning this, but unfortunately no answers

The queryset delete method (e.g. File.objects.filter(...).delete()) returns a tuple.
When you delete an instance (e.g. instance.delete()) it returns None.
I'm not sure what you mean by 'when I check the file list, no file is gone'. Note that when you delete a model instance, it will remove the row from the database. If the model has a FileField, it won't automatically delete the associated file.

I had a similar problem (that's how I found this), and I think if you simply put a '/' in the end of the URL for delete, it should work. At least thats what worked with me.
...
url(r'^(?P<slug>[\w-]+)/(?P<pk>\d+)/delete/<<<<$', views.file_delete, name="file_delete"),
]

Related

django-easy-select2 Select2Multiple widget doesn't render

The installation page looked simple enough. I installed it, added easy_select2 in INSTALLED_APPS in the settings, ran collectstatic, and then had this in my form:
from easy_select2 import Select2Multiple
# from django_select2 import forms as select2_forms
class LeadForm(forms.Form):
email = forms.CharField(max_length=100)
overseas_company = forms.MultipleChoiceField(
choices=countries,
label='Do you have any companies overseas and where?',
widget=Select2Multiple()
)
But it still renders as if I had done nothing at all. I tried django_select2 as well, and it didn't work either, so I must be doing something wrong Select2 wise.
I tried looking at the HTTP request log. Merely enabling easy_select2 doesn't make the template request the jQuery/select2 js files that are needed for the Select2 widget to function. Is this the problem? But the tutorial never said I had to add anything to any existing templates.
I had the same problem too.
You have to add {{ form.media }} in the head section of your template for django to make it work.
source: http://do-it-big.com/getting-django-easy-select2-to-include-jquery-and-friends/

Pass <input> value to Django view

Let's say I have the following pointless example view:
def foo(request, input):
return HttpResponse()
and in a template I have a form:
<form method="get" action="{% url 'foo' ??? %}">
<input id="myinput" type="text" name="myinput">
...
</form>
Finally, I have the following url in my URLconf:
urlpatterns = [
url(r'^foo/(.+)/', views.foo, name='foo'),
]
What I would like to do, is pass the value entered by the user into the input with the id of #myinput to the foo() view function. To put it another way, you should be able to enter bar in the html input, and when you submit the form it will take you to foo/bar/.
I know that within the foo view I could access the value of the input easily with request.GET['myinput'], but I want it to show up in the url as well.
This seems like it should be a fairly common task, but I have not been able to come up with a solution yet. Any suggestions would be appreciated. My Frankenstein's Monster of a first Django site is almost complete, and this is one of last pieces I am missing.
The source of my misunderstanding
Although I did not make this clear in an attempt to simplify my example and avoid using app-specific code, my use case is a simple search view. The view was actually one of the first views I wrote in the start of my Django journey, and I mistakenly was POSTing my data instead of GETing it. This was making it so that if I was searching for the item foo, it would take me to the detail page for foo, but the url would be mysite/search/ (i.e., the search query is not included in the url though it is included in the request), and I can't return to those search results by visiting the url mysite/search/.
While I was using a GET request in my toy example in this question, I didn't realize that I had been using a POST in my app, and that with some minor tweaking I can get the functionality I want for free very easily. I know that all of this is extremely obvious to veteran and even intermediate web developers, but for someone starting from scratch without web or cs experience, things like HTTP can be a little confusing. At least for me it is. Thanks so much to #Two-Bit Alchemist for explaining this in a way that I can understand.
Applying all this to my toy example
I would get rid of the passed parameter in my view:
def foo(request):
# If I want to do something with the search query, I can access it with
# request.GET['search_query']
return HttpResponse()
change my form in my template to:
<form method="get" action="{% url 'foo' %}">
<input id="myinput" type="text" name="search_query">
...
</form>
and change my url to:
urlpatterns = [
url(r'^foo/search/', views.foo, name='foo'),
]
As #Two-Bit Alchemist said: "The rest will happen like magic". If a user enters bar in the input and submits the form, they will be taken to foo/search/?search_query=bar. This is what I was looking for.

How to secure URLs in Django

Is there a way to prevent users from accessing some (or all) URLs in application? For example, I am following Django tutorial and one of the examples has a URL:
#music/album/<pk>/delete
url(r'image/(?P<pk>[0-9]+)/delete/$', views.ImageDelete.as_view(), name='image-delete'),
that deletes database entry give pk as a parameter. Of course, now it is possible to delete this entry with just copy-pasting the URL with any existing primary-key, so what is the best practice to avoid it? Thanks
EDIT. Based on the replies and comments, I decided to elaborate a bit more. I am actually using DeleteView and forms with POST request as #solarissmoke suggested in answer.
<form action="{% url 'album:image-delete' image.id%}" method="post" style="display: inline;">
{% csrf_token %}
<input type="hidden" name="image_id" value="{{ image.id }}"/>
<button type="submit" class="btn btn-default btn-sm">
<span class="glyphicon glyphicon-trash"></span>
</button>
</form>
and in my views.py:
class ImageDelete(DeleteView):
model = Album
# if you successfully delete the object, page redirects to <homepage>
success_url = reverse_lazy('album:index')
So, there were few suggestions on checkin whether the user is verified to delete URL entry (e.x. the image) and to add pop up/notification to verify if the user indeed wants to delete the entry. However, it does not feel like a complete solution. In the comments I brought example of Facebook, where you can not delete imeage/post by just copy-pasting the delete URL. Surely I'm not asking for Facebook-like security, however, I'm really curious how can secure URLs so that it's nearly impossible for regular user to delete entry with simple copy-pasting. Thanks again!
Best practice is that you should not be allowing modification of data like this through HTTP GET requests, which are intended (as the name suggests) for getting data rather than updating it.
You should use forms and POST requests to perform actions like deleting objects etc. Django provides lots of helper views for doing this. For example DeleteView:
A view that displays a confirmation page and deletes an existing object. The given object will only be deleted if the request method is POST. If this view is fetched via GET, it will display a confirmation page that should contain a form that POSTs to the same URL.
The advantages of using these views are:
You can make sure the user has permissions to edit an object before making any changes. Django will perform the basic checks (e.g., CSRF) for you. You can augment the views to perform additional checks like making sure a user is logged in or checking any other permission.
You can enforce Cross-Site Request Forgery Protection.
It is not possible to accidentally delete an object by visiting a URL a second time (as the documentation above explains).
there are many ways.. e.g:
user = request.user
if user.is_authenticated() and user.profile.can_delete_image(image_pk):
# only then, image can be deleted by this user
# can_delete_image(image_pk) is defined by you
else:
raise DeletePermissionDenied # you can define your own Exception, just for fun

url template tage variable not being parsed django

I am trying to dynamically generate links in my template of the form ..../hub/username/ but am always getting back only ..../hub for some reason. I have read through the docs several times and it seems very clear what to do, but I am just making some mistake somewhere I suppose!
Please Help!
I have in my urls:
urlpatterns = patterns(
'',
url(r'^hub/(.+)/$', 'hub.views.hub_view', name="hub"),
...
...
)
I have in my template:
<li><a id="todoMenuButton" href="{% url 'hub' user.username %}">ToDo</a></li>
template context preprocessors is enabled in my settings:
TEMPLATE_CONTEXT_PROESSORS = (
'django.core.context_processors.request',
)
Thanks!
Instead of adding the request context processor to the default set, you've overwritten the defaults with that single one. That means there is no user variable defined in the context. You could use request.user instead, but it would be better to fix your seeing as there are other useful processors you are missing out on (eg debug).
It was a simple mistake, I was using the todo button instead of the hub button :/
Sorry!
Thanks!

django - urlconf request mapping and views not reached

i have a form in a template, but it can't reach the correspondant view. the method is GET. i've tried also to pass it an argument just to populate the query string, but the request doesnt reach the mapped view. here'sthe views.py, urls.py and the template:
template.html
<div id="search-new-btn">
<form action="/anagrafica/new/" method="get">
<input type="submit" title="Crea nuovo soggetto anagrafica" value="Nuovo" />
</form>
</div>
views.py
def anagrafica_new(request):
if request.method == 'GET':
form = AnagraficaForm()
sub = 'Nuovo soggetto anagrafica'
dict = {'sub':sub,'form':form}
return render_to_response('anagrafica_form.html', dict, context_instance=RequestContext(request))
urls.py
...
url(r'^anagrafica/new/',('gpf1.views.anagrafica_new')),
...
(http://openpaste.org/05b157c1)
Anyway, i've also tried to remove the () from the callback url.py mapping, but it didnt change nothing.
the request seems to be mapped cause my debug server print this:
"GET /anagrafica/new/? HTTP/1.1" 200 17310
but nothing more. the browser remain on the same page; i also put a print statement just inside the view but it never has been reached. any idea?
thanks,
Luke
As Willian suggested don't put the view in a tuple. Another thing you are missing is the $ at the end of regular expression i.e.:
urls.py
...
url(r'^anagrafica/$','gpf1.views.anagrafica'),
...
url(r'^anagrafica/new/$','gpf1.views.anagrafica_new'),
...
Make sure that the folder containing gpf1 package is within Pythonpath variable (reachable by python).
Edit:
Subsequently I noticed you have a root url map for
url(r'^anagrafica/','gpf1.views.anagrafica')
Since you are missing $ at the end Django will try to map anagrafica/anything here to gpf1.views.anagrafica which is probably not what you want. Consider rereading regular expressions and Django's url mapping documentation as it will make things a bit clearer.

Categories

Resources