Personalized/random URLs in Python/Django - python

I'm a Python beginner (and English language beginner too, by the way).
I just created a simple form in Python/Django that creates, edit and remove items.
I have HTML links that refers to my URLs like:
FILE.HTML:
<INPUT Type="BUTTON" VALUE="Edit" ONCLICK="window.location.href='/edit/{{ object.id }}/'">
URLS.PY:
url(r'^edit/(?P<id>\d+)/$', 'app.views.editobj'),
VIEWS.PY:
def editobj(request, id):
But of course, there's a problem, I wouldn't like people go direct on URL (only if the button was clicked) because somebody could just type on URL: /removeobj/1 and remove the object ID=1. What I would like to do is create differente URLs, maybe random, so the user would never guess what is the URL that the button will open, and of course, that it does work with the ID argument, so when it's goind to edit/remove, opens the right object.
I'm Hoping I was clear on my needs. Thanks.

Generating random URL's would be highly inefficient, not to mention unnecessarily difficult to implement. The common way to do what you are asking is to POST to a URL. I think you should do a little more reading on Django POSTing, as it will help you get a better understanding of what it does. In any case, here is an example of using this:
urls.py
url(r'^delete/$', 'app.views.delete_object', name="delete_obj"),
views.py
def delete_object(request):
""" Get the ID of an object via POST, then delete it. """
if request.method == 'POST' # This makes sure the request is a POST
obj_id = request.POST['obj_id']
obj = MODELNAME.objects.get(id=obj_id) # Use your model name here
# You can use if conditions here to make sure the object you just
# retrieved is allowed to be deleted by this user, or in general.
obj.delete()
messages.success(request, 'Object successfully deleted!')
return redirect(reverse('index')) # Make sure you use a name that exists
.html
<form method="POST" action="{% url 'delete_obj' %}">
{% CSRF_TOKEN %}
<input type="hidden" value="{{ obj.id }}" name="obj_id" />
<button type="submit">Submit</submit>
</form>
You can use more logic in the views.py to make sure that the object is allowed to be deletable, but for the most part, the code I wrote should give you somewhat of an understanding of the way to create a POST -> Delete Object workflow. Feel free to ask questions in the comment section below my answer.

The simplest way I see to do a "random" url would be to add a UUIDField (https://docs.djangoproject.com/en/1.8/ref/models/fields/#uuidfield or https://github.com/dcramer/django-uuidfield for Django <1.8) with a default to your model.
Your url can then become
url(r'^delete/(?P<uuid>[\da-f-]+)', 'app.views.delete_object', name='delete_obj')
uuid's are virtually impossible to guess.
Now if you don't add extra security to check that the user has the right to delete the object, anyone could still run a robot that would go through every single possible uuid and flush your database.
And if it is just a matter of not "guessing" #Hybrid's solution is probably a better starting point.

Related

Variable URLs and passing non-input value from html to Python (Flask)

According to this question you can only send data from input forms from html to Python with POST. I'm trying to figure out how to pass a value (that's actually originally contained in a dictionary that I passed in from Python) from html to Python.
My two approaches I considered (and have not figured out how to do successfully) are:
Taking a look at the Flask quickstart, this should be quite simple. I'm just not sure what the syntax should look like on the html side to pass in this parkCode.
#app.route('/park/<parkCode>', methods =['GET', 'POST'])
def park(parkCode):
return render_template('park.html', parkCode = parkCode)
Alternatively, is there some way to simply send a string from html to Python without using an input form? I have yet to find a way to do this.
For reference, this is the line where I'm sending over the ```parks`` dictionary:
return render_template('search_results.html', parks=parks)
Then, in my search_results.html file:
{% for park in parks %}
<div method = "POST" action = "/park">{{park["fullName"]}}</div>
{% endfor %}
But I want the to send the park["fullName"] to my Python code.
.route decorator always handles only URL paths. Since form action is a static value in HTML, the only way to change it is to use JavaScript (for example, by changing the action attribute at submit time). If you're going to use JavaScript, you might as well then just use JavaScript to submit the request itself, which leads us to
Yes, you can use AJAX to send a request. Since ES6, the easiest way to do this is fetch. The choice of whether to use a form or whether to use AJAX depends on what you want to happen after the request: a submitted form results in a new page being rendered, while an AJAX request cannot change the current page, only trigger JavaScript code (although obviously you can change the page in JavaScript should you so wish).
Basically, you can't do what you want without JavaScript. The third option that does work without JavaScript is using the form as it was meant to be used. On flask side, it involves not naming the parameter inside the route, but using request.args (for GET forms) or request.form (for POST forms):
#app.route('/park', methods =['POST'])
def park():
parkCode = request.form.get('parkCode')
return render_template('park.html', parkCode = parkCode)
with the accompanying HTML:
<form action="/park" method="POST">
<input name="parkCode"/>
<input type="submit" value="Submit"/>
</form>

How to clean up input data in a Django view to avoid CSRF?

Lets say, in a view, I use input data to create an instance of a Model in my Django project.
def create_post(request, message):
post = Post.objects.create(message=message)
post.save()
Now, the problem is, the 'message' is coming from sources which may be unsavory, that is, a plugin that makes AJAX requests to my API. If it was a django form, I could have just used {% csrf_token %} and cleared the issue, but, I want to do this in my view. How do I do this?
Note, please don't suggest adding csrf protection in my Javscript code, because that will not work for my case.
Thanks!

Django: Python variable data as background image

Details and Problem:
I am making a website which uses steam's social authentication and API, it saves users data in the database, and makes the session cookie that contains two main data, username and logged in cookies.
The data which the variable contains is the url to the image, which leads me to the problem, i want to show the picture of user on the index page, but css static file would not allow me to use the variable.
so i tried some other options:
Using HTML <style> tag:
<a class="profile_picture" href="/profile" style="background-image: url({{ picture }})"></a> # would not work, as it takes every character as string.
Using HTML <img> tag:
<img class="profilepicture" src="{{ picture }}"></img> # same problem here, takes every character as unicode string.
Question:
Is there any other way to do this with the setup i have? ( Django, CSS, HTML, Python ), i am trying to do this without installing something extra, i have seen sass, but is there still any other possible way?
Can i use <style> or <img> tag to take variable url as background image?, if so how?
It's hard to answer this question without all of the details, but the first issue that comes to mind is that you are most likely referencing your context variable "picture" incorrectly. Again, I'm not sure by your question what exactly you are doing, but perhaps this example will help:
If I have a model named "Person" and that model has 2 fields, "name" and "picture". In my views.py, if I had a function called main:
def main(request):
persons = Person.objects.all()
return render(request, index.html, {"persons":persons } )
This function creates an context variables named persons that will be available in your index.html file. Obviously this is a very loose example, but serves to guide you with the basics. So in your index.html template, you would reference the picture field by doing this:
{% for person in persons %}
{{ person.picture }}
{% endfor %}
Hope that helps.

How to use <select> information in django form

So I have my django project which includes a HTML page that shows a list and a submit button.
I want to use the submit button to send the selected item ID to the server and than use it.
That`s my code :
<form>
<select>
{% for item in list %}
<option value={{item.name}}>{{ item.name }}</option>
{% endfor %}
</select>
<input type="submit"/>
</form>
The things I want to know are :
What to write in the action of the form so it will only reload the page.
How to enter the form data into a view.
As i understand, you want to take the value on your select and do something with it in the server.
I would advise you to read the documentation, as it is pretty detailed about what you need to know to work with forms. You should also read a little about forms, as you are missing a couple details.
Now, the action must point to one of your urls. Your url must be pointing to a view and in your view, if everything is ok, you should be getting a request object.
Depending on your post method, you have a python dictionary in request.GET or request.POST, filled with the values in your form.
That is assuming you are using your form created from scratch. In django you can use the Form class, which creates the html (or lets you create it, but giving you some constraints), validates the form, saves the form to a model (in the case it is a ModelForm). It is a valuable class for me and prefer it over working with raw html.
Also, assuming you haven't, i strongly advice you to go through the getting started. Even if it keeps things basic, it does a good job at introducing core django modules.

What is the best secure way to allow a user to delete a model instance that they added to the db?

I would like to give users access to delete a model instance that they added to the db. In the django docs it says allowing someone to delete from the template is not a good practice. Is there a secure way to let a user click a "delete this" link from the template and remove that model instance? How should I go about doing that?
Check out this question for discussion related to what you are asking about.
Essentially, when you normally click on a link on the page the browser makes a GET request to the server to get the next page's contents. Just like there is a lot of pushing towards semantically relevant CSS layouts, it is also important that your page requests are semantically relevant. The problem with using links to remove items is that it is making a GET request to DELETE something in the database. From this comes the problem that some search engines might index your links and accidentally erase content. There also comes the problem of cross-site request forgeries which can make an unsuspecting user make a command to a website without being aware. So the proper way to handle this is by following the rule that any request that modifies state in the server should be processed via POST. As such, instead of doing this:
Delete Item
It is better to do this:
<form action='{% url remove_item %}' method='POST' id='form'>
<input type='hidden' name='action' value='delete'>
<input type='hidden' name='id' value='{{ item.id }}'>
<input type="submit" value="Delete Item">
</form>
If you would like to keep your links while maintaining the POST, you'd have to resort to Javascript:
Delete Item
Unsightly, yes, but it's for the best. Your Django view would then do something like this:
def remove_item(request):
if request.method == 'POST':
## remove item
Furthermore, as Scott mentions, Django has some built in stuff to help you avoid the cross-site request forgeries I mentioned above, since it is still possible to do it even if you are doing a POST (just slightly harder). The way to avoid this is to have some kind of token tied to the form that needs to be validated server side before allowing the action to be taken. Check out the CsrfMiddleware class for more details on that. It will essentially automate some of that work out of it for you.
Additional Reading
URIs, Addressability, and the use of HTTP GET and POST
9.1.1 Safe Methods, HTTP 1.1, RFC 2616
Architecture of the World Wide Web, Volume One
Using POST with a regular link
Cross-Site Request Forgeries and You
Have the user submit a POST request to delete that model instance. These kinds of changes should never be possible via GET requests, so that people can't link each other to unwittingly performing changes on the site.
In your view, check that request.user is the same as the author of that particular model instance. You could also check that the HTTP_REFERRER is not set to another site if you were really worried.
Your security issue here is Cross Site Request Forgery. Django provides CsrfMiddleware which will actually add security to your forms to prevent this kind of attack. But it only works as long as you're not allowing permanent changes to take place via GET requests.

Categories